Diff for /loncom/homework/grades.pm between versions 1.80 and 1.273

version 1.80, 2003/03/28 20:49:55 version 1.273, 2005/06/04 19:05:31
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;
   
   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);
    }
       }
    }
       }
       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 @parts;      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) = @_;
       $getsec = $getsec eq '' ? 'all' : $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 eq 'all' || $getsec eq $section) {
             $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 211  sub jscriptNform { Line 448  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 221  sub jscriptNform { Line 459  sub jscriptNform {
 }  }
   
 #------------------ End of general use routines --------------------  #------------------ End of general use routines --------------------
   
   #
   # Find most similar essay
   #
   
   sub most_similar {
       my ($uname,$udom,$uessay)=@_;
   
   # ignore spaces and punctuation
   
       $uessay=~s/\W+/ /gs;
   
   # these will be returned. Do not care if not at least 50 percent similar
       my $limit=0.6;
       my $sname='';
       my $sdom='';
       my $scrsid='';
       my $sessay='';
   # go through all essays ...
       foreach my $tkey (keys %oldessays) {
    my ($tname,$tdom,$tcrsid)=split(/\./,$tkey);
   # ... except the same student
           if (($tname ne $uname) || ($tdom ne $udom)) {
       my $tessay=$oldessays{$tkey};
               $tessay=~s/\W+/ /gs;
   # String similarity gives up if not even limit
               my $tsimilar=&String::Similarity::similarity($uessay,$tessay,$limit);
   # Found one
               if ($tsimilar>$limit) {
    $limit=$tsimilar;
                   $sname=$tname;
                   $sdom=$tdom;
                   $scrsid=$tcrsid;
                   $sessay=$oldessays{$tkey};
               }
           } 
       }
       if ($limit>0.6) {
          return ($sname,$sdom,$scrsid,$sessay,$limit);
       } else {
          return ('','','','',0);
       }
   }
   
 #-------------------------------------------------------------------  #-------------------------------------------------------------------
   
 #------------------------------------ Receipt Verification Routines  #------------------------------------ Receipt Verification Routines
Line 229  sub jscriptNform { Line 511  sub jscriptNform {
 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');
       
       my $receiptparts=0;
       if ($env{"course.$courseid.receiptalg"} eq 'receipt2') { $receiptparts=1; }
       my $parts=['0'];
       if ($receiptparts) { ($parts)=&response_type($url,$symb); }
     foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {      foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$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 269  sub verifyreceipt { Line 560  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 284  sub listStudents { Line 578  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 '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>';
       }
    }
    $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 {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
  my ($uname,$udom) = split(/:/,$student);   my ($uname,$udom) = split(/:/,$student);
  my (%status) =&student_gradeStatus($url,$symb,$udom,$uname,$partlist);   my %status = ();
  my $statusflg = '';   if ($env{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {
  foreach (keys(%status)) {      (%status) =&student_gradeStatus($url,$symb,$udom,$uname,$partlist);
     $statusflg = 1 if ($status{$_} ne 'nothing');      my $submitted = 0;
     my ($foo,$partid,$foo1) = split(/\./,$_);      my $graded = 0;
     if ($status{'resource.'.$partid.'.submitted_by'} ne '') {      my $incorrect = 0;
  $statusflg = '';      foreach (keys(%status)) {
  $gradeTable.='<input type="hidden" name="'.   $submitted = 1 if ($status{$_} ne 'nothing');
     $student.':submitted_by" value="'.   $graded = 1 if ($status{$_} =~ /^ungraded/);
     $status{'resource.'.$partid.'.submitted_by'}.'" />';   $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="middle">&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 'all') {
    foreach (@$partlist) {
       $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) {
  $gradeTable='<br />&nbsp;<font color="red">'.   my $num_students=(scalar(keys(%$fullname)));
     'No submission found for this resource.</font><br />';   if ($num_students eq 0) {
       $gradeTable='<br />&nbsp;<font color="red">There are no students currently enrolled.</font>';
    } else {
       my $submissions='submissions';
       if ($submitonly eq 'incorrect') { $submissions = 'incorrect submissions'; }
       if ($submitonly eq 'graded'   ) { $submissions = 'ungraded submissions'; }
       $gradeTable='<br />&nbsp;<font color="red">'.
    '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 430  LISTJAVASCRIPT Line 785  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 460  sub sub_page_js { Line 860  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 484  sub sub_page_js { Line 884  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 496  sub sub_page_js { Line 896  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 515  sub sub_page_js { Line 914  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 526  sub sub_page_js { Line 924  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 544  sub sub_page_js { Line 942  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 581  sub sub_page_js { Line 980  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 589  sub sub_page_js { Line 995  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 629  SUBJAVASCRIPT Line 1034  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 666  sub sub_page_kw_js { Line 1074  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 686  sub sub_page_kw_js { Line 1093  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 709  sub sub_page_kw_js { Line 1118  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 717  sub sub_page_kw_js { Line 1141  sub sub_page_kw_js {
  height = 600;   height = 600;
  scrollbar = "yes";   scrollbar = "yes";
     }      }
 //    if (window.pWin) window.pWin.close();      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 738  sub sub_page_kw_js { Line 1167  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 751  sub sub_page_kw_js { Line 1182  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("  if (includemsg) {");      pDoc.write("  imgformname.src = \\"$iconpath/\\"+((includemsg) ? \\"mailto.gif\\" : \\"mailbkgrd.gif\\");");
     pDoc.write("     imgformname.src = \\"$iconpath/mailto.gif\\";");      pDoc.write("  var includemsg = opener.document.SCORE[\\"includemsg\\"+usrctr];");
     pDoc.write("  } else {");  
     pDoc.write("     imgformname.src = \\"$iconpath/mailbkgrd.gif\\";");  
     pDoc.write("  }");  
     pDoc.write("  var includemsg = eval(\\"opener.document.SCORE.includemsg\\"+usrctr);");  
     pDoc.write("  includemsg.value = msgchk;");      pDoc.write("  includemsg.value = msgchk;");
   
     pDoc.write("  self.close()");      pDoc.write("  self.close()");
Line 809  sub sub_page_kw_js { Line 1236  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 842  sub sub_page_kw_js { Line 1270  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 863  sub sub_page_kw_js { Line 1296  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 906  sub sub_page_kw_js { Line 1329  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 927  sub gradeBox { Line 1351  sub gradeBox {
   '' : $$record{'resource.'.$partid.'.awarded'}*$wgt);    '' : $$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><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."</nobr></td>\n";
  $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');   $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
  $ctr++;   $ctr++;
     }      }
Line 955  sub gradeBox { Line 1388  sub gradeBox {
  'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n";   'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n";
     if ($$record{'resource.'.$partid.'.solved'} eq 'excused') {      if ($$record{'resource.'.$partid.'.solved'} eq 'excused') {
  $result.='<option> </option>'.   $result.='<option> </option>'.
     '<option selected="on">excused</option></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";
     return $result;      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 1000  sub show_problem { Line 1457  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">'."\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
   #
     $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 />
 KEYWORDS  KEYWORDS
   #
   # Load the other essays for similarity check
   #
               my $essayurl=&Apache::lonnet::declutter($url);
       my ($adom,$aname,$apath)=($essayurl=~/^(\w+)\/(\w+)\/(.*)$/);
       $apath=&Apache::lonnet::escape($apath);
       $apath=~s/\W/\_/gs;
       %oldessays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname);
         }          }
     }      }
   
     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') {
  my @col_list;  
  ($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 1121  KEYWORDS Line 1634  KEYWORDS
     next if ($ncol <= 0);      next if ($ncol <= 0);
             s/\_/\./g;              s/\_/\./g;
             next if ($record{'resource.'.$_.'.collaborators'} eq '');              next if ($record{'resource.'.$_.'.collaborators'} eq '');
             my (@colList) = split(/,?\s+/,              my @goodcollaborators = ();
                                    $record{'resource.'.$_.'.collaborators'});              my @badcollaborators  = ();
             my @collaborators = ();      foreach (split(/,?\s+/,$record{'resource.'.$_.'.collaborators'})) { 
     foreach (@colList) { #pre-filter list - throw out submitter   $_ =~ s/[\$\^\(\)]//g;
    next if ($_ eq '');
  my ($co_name,$co_dom) = split /\@|:/,$_;   my ($co_name,$co_dom) = split /\@|:/,$_;
  $co_dom = $udom if (! defined($co_dom));   $co_dom = $udom if (! defined($co_dom) || $co_dom =~ /^domain$/i);
  next if ($co_name eq $uname && $co_dom eq $udom);   next if ($co_name eq $uname && $co_dom eq $udom);
  push @collaborators, $_;   # Doing this grep allows 'fuzzy' specification
    my @Matches = grep /^$co_name:$co_dom$/i,keys %$classlist;
    if (! scalar(@Matches)) {
       push @badcollaborators,$_;
    } else {
       push @goodcollaborators, @Matches;
    }
     }      }
             my (@badcollaborators);              if (scalar(@goodcollaborators) != 0) {
             if (scalar(@collaborators) != 0) {  
                 $result.='<b>Collaborators: </b>';                  $result.='<b>Collaborators: </b>';
                 foreach my $collaborator (@collaborators) {                  foreach (@goodcollaborators) {
                     my ($co_name,$co_dom) = split /\@|:/,$collaborator;      my ($lastname,$givenn) = split(/,/,$$fullname{$_});
                     $co_dom = $udom if (! defined($co_dom));      push @col_fullnames, $givenn.' '.$lastname;
                     # Doing this grep allows 'fuzzy' specification      $result.=$$fullname{$_}.'&nbsp; &nbsp; &nbsp;';
                     my @Matches = grep /^$co_name:$co_dom$/i,   }
                     keys %$classlist;  
     if (! scalar(@Matches)) {  
                         push @badcollaborators,':'.$collaborator.':';  
                         next;  
                     }  
                     push @col_list, @Matches;  
                     foreach (@Matches) {  
                         my ($lastname,$givenn) = split(/,/,$$fullname{$_});  
                         push @col_fullnames, $givenn.' '.$lastname;  
                         $result.=$$fullname{$_}.'&nbsp; &nbsp; &nbsp;';  
                     }  
                 }  
                 $result.='<br />'."\n";                  $result.='<br />'."\n";
                 if (scalar(@badcollaborators) > 0) {   my ($part)=split(/\./,$_);
                     $result.='<table border="0"><tr bgcolor="#ffbbbb"><td>';   $result.='<input type="hidden" name="collaborator'.$counter.
                     $result.='This student has submitted ';      '" value="'.$part.':'.(join ':',@goodcollaborators).'" />'.
                     if (scalar(@badcollaborators) == 1) {      "\n";
                         $result .= 'an invalid collaborator';      }
                     } else {      if (scalar(@badcollaborators) > 0) {
                         $result .= 'invalid collaborators';   $result.='<table border="0"><tr bgcolor="#ffbbbb"><td>';
                     }   $result.='This student has submitted ';
                     $result .= ': '.join(', ',@badcollaborators);   $result.=(scalar(@badcollaborators) == 1) ? 'an invalid collaborator' : 'invalid collaborators';
     $result .= '</td></tr></table>';                    $result .= ': '.join(', ',@badcollaborators);
                 }   $result .= '</td></tr></table>';
                 if (scalar(@collaborators > $ncol)) {      }         
                     $result .= '<table border="0"><tr bgcolor="#ffbbbb"><td>';      if (scalar(@badcollaborators > $ncol)) {
                     $result .= 'This student has submitted too many '.   $result .= '<table border="0"><tr bgcolor="#ffbbbb"><td>';
                         'collaborators.  Maximum is '.$ncol;   $result .= 'This student has submitted too many '.
                     $result .= '</td></tr></table>';      'collaborators.  Maximum is '.$ncol.'.';
                 }   $result .= '</td></tr></table>';
                 $result.='<input type="hidden" name="collaborator'.$counter.      }
                     '" value="'.(join ':',@col_list).'" />'."\n";  
             }  
  }   }
     }      }
     $request->print($result."\n");      $request->print($result."\n");
Line 1181  KEYWORDS Line 1686  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;
     $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part '.   }
  $partid.'</b> <font color="#999999">( ID '.$respid.   my $responsetype = $responseType->{$partid}->{$respid};
  ' )</font>&nbsp; &nbsp;'.   if (!exists($record{"resource.$partid.$respid.submission"})) {
                                 ($record{"resource.$partid.$respid.uploadedurl"}?      $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part:</b> '.
                                 '<a href="'.   $display_part.' <font color="#999999">( ID '.$respid.
                                 &Apache::lonnet::tokenwrapper($record{"resource.$partid.$respid.uploadedurl"}).   ' )</font>&nbsp; &nbsp;'.
    '"><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 />':'').   '<font color="red">Nothing submitted - no attempts</font><br /><br />';
                                 '<b>Answer: </b>'.      next;
  &keywords_highlight($subval).'</td></tr>'."\n"   }
  if ($ENV{'form.lastSub'} eq 'lastonly' ||    foreach (@$string) {
     ($ENV{'form.lastSub'} eq 'hdgrade' &&       my ($partid,$respid) = /^resource\.([^\.]*)\.([^\.]*)\.submission/;
      $$handgrade{$part} =~ /:yes$/));      if ($part ne ($partid.'_'.$respid)) { next; }
       my ($ressub,$subval) = split(/:/,$_,2);
       # Similarity check
       my $similar='';
       if($env{'form.checkPlag'}){
    my ($oname,$odom,$ocrsid,$oessay,$osim)=
       &most_similar($uname,$udom,$subval);
    if ($osim) {
       $osim=int($osim*100.0);
       $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;
    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);
       
    &Apache::lonnet::logthis("found a portfolio file".$record{"resource.$partid.$respid.portfiles"});
    &Apache::lonnet::logthis("uploaded URL file".$record{"resource.$partid.$respid.uploadedurl"});
       }
    }
    if ($record{"resource.$partid.$respid.uploadedurl"}) {
       push(@files,$record{"resource.$partid.$respid.uploadedurl"});
    }
    if (@files) {
       $lastsubonly.='<br /><font color="red" size="1">Like all files provided by users, this file may contain virusses</font><br />';
       foreach my $file (@files) {
    &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";
     $result.='<tr><td bgcolor="#ffffff">'."\n".   $result.='&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.
  '&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.      ',\''.$msgfor.'\')"; TARGET=_self>'.
  ',\''.$msgfor.'\')"; TARGET=_self>'.      'Compose Message to student'.(scalar(@col_fullnames) >= 1 ? 's' : '').'</a> &nbsp;'.
  '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;(Message will be sent when you click on Save & Next below.)'."\n" 
  '<br />&nbsp;(Message will be sent when you click on Save & Next below.)'."\n"       if ($env{'form.handgrade'} eq 'yes');
  if ($ENV{'form.handgrade'} eq 'yes');   $request->print($result);
     $request->print($result);      }
   
     my %seen = ();      my %seen = ();
     my @partlist;      my @partlist;
       my @gradePartRespid;
     for (sort keys(%$handgrade)) {      for (sort keys(%$handgrade)) {
  my ($partid,$respid) = split(/_/);   my ($partid,$respid) = split(/_/);
  next if ($seen{$partid} > 0);   next if ($seen{$partid} > 0);
  $seen{$partid}++;   $seen{$partid}++;
  next if ($$handgrade{$_} =~ /:no$/);   next if ($$handgrade{$_} =~ /: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 1283  KEYWORDS Line 1853  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 1314  KEYWORDS Line 1878  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 1334  sub get_last_submission { Line 1898  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'};
   
     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/);
  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;   $message.="\n\nPoint".($pts > 1 ? 's':'').' awarded = '.$pts.' out of '.$wgt;
  $message.=" for <a href=\"".   $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);         $env{'form.msgsub'},$message);
     }      }
     if ($ENV{'form.collaborator'.$ctr}) {      if ($env{'form.collaborator'.$ctr}) {
  my (@collaborators) = split(/:/,$ENV{'form.collaborator'.$ctr});   my @collabstrs=&Apache::loncommon::get_env_multiple("form.collaborator$ctr");
  foreach (@collaborators) {   foreach my $collabstr (@collabstrs) {
     &saveHandGrade($request,$url,$symb,$_,$udom,$ctr,      my ($part,@collaborators) = split(/:/,$collabstr);
    $ENV{'form.unamedom'.$ctr});      foreach (@collaborators) {
     if ($message ne '') {   my ($errorflag,$pts,$wgt) = 
  $msgstatus = &Apache::lonmsg::user_normal_msg ($_,$udom,      &saveHandGrade($request,$url,$symb,$_,$udom,$ctr,
        $ENV{'form.msgsub'},     $env{'form.unamedom'.$ctr},$part);
        $message);   if ($errorflag eq 'not_allowed') {
       $request->print("<font color=\"red\">Not allowed to modify grades for $_:$udom</font>");
       next;
    } else {
       if ($message ne '') {
    $msgstatus = &Apache::lonmsg::user_normal_msg($_,$udom,$env{'form.msgsub'},$message);
       }
       
    }
     }      }
  }   }
     }      }
Line 1406  sub processHandGrade { Line 1977  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,
        $env{'course.'.$env{'request.course.id'}.'.domain'},
        $env{'course.'.$env{'request.course.id'}.'.num'});
     }      }
     $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
     if ($ENV{'form.refresh'} eq 'on') {      my (undef,undef,$fullname) = &getclasslist($env{'form.section'},'1');
  my $ctr = 0;      if ($env{'form.refresh'} eq 'on') {
  $ENV{'form.NTSTU'}=$ngrade;   my ($ctr,$total) = (0,0);
  while ($ctr < $ngrade) {   while ($ctr < $ngrade) {
     ($ENV{'form.student'},$ENV{'form.userdom'}) = split(/:/,$ENV{'form.unamedom'.$ctr});      $total++ if  $env{'form.unamedom'.$ctr} ne '';
     &submission($request,$ctr,$ngrade-1);  
     $ctr++;      $ctr++;
  }   }
    $env{'form.NTSTU'}=$ngrade;
    $ctr = 0;
    while ($ctr < $total) {
       my $processUser = $env{'form.unamedom'.$ctr};
       ($env{'form.student'},$env{'form.userdom'}) = split(/:/,$processUser);
       $env{'form.fullname'} = $$fullname{$processUser};
       &submission($request,$ctr,$total-1);
       $ctr++;
    }
    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 '';   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 (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'0');  
     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 {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
Line 1482  sub processHandGrade { Line 2071  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') {   if ($submitonly =~ /^(yes|graded|incorrect)$/) {
     my (%status) = &student_gradeStatus($ENV{'form.url'},$symb,$udom,$uname,$partlist) ;  #    my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);
     my $statusflg = '';      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 1505  sub processHandGrade { Line 2107  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 1523  sub processHandGrade { Line 2125  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') {      foreach my $new_part (split(/:/,$env{'form.partlist'.$newflg})) {
  $newrecord{'resource.'.$_.'.solved'} = 'excused';   #collaborator may vary for different parts
  if (exists($record{'resource.'.$_.'.awarded'})) {   if ($submitter && $new_part ne $part) { next; }
     $newrecord{'resource.'.$_.'.awarded'} = '';   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');      }
       if ($record{'resource.'.$new_part.'.awarded'} ne $partial) {
    $newrecord{'resource.'.$new_part.'.awarded'}  = $partial;
     }      }
     $newrecord{'resource.'.$_.'.submitted_by'} = $submitter       my $reckey = 'resource.'.$new_part.'.solved';
  if ($submitter && ($record{'resource.'.$_.'.submitted_by'} ne $submitter));      if ($partial == 0) {
     $newrecord{'resource.'.$_.'regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";   if ($record{$reckey} ne 'incorrect_by_override') {
       $newrecord{$reckey} = 'incorrect_by_override';
    }
       } else {
    if ($record{$reckey} ne 'correct_by_override') {
       $newrecord{$reckey} = 'correct_by_override';
    }
       }    
       if ($submitter && 
    ($record{'resource.'.$new_part.'.submitted_by'} ne $submitter)) {
    $newrecord{'resource.'.$new_part.'.submitted_by'} = $submitter;
       }
       $newrecord{'resource.'.$new_part.'.regrader'}=
    "$env{'user.name'}:$env{'user.domain'}";
    }
    # 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') {
       push (@v_flag,$new_part);
  }   }
     }      }
   
     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);
       }
       if ($aggregateflag) {
           &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
                     $env{'course.'.$env{'request.course.id'}.'.domain'},
                     $env{'course.'.$env{'request.course.id'}.'.num'});
     }      }
     return '',$pts,$wgt;      return '',$pts,$wgt;
 }  }
   
   # ----------- 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, $stuname, $v_flag) = @_;
       my $version_parts = join('|',@$v_flag);
       my $parts = join('|', @$parts_graded);
       my $portfolio_root = &Apache::loncommon::propath($domain,
    $stuname).
    '/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});
               # &Apache::lonnet::logthis("should be unmarking and remarking");
               foreach my $file (@portfiles) {
                   &Apache::lonnet::unmark_as_readonly($domain,$stuname,[$symb,$env{'request.course.id'}],$file);
                   my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*$)/);
                   my $version = 0;
                   my @answer_file_parts = split(/\./, $answer_file);
                   my @dir_list = &Apache::lonnet::dirlist($directory,$domain,$stuname,$portfolio_root);
                   my @file_names;
                   my @file_name_parts;
                   foreach my $row (@dir_list) {
                       @file_names = split(/\&/,$row,2);
                       @file_name_parts = split(/\./, $file_names[0]);
                       # ($file_name_parts[scalar @file_name_parts] eq $answer_file_parts[scalar @answer_file_parts])
                       if (($file_name_parts[0] eq $answer_file_parts[0]) && 
                           ($file_name_parts[-1] eq $answer_file_parts[-1])) {
                           # gets here if filename and extension match, regardless of version
                           if (scalar @file_name_parts == 3) { # a versioned file is found
                               # so save it for later
                               if ($file_name_parts[1] > $version) {$version = $file_name_parts[1]};
                           }
                       }
                   }
                   $version++;
                   my $home_server = &Apache::lonnet::homeserver($stuname,$domain,undef);
                   $env{'form.copy'} = &Apache::lonnet::getfile("/uploaded/$domain/$stuname/portfolio$directory$answer_file");
                   if($env{'form.copy'} eq '-1') {
                       &Apache::lonnet::logthis('problem getting file '.$directory.$answer_file);
                   } else {
                      my $copy_result = &Apache::lonnet::finishuserfileupload($stuname,$domain,$home_server,'copy',
                                       '/portfolio'.$directory.$answer_file_parts[0].'.'.$version.'.'.$answer_file_parts[-1]);
                       push(@v_portfiles, $answer_file_parts[0].'.'.$version.'.'.$answer_file_parts[-1]);
                       &Apache::lonnet::mark_as_readonly($domain,$stuname,
                                   ['/portfolio'.$directory.$answer_file_parts[0].'.'.$version.'.'.$answer_file_parts[-1]],
                                   [$symb,$env{'request.course.id'},'graded']);
                   }
               }
               $$record{$key} = join(',',@v_portfiles);
           }
       } 
       return 'ok';   
       
   }
   
 #--------------------------------------------------------------------------------------  #--------------------------------------------------------------------------------------
 #  #
 #-------------------------- Next few routines handles grading by section or whole class  #-------------------------- Next few routines handles grading by section or whole class
Line 1577  sub viewgrades_js { Line 2352  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 1595  sub viewgrades_js { Line 2370  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 1605  sub viewgrades_js { Line 2380  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+      var scorename = document.classgrade["GD_"+user+"_"+partid+"_awarded"];
  "_"+partid+"_awarded");      var saveval   = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value;
     var saveval   = eval("document.classgrade.GD_"+user+      var selname   = document.classgrade["GD_"+user+"_"+partid+"_solved"];
  "_"+partid+"_solved_s.value");  
     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 1627  sub viewgrades_js { Line 2400  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 1643  sub viewgrades_js { Line 2416  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+   var scorename = document.classgrade["GD_"+user+"_"+partid+"_awarded"];
      "_"+partid+"_awarded");   var saveval   = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value;
  var saveval   = eval("document.classgrade.GD_"+user+   var selname   = document.classgrade["GD_"+user+"_"+partid+"_solved"];
      "_"+partid+"_solved_s.value");   if ((saveval != "correct") || override) {
  var selname   = eval("document.classgrade.GD_"+user+  
      "_"+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+   var scorename = document.classgrade["GD_"+user+"_"+partid+"_awarded"];
      "_"+partid+"_awarded");   var saveval   = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value;
  var saveval   = eval("document.classgrade.GD_"+user+   var selname   = document.classgrade["GD_"+user+"_"+partid+"_solved"];
      "_"+partid+"_solved_s.value");   if ((saveval != "correct") || override) {
  var selname   = eval("document.classgrade.GD_"+user+      scorename.value = document.classgrade["GD_"+user+"_"+partid+"_awarded_s"].value;
      "_"+partid+"_solved");  
  if (saveval != "correct") {  
     scorename.value = eval("document.classgrade.GD_"+user+  
      "_"+partid+"_awarded_s.value");;  
     selname[0].selected = true;      selname[0].selected = true;
  }   }
     }      }
Line 1674  sub viewgrades_js { Line 2446  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 1696  sub viewgrades_js { Line 2468  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 1717  sub viewgrades_js { Line 2491  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+   var resetscore = document.classgrade["GD_"+user+"_"+partid+"_awarded"];
       "_"+partid+"_awarded");   resetscore.value = document.classgrade["GD_"+user+"_"+partid+"_awarded_s"].value;
  resetscore.value = eval("document.classgrade.GD_"+user+   var resettries = document.classgrade["GD_"+user+"_"+partid+"_tries"];
  "_"+partid+"_awarded_s.value");   resettries.value = document.classgrade["GD_"+user+"_"+partid+"_tries_s"].value;
    var saveselval   = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value;
  var saveselval   = eval("document.classgrade.GD_"+user+   var selname   = document.classgrade["GD_"+user+"_"+partid+"_solved"];
      "_"+partid+"_solved_s.value");  
   
  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 1745  sub viewgrades { Line 2516  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 1758  sub viewgrades { Line 2535  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 1783  sub viewgrades { Line 2562  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 1791  sub viewgrades { Line 2570  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
Line 1810  sub viewgrades { Line 2590  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 {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
  my ($uname,$udom) = split(/:/);  
  $result.='<input type="hidden" name="ctr'.$ctr.'" value="'.$uname.'" />'."\n";  
  $result.=&viewstudentgrade($url,$symb,$ENV{'request.course.id'},  
    $_,$$fullname{$_},\@parts,\%weight);  
  $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) {
    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.=&show_grading_menu_form($symb,$url);      $result.=&show_grading_menu_form($symb,$url);
     return $result;      return $result;
 }  }
   
 #--- call by previous routine to display each student  #--- call by previous routine to display each student
 sub viewstudentgrade {  sub viewstudentgrade {
     my ($url,$symb,$courseid,$student,$fullname,$parts,$weight) = @_;      my ($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";
     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="middle">';
           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 '' ? '' : $score*$$weight{$part};
     $result.='<input type="hidden" name="'.      $result.='<input type="hidden" name="'.
  'GD_'.$uname.'_'.$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_'.$uname.'_'.$part.'_awarded" '.   'GD_'.$student.'_'.$part.'_awarded" '.
  'onChange="javascript:changeSelect(\''.$part.'\',\''.$uname.   'onChange="javascript:changeSelect(\''.$part.'\',\''.$student.
  '\')" value="'.$pts.'" size="4" /></td>'."\n";   '\')" value="'.$pts.'" size="4" /></td>'."\n";
  } elsif ($type eq 'solved') {   } elsif ($type eq 'solved') {
     my ($status,$foo)=split(/_/,$score,2);      my ($status,$foo)=split(/_/,$score,2);
     $status = 'nothing' if ($status eq '');      $status = 'nothing' if ($status eq '');
     $result.='<input type="hidden" name="'.'GD_'.$uname.'_'.      $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_'.$uname.'_'.$part.'_solved" '.   'GD_'.$student.'_'.$part.'_solved" '.
  'onChange="javascript:changeOneScore(\''.$part.'\',\''.$uname.'\')" >'."\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_'.$uname.'_'.$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_'.$uname.'_'.$part.'_'.$type.'" '.   'GD_'.$student.'_'.$part.'_'.$type.'" '.
  'value="'.$score.'" size="4" /></td>'."\n";   'value="'.$score.'" size="4" /></td>'."\n";
  }   }
     }      }
Line 1918  sub viewstudentgrade { Line 2722  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>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 1934  sub editgrades { Line 2740  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 1959  sub editgrades { Line 2765  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;
     for ($i=0; $i<$ENV{'form.total'}; $i++) {      my ($updateCtr,$noupdateCtr) = (1,1);
  my $user = $ENV{'form.ctr'.$i};      for ($i=0; $i<$env{'form.total'}; $i++) {
    my $line;
    my $user = $env{'form.ctr'.$i};
    my $usercolon = $user;
    $usercolon =~s/_/:/;
    my ($uname,$udom)=split(/_/,$user);
  my %newrecord;   my %newrecord;
  my $updateflag = 0;   my $updateflag = 0;
  my @userdom = grep /^$user:/,keys %$classlist;   $line .= '<td>'.&nameUserString(undef,$$fullname{$usercolon},$uname,$udom).'</td>';
  my (undef,$udom) = split(/:/,$userdom[0]);   my $usec=$classlist->{"$uname:$udom"}[5];
    if (!&canmodify($usec)) {
  $result .= '<tr bgcolor="#ffffde"><td>'.$user.'&nbsp;</td><td>'.      my $numcols=scalar(@partid)*4+2;
     $$fullname{$userdom[0]}.'&nbsp;</td>';      $noupdate.=$line."<td colspan=\"$numcols\"><font color=\"red\">Not allowed to modify student</font></td></tr>";
       next;
    }
           my %aggregate = ();
           my $aggregateflag = 0;
  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'));
     $result .= '<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)) {      if ($dropMenu eq 'reset status' &&
    $old_score ne '') { # ignore if no previous attempts => nothing to reset
    $newrecord{'resource.'.$_.'.tries'} = 0;
    $newrecord{'resource.'.$_.'.solved'} = '';
    $newrecord{'resource.'.$_.'.award'} = '';
    $newrecord{'resource.'.$_.'.awarded'} = 0;
    $newrecord{'resource.'.$_.'.regrader'}="$env{'user.name'}:$env{'user.domain'}";
    $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;
  }   }
  $result .= '<td align="center">'.$old_aw.'&nbsp;</td>'.   $line .= '<td align="center">'.$old_aw.'&nbsp;</td>'.
     '<td align="center">'.$awarded.'&nbsp;</td>';      '<td align="center">'.$awarded.'&nbsp;</td>';
     }      }
  }   }
  $result .= '</tr>'."\n";   $line.='</tr>'."\n";
  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,$user);      $udom,$uname);
       $result.='<tr bgcolor="#ffffde"><td align="right">&nbsp;'.$updateCtr.'&nbsp;</td>'.$line;
       $updateCtr++;
    } else {
       $noupdate.='<tr bgcolor="#ffffde"><td align="right">&nbsp;'.$noupdateCtr.'&nbsp;</td>'.$line;
       $noupdateCtr++;
  }   }
           if ($aggregateflag) {
               &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
                         $env{'course.'.$env{'request.course.id'}.'.domain'},
                         $env{'course.'.$env{'request.course.id'}.'.num'});
           }
       }
       if ($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 2063  sub split_part_type { Line 2911  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 2101  ENDPICK Line 2951  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 2138  ENDPICK Line 2990  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 2167  Total number of records found in file: $ Line 3009  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 2214  sub csvuploadmap_footer { Line 3062  sub csvuploadmap_footer {
 ENDPICK  ENDPICK
 }  }
   
   sub upcsvScores_form {
       my ($request) = shift;
       my ($symb,$url)=&get_symb_and_url($request);
       if (!$symb) {return '';}
       my $result =<<CSVFORMJS;
   <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>
   CSVFORMJS
       $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.='<table width=100% border=0><tr bgcolor="#e6ffff"><td>'."\n";
       $result.='&nbsp;<b>Specify a file containing the class scores for current resource'.
    '.</b></td></tr>'."\n";
       $result.='<tr bgcolor=#ffffe6><td>'."\n";
       my $upfile_select=&Apache::loncommon::upfile_select_html();
       my $ignore=&mt('Ignore First Line');
       $result.=<<ENDUPFORM;
   <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
   <input type="hidden" name="symb" value="$symb" />
   <input type="hidden" name="url" value="$url" />
   <input type="hidden" name="command" value="csvuploadmap" />
   <input type="hidden" name="probTitle" value="$env{'form.probTitle'}" />
   <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />
   $upfile_select
   <br /><input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scores" />
   <label><input type="checkbox" name="noFirstLine" />$ignore</lable>
   </form>
   ENDUPFORM
       $result.='</td></tr></table>'."\n";
       $result.='</td></tr></table><br /><br />'."\n";
       $result.=&show_grading_menu_form($symb,$url);
       return $result;
   }
   
   
 sub csvuploadmap {  sub csvuploadmap {
     my ($request)= @_;      my ($request)= @_;
     my ($symb,$url)=&get_symb_and_url($request);      my ($symb,$url)=&get_symb_and_url($request);
     if (!$symb) {return '';}      if (!$symb) {return '';}
   
     my $datatoken;      my $datatoken;
     if (!$ENV{'form.datatoken'}) {      if (!$env{'form.datatoken'}) {
  $datatoken=&Apache::loncommon::upfile_store($request);   $datatoken=&Apache::loncommon::upfile_store($request);
     } else {      } else {
  $datatoken=$ENV{'form.datatoken'};   $datatoken=$env{'form.datatoken'};
  &Apache::loncommon::load_tmp_file($request);   &Apache::loncommon::load_tmp_file($request);
     }      }
     my @records=&Apache::loncommon::upfile_record_sep();      my @records=&Apache::loncommon::upfile_record_sep();
       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 2252  sub csvuploadmap { Line 3145  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') {  <input type="submit" value="Assign Grades" /><br />
     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("<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','1');      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;      next;
  }   }
    my $usec=$classlist->{"$username:$domain"}[5];
    if (!&canmodify($usec)) {
       push(@notallowed,"$username:$domain");
       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);
    $entries{$fields{$dest}}=~s/\s//g;
    my $pcr=$entries{$fields{$dest}} / $wgt;
    my $award='correct_by_override';
    $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}};
       }
  }   }
  $grades{"resource.regrader"}="$ENV{'user.name'}:$ENV{'user.domain'}";   if (! %grades) { push(@skipped,"$username:$domain no data to store"); }
  &Apache::lonnet::cstore(\%grades,$symb,$ENV{'request.course.id'},   $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";
   # &Apache::lonnet::logthis(" storing ".(join('-',%grades)));
    &Apache::lonnet::cstore(\%grades,$symb,$env{'request.course.id'},
  $domain,$username);   $domain,$username);
  $request->print('.');   $request->print('.');
  $request->rflush();   $request->rflush();
Line 2302  sub csvuploadassign { Line 3286  sub csvuploadassign {
     }      }
     $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(&view_edit_entire_class_form($symb,$url));      $request->print("<br />\n");
     $request->print(&show_grading_menu_form($symb,$url));      $request->print(&show_grading_menu_form($symb,$url));
     return '';      return '';
 }  }
Line 2313  sub csvuploadassign { Line 3301  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 2327  function checkPickOne(formname) { Line 3315  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 2373  LISTJAVASCRIPT Line 3335  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 2393  LISTJAVASCRIPT Line 3357  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><input type="radio" name="vProb" value="no" checked="on" /> no '."\n".
  '<input type="radio" name="vProb" value="yes" /> yes '."<br>\n";   '<input type="radio" name="vProb" value="yes" /> yes '."<br>\n";
   
     $result.='&nbsp;<b>Submission Details: </b>'.      $result.='&nbsp;<b>Submission Details: </b>'.
  '<input type="radio" name="lastSub" value="none" /> none'."\n".   '<input type="radio" name="lastSub" value="none" /> none'."\n".
  '<input type="radio" name="lastSub" value="datesub" checked /> dates and submissions'."\n".   '<input type="radio" name="lastSub" value="datesub" checked /> by dates and submissions'."\n".
  '<input type="radio" name="lastSub" value="all" /> all details'."\n";   '<input type="radio" name="lastSub" value="all" /> all details'."\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 {lc($$fullname{$a}) cmp lc($$fullname{$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;<input type="radio" name="student" value="'.$student.'" /> '
  $studentTable.=($ptr%4 == 0 ? '</td></tr>' : '');      .&nameUserString(undef,$$fullname{$student},$uname,$udom)."\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 2445  LISTJAVASCRIPT Line 3409  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 && $ctr < 100) {  
  if($mapcurRes == $mapiterator->BEGIN_MAP) { $mapdepth++; }  
  if($mapcurRes == $mapiterator->END_MAP) { $mapdepth++; }  
   
  if (ref($mapcurRes) && $mapcurRes->is_problem() && !$mapcurRes->randomout) {  
     $countProblems++;  
  }  
  $ctr++;  
     }  
     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 2506  sub displayPage { Line 3435  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
   
     my $iterator = $navmap->getIterator($map->map_start(),      my $iterator = $navmap->getIterator($map->map_start(),
Line 2530  sub displayPage { Line 3470  sub displayPage {
   
     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();
     $parts = &temp_parts_fix($parts); # remove line when lonnavmap is fixed  
             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++;  
     }      }
   
     $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 2649  sub displayPage { Line 3568  sub displayPage {
     return '';      return '';
 }  }
   
 sub temp_parts_fix { #remove sub once lonnavmap is fixed  sub displaySubByDates {
     my $parts = shift;      my ($symb,$record,$parts,$responseType,$checkIcon,$uname,$udom) = @_;
     my %seen = ();      my $isCODE=0;
     my @correctParts = ();      if (exists($record->{'resource.CODE'})) { $isCODE=1; }
     foreach (@{$parts}) {      my $studentTable='<table border="0" width="100%"><tr><td bgcolor="#777777">'.
  next if ($seen{$_} > 0);   '<table border="0" width="100%"><tr bgcolor="#e6ffff">'.
  $seen{$_}++;   '<td><b>Date/Time</b></td>'.
  push @correctParts,$_;   ($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>';
       
     }      }
     return \@correctParts;      $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
   
     my $iterator = $navmap->getIterator($map->map_start(),      my $iterator = $navmap->getIterator($map->map_start(),
Line 2687  sub updateGradeByPage { Line 3676  sub updateGradeByPage {
   
     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();
     $parts = &temp_parts_fix($parts); # remove line when lonnavmap is fixed  
             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++;  
     }      }
   
     $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 2779  sub updateGradeByPage { Line 3792  sub updateGradeByPage {
 #  #
 #------ start of section for handling grading by page/sequence ---------  #------ start of section for handling grading by page/sequence ---------
   
   sub defaultFormData {
       my ($symb,$url)=@_;
       return '
         <input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
        '<input type="hidden" name="url"     value="'.$url.'" />'."\n".
        '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".
        '<input type="hidden" name="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 2796  sub getSequenceDropDown { Line 3818  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 {
       my ($file2grade) = @_;
       my $result= '<select name="scantron_selectfile">';
       $result.="<option></option>";
       foreach my $filename (sort(&scantron_filenames())) {
    $result.="<option".($filename eq $file2grade ? ' selected="on"':'').">$filename</option>\n";
       }
       $result.="</select>";
       return $result;
   }
   
   sub scantron_scantab {
       my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
       my $result='<select name="scantron_format">'."\n";
       $result.='<option></option>'."\n";
       foreach my $line (<$fh>) {
    my ($name,$descrip)=split(/:/,$line);
    if ($name =~ /^\#/) { next; }
    $result.='<option value="'.$name.'">'.$descrip.'</option>'."\n";
       }
       $result.='</select>'."\n";
   
       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; }
    $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="on" /> 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 $grading_menu_button=&show_grading_menu_form($symb,$url);
       my $file_selector=&scantron_uploads($file2grade);
       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="scantronupload">      <table width="100%" border="0">
   <input type="hidden" name="symb" value="$symb" />  
   <input type="hidden" name="url" value="$url" />  
   <input type="hidden" name="command" value="scantron_configphase" />  
   <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>
                 &nbsp;<b>Specify a Scantron data file to upload.</b>
               </td>
             </tr>
             <tr bgcolor="#ffffe6">
             <td>              <td>
  <!-- FIXME I need to present a list of files from a specfic directory that has been configured, or any existing delay queues -->  SCANTRONFORM
  Filename of scoring office file:      my $default_form_data=&defaultFormData(&get_symb_and_url($r,1));
  <select name="selectfile">      my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
     <option value="filname1">filename1</option>      my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};
     <option value="filname2">filename2</option>      $r->print(<<UPLOAD);
  </select>                <script type="text/javascript" language="javascript">
     </td>      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="Show List of Files" />
                 </td>
               </tr>
             </table>
           </td>
         </form>
       </tr>
   SCANTRONFORM
   
       $r->print(<<SCANTRONFORM);
   </table>    </table>
   <input type="submit" value="Submit" />  $grading_menu_button
 </form>  
 SCANTRONFORM  SCANTRONFORM
   
       return
   }
   
   sub get_scantron_config {
       my ($which) = @_;
       my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
       my %config;
       #FIXME probably should move to XML it has already gotten a bit much now
       foreach my $line (<$fh>) {
    my ($name,$descrip)=split(/:/,$line);
    if ($name ne $which ) { next; }
    chomp($line);
    my @config=split(/:/,$line);
    $config{'name'}=$config[0];
    $config{'description'}=$config[1];
    $config{'CODElocation'}=$config[2];
    $config{'CODEstart'}=$config[3];
    $config{'CODElength'}=$config[4];
    $config{'IDstart'}=$config[5];
    $config{'IDlength'}=$config[6];
    $config{'Qstart'}=$config[7];
    $config{'Qlength'}=$config[8];
    $config{'Qoff'}=$config[9];
    $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;
       }
       return %config;
   }
   
   sub username_to_idmap {
       my ($classlist)= @_;
       my %idmap;
       foreach my $student (keys(%$classlist)) {
    $idmap{$classlist->{$student}->[&Apache::loncoursedata::CL_ID]}=
       $student;
       }
       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 {
       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 {
       my ($line,$whichline,$scantron_config,$scan_data,$justHeader)=@_;
       my %record;
       my $questions=substr($line,$$scantron_config{'Qstart'}-1);
       my $data=substr($line,0,$$scantron_config{'Qstart'}-1);
       if ($$scantron_config{'CODElocation'} ne 0) {
    if ($$scantron_config{'CODElocation'} < 0) {
       $record{'scantron.CODE'}=substr($data,
       $$scantron_config{'CODEstart'}-1,
       $$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 {
       #FIXME interpret first N questions
    }
       }
       $record{'scantron.ID'}=substr($data,$$scantron_config{'IDstart'}-1,
     $$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 $questnum=0;
       while ($questions) {
    $questnum++;
    my $currentquest=substr($questions,0,$$scantron_config{'Qlength'});
    substr($questions,0,$$scantron_config{'Qlength'})='';
    if (length($currentquest) < $$scantron_config{'Qlength'}) { next; }
    if ($$scantron_config{'Qon'} eq 'letter') {
       if (!$currentquest || $currentquest eq $$scantron_config{'Qoff'} ||
    $currentquest !~ /^[A-Z]$/) {
    $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 || $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 {
       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;
       return \%record;
   }
   
   sub scantron_add_delay {
       my ($delayqueue,$scanline,$errormessage,$errorcode)=@_;
       push(@$delayqueue,
    {'line' => $scanline, 'emsg' => $errormessage,
     'ecode' => $errorcode }
    );
   }
   
   sub scantron_find_student {
       my ($scantron_record,$scan_data,$idmap,$line)=@_;
       my $scanID=$$scantron_record{'scantron.ID'};
       if ($scanID =~ /^\s*$/) {
     return &scan_data($scan_data,"$line.user");
       }
       foreach my $id (keys(%$idmap)) {
     if (lc($id) eq lc($scanID)) {
        return $$idmap{$id};
     }
       }
       return undef;
   }
   
   sub scantron_filter {
       my ($curres)=@_;
                           # randomout is dysfunctional at best for this purpose
       if (ref($curres) && $curres->is_problem()) { #&& !$curres->randomout) {
    return 1;
       }
       return 0;
   }
   
   sub scantron_process_corrections {
       my ($r) = @_;
       my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
       my ($scanlines,$scan_data)=&scantron_getfile();
       my $classlist=&Apache::loncoursedata::get_classlist();
       my $which=$env{'form.scantron_line'};
       my $line=&scantron_get_line($scanlines,$scan_data,$which);
       my ($skip,$err,$errmsg);
       if ($env{'form.scantron_skip_record'}) {
    $skip=1;
       } elsif ($env{'form.scantron_corrections'} =~ /^(duplicate|incorrect)ID$/) {
    my $newstudent=$env{'form.scantron_username'}.':'.
       $env{'form.scantron_domain'};
    my $newid=$classlist->{$newstudent}->[&Apache::loncoursedata::CL_ID];
    ($line,$err,$errmsg)=
       &scantron_fixup_scanline(\%scantron_config,$scan_data,$line,$which,
        'ID',{'newid'=>$newid,
       'username'=>$env{'form.scantron_username'},
       'domain'=>$env{'form.scantron_domain'}});
       } elsif ($env{'form.scantron_corrections'} =~ /^(duplicate|incorrect)CODE$/) {
    my $resolution=$env{'form.scantron_CODE_resolution'};
    my $newCODE;
    my %args;
    if      ($resolution eq 'use_unfound') {
       $newCODE='use_unfound';
    } elsif ($resolution eq 'use_found') {
       $newCODE=$env{'form.scantron_CODE_selectedvalue'};
    } elsif ($resolution eq 'use_typed') {
       $newCODE=$env{'form.scantron_CODE_newvalue'};
    } elsif ($resolution =~ /^use_closest_(\d+)/) {
       $newCODE=$env{"form.scantron_CODE_closest_$1"};
    }
    if ($env{'form.scantron_corrections'} eq 'duplicateCODE') {
       $args{'CODE_ignore_dup'}=1;
    }
    $args{'CODE'}=$newCODE;
    ($line,$err,$errmsg)=
       &scantron_fixup_scanline(\%scantron_config,$scan_data,$line,$which,
        'CODE',\%args);
       } elsif ($env{'form.scantron_corrections'} =~ /^(missing|double)bubble$/) {
    foreach my $question (split(',',$env{'form.scantron_questions'})) {
       ($line,$err,$errmsg)=
    &scantron_fixup_scanline(\%scantron_config,$scan_data,$line,
    $which,'answer',
    { 'question'=>$question,
          'response'=>$env{"form.scantron_correct_Q_$question"}});
       if ($err) { last; }
    }
       }
       if ($err) {
    $r->print("Unable to accept last correction, an error occurred :$errmsg:");
       } 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'});
       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>
   </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;      return $result;
 }  }
   
 sub scantron_configphase {  sub scantron_validate_file {
     my ($r) = @_;      my ($r) = @_;
     my $sequence=$ENV{'form.selectpage'};      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;      my $result;
     $result.="got page $sequence";      if (@todelete) {
     $Apache::lonxml::debug=1;   $result=&Apache::lonnet::del('nohist_scantrondata',\@todelete,$cdom,$cname);
     &Apache::lonhomework::showhash(%ENV);      }
     $Apache::lonxml::debug=0;  
     #FIXME Needs to present some lines from the file and allow the instructor to specify which columns represent what data, possibly have some nice defaults setup, probably should do a pass through all problems for a student to get an idea of how many questions there are, and homw many lines we'll have,  
     return $result;      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'};
       my $docuhome=$env{'course.'.$env{'request.course.id'}.'.home'};
       $env{'form.sillywaytopassafilearound'}=$contents;
       &Apache::lonnet::finishuserfileupload($docuname,$docudom,$docuhome,'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'});
       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 @alphabet=('A'..'Z');
       $r->print("<table border='1'><tr><td rowspan='2'>$quest</td>");
       for (my $i=0;$i<$max+1;$i++) {
    $r->print('<td align="center">');
    if ($selected[0] eq $alphabet[$i]) { $r->print('X'); shift(@selected) }
    else { $r->print('&nbsp;'); }
    $r->print('</td>');
       }
       $r->print('<td></td></tr><tr>');
       for (my $i=0;$i<$max;$i++) {
    $r->print('<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=$env{'form.scantron_CODElist'};
       my $cdom =$env{'course.'.$env{'request.course.id'}.'.domain'};
       my $cnum =$env{'course.'.$env{'request.course.id'}.'.num'};
       my %result=&Apache::lonnet::get('CODEs',[$old_name],$cdom,$cnum);
       my %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 (undef,undef,$sequence)=&Apache::lonnet::decode_symb($env{'form.selectpage'});
       my ($symb,$url)=&get_symb_and_url($r);
       if (!$symb) {return '';}
       my $default_form_data=&defaultFormData($symb,$url);
   
       my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
       my ($scanlines,$scan_data)=&scantron_getfile();
       my $classlist=&Apache::loncoursedata::get_classlist();
       my %idmap=&username_to_idmap($classlist);
       my $navmap=Apache::lonnavmaps::navmap->new();
       my $map=$navmap->getResourceByUrl($sequence);
       my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
   #    $r->print("geto ".scalar(@resources)."<br />");
       my $result= <<SCANTRONFORM;
   <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload">
     <input type="hidden" name="command" value="scantron_configphase" />
     $default_form_data
   SCANTRONFORM
       $r->print($result);
   
       my @delayqueue;
       my %completedstudents;
       
       my $count=&get_todo_count($scanlines,$scan_data);
       my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron Status',
        'Scantron Progress',$count,
       'inline',undef,'scantronupload');
       &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,
     'Processing first student');
       my $start=&Time::HiRes::time();
       my $i=-1;
       my ($uname,$udom,$started);
       while ($i<$scanlines->{'count'}) {
     ($uname,$udom)=('','');
     $i++;
     my $line=&scantron_get_line($scanlines,$scan_data,$i);
     if ($line=~/^[\s\cz]*$/) { next; }
    if ($started) {
       &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
        'last student');
    }
    $started=1;
     my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
     $scan_data);
     unless ($uname=&scantron_find_student($scan_record,$scan_data,
          \%idmap,$i)) {
        &scantron_add_delay(\@delayqueue,$line,
     'Unable to find a student that matches',1);
        next;
     }
     if (exists $completedstudents{$uname}) {
        &scantron_add_delay(\@delayqueue,$line,
     'Student '.$uname.' has multiple sheets',2);
        next;
     }
     ($uname,$udom)=split(/:/,$uname);
     &Apache::lonnet::delenv('form.counter');
     &Apache::lonnet::appenv(%$scan_record);
   
    my $i=0;
    foreach my $resource (@resources) {
       $i++;
       my %form=('submitted'     =>'scantron',
         'grade_target'  =>'grade',
         'grade_username'=>$uname,
         'grade_domain'  =>$udom,
         'grade_courseid'=>$env{'request.course.id'},
         'grade_symb'    =>$resource->symb());
       if (exists($scan_record->{'scantron.CODE'}) &&
    $scan_record->{'scantron.CODE'}) {
    $form{'CODE'}=$scan_record->{'scantron.CODE'};
       } else {
    $form{'CODE'}='';
       }
       my $result=&Apache::lonnet::ssi($resource->src(),%form);
       if ($result ne '') {
    &Apache::lonnet::logthis("scantron grading error -> $result");
    &Apache::lonnet::logthis("scantron grading error info name $uname domain $udom course $env{'request.course.id'} url ".$resource->src());
       }
       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('scantron\.');
       }
       &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 $home=&Apache::lonnet::homeserver($env{'form.courseid'},
    $env{'form.domainid'});
       my $fname=$env{'form.upfile.filename'};
     #FIXME      #FIXME
     #   loop through students, {      #copied from lonnet::userfileupload()
     #      Check if studnet info valid, if not add line to delay queue      #make that function able to target a specified course
     #      foreach question 'submit' the students answer to the server      # Replace Windows backslashes by forward slashes
     #             through grade target {      $fname=~s/\\/\//g;
     #         generate data to pass back that includes grade recevied      # Get rid of everything but the actual filename
     #      }      $fname=~s/^.*\/([^\/]+)$/$1/;
     #   }      # Replace spaces by underscores
     #   loop through delay queue {      $fname=~s/\s+/\_/g;
     #      print out each delayed student with interface to select how      # Replace all other weird characters by nothing
     #          to repair student provided info      $fname=~s/[^\w\.\-]//g;
     #       Expected errors include      # See if there is anything left
     #           1 bad/no stuid/username      unless ($fname) { return 'error: no uploaded file'; }
     #           2 invalid bubblings      my $uploadedfile=$fname;
     #   }      $fname='scantron_orig_'.$fname;
     # if delay queue exists 2 submits one to process delayed students one      if (length($env{'form.upfile'}) < 2) {
     #     to ignore delayed students, possibly saving the delay queue for later   $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'},$home,'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 2897  sub show_grading_menu_form { Line 5241  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 2915  sub gradingmenu { Line 5259  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[4].checked) formname.submit();   } else {
       cmd = cmdx;
  if (cmd[3].checked) browseAndUpload();      cmdsave = 'submission';
    }
  if (cmd[5].checked) {   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 2940  sub gradingmenu { Line 5289  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;  
  }  
     }  
   
     function browseAndUpload() {  
  bNLoad = window.open('', 'BrowseAndUpload', 'toolbar=no,location=no,scrollbars=no,width=550,height=200,screenx=100,screeny=75');  
  bNLoad.focus();  
  var lDoc = bNLoad.document;  
  lDoc.write("<html><head>");  
  lDoc.write("<title>Browse And Upload</title>");  
   
  lDoc.write("<script language=javascript>");  
  lDoc.write("function checkUpload(formname) {");  
   
  lDoc.write("  if (formname.upfile.value == \\"\\") {");  
  lDoc.write("     alert(\\"Please use the browse button to select a file from your local directory.\\");");  
  lDoc.write("     return false;");  
  lDoc.write("   }");  
  lDoc.write("   var openformname = opener.document.gradingMenu;");  
  lDoc.write("   formname.saveState.value = \\"saveCmd=\\"+opener.radioSelection(openformname.command)+\\":saveSec=\\"+opener.pullDownSelection(openformname.section)+\\":saveSub=\\"+opener.radioSelection(openformname.submitonly)+\\":saveStatus=\\"+opener.pullDownSelection(openformname.status);");  
  lDoc.write("   document.gradesupload.submit();");  
  lDoc.write("   if (navigator.appName !=\\"Netscape\\") {self.close()};");  
  lDoc.write("   setTimeout('self.close()',750)");   
  lDoc.write("}");  
   
  lDoc.write("<");  
  lDoc.write("/script>");  
   
  lDoc.write("</head><body bgcolor=white>");  
  lDoc.write("<form method=\\"post\\" enctype=\\"multipart/form-data\\" action=\\"/adm/grades\\" name=\\"gradesupload\\" target=\\"LONcatInfo\\">");  
  lDoc.write("<input type=\\"hidden\\" name=\\"symb\\"       value=\\"$symb\\">");  
  lDoc.write("<input type=\\"hidden\\" name=\\"url\\"        value=\\"$url\\">");  
  lDoc.write("<input type=\\"hidden\\" name=\\"probTitle\\"  value=\\"$probTitle\\">");  
  lDoc.write("<input type=\\"hidden\\" name=\\"saveState\\"  value=\\"\\">");  
  lDoc.write("<input type=\\"hidden\\" name=\\"command\\"    value=\\"csvuploadmap\\">");  
   
  lDoc.write("<font color=\\"green\\" size=+1>&nbsp;<b>Specify a file containing the class scores for problem - $probTitle</b></font><br><br>");  
   
  lDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");  
  lDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");  
  lDoc.write("<td>");  
  lDoc.write("<input type=\\"file\\" name=\\"upfile\\" size=\\"50\\" />");  
  lDoc.write("<br />Type: <select name=\\"upfiletype\\">");  
         lDoc.write("<option value=\\"csv\\">CSV (comma separated values, spreadsheet)</option>");  
         lDoc.write("<option value=\\"space\\">Space separated</option>");  
         lDoc.write("<option value=\\"tab\\">Tabulator separated</option>");  
         lDoc.write("<option value=\\"xml\\">HTML/XML</option>");  
         lDoc.write("</select>");  
         lDoc.write("</td></tr></table>");  
         lDoc.write("</td></tr></table>&nbsp;");  
         lDoc.write("<input type=\\"button\\" value=\\"Upload Scores\\" onClick=\\"javascript:checkUpload(this.form)\\">&nbsp;&nbsp;");  
         lDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br><br>");  
         lDoc.write("</form>");  
         lDoc.write("</body></html>");  
   }  
 </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; ';
   
  '<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.      $result.=&mt('Student Status').':</b>'.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,undef);
  '<input type="radio" name="command" value="viewgrades" '.  
       $result.='</td></tr>';
   
       $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.
    '<input type="radio" name="radioChoice" value="submission" '.
    ($saveCmd eq 'submission' ? 'checked' : '').'> '.'<b>'.&mt('Current Resource').':</b> '.&mt('For one or more students').
    ' <select name="submitonly">'.
    '<option value="yes" '.
    ($saveSub eq 'yes' ? 'selected="on"' : '').'>with submissions</option>'.
    '<option value="graded" '.
    ($saveSub eq 'graded' ? 'selected="on"' : '').'>with ungraded submissions</option>'.
    '<option value="incorrect" '.
    ($saveSub eq 'incorrect' ? 'selected="on"' : '').'>with incorrect submissions</option>'.
    '<option value="all" '.
    ($saveSub eq 'all' ? 'selected="on"' : '').'>with any status</option></select></td></tr>'."\n";
   
       $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.
    '<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</td></tr>'."\n";
   
  '<tr bgcolor="#ffffe6"valign="top"><td><input type="radio" name="command" value="submission" '.      $result.='<tr bgcolor="#ffffe6" valign="top"><td>'.
  ($saveCmd eq 'submission' ? 'checked' : '').'> '.   '<input type="radio" name="radioChoice" value="pickStudentPage" '.
  ($hdgrade eq 'yes' ? 'View/Grade essay response of' : 'View').   ($saveCmd eq 'pickStudentPage' ? 'checked' : '').'> '.
  ' an individual student </td>'."\n".   'The <b>complete</b> set/page/sequence: For one student</td></tr>'."\n";
  '<td>-->&nbsp;For students who has: '.  
  '<input type="radio" name="submitonly" value="yes" '.      $result.='<tr bgcolor="#ffffe6"><td><br />'.
  ($saveSub eq 'yes' ? 'checked' : '').' /> submitted'.   '<input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="Next->" />'.
  '<input type="radio" name="submitonly" value="all" '.   '</td></tr></table>'."\n";
  ($saveSub eq 'all' ? 'checked' : '').' /> everybody</td></tr>'."\n".  
       $result.='</td><td valign="top">';
  '<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.  
  '<input type="radio" name="command" value="csvupload" '.      $result.='<table width="100%" border=0>';
  ($saveCmd eq 'csvupload' ? 'checked' : '').'> '.      $result.='<tr bgcolor="#ffffe6"><td>'.
  'Upload scores from file</td></tr>'."\n";   '<input type="button" onClick="javascript:checkChoice(this.form,\'3\',\'csvform\');" value="'.&mt('Upload').'" />'.
    ' '.&mt('scores from file').' </td></tr>'."\n";
   
     $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.      $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.
  '<input type="radio" name="command" value="scantron_selectphase" /> '.   '<input type="button" onClick="javascript:checkChoice(this.form,\'4\',\'scantron_selectphase\');'.
         'Grade scantron forms</td></tr>'."\n";   '" value="'.&mt('Grade').'" /> scantron forms</td></tr>'."\n";
   
     if ((&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'})) && ($symb)) {      if ((&Apache::lonnet::allowed('mgr',$env{'request.course.id'})) && ($symb)) {
  $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.   $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.
     '<input type="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"><br />'."\n".      $result.='</form></td></tr></table>'."\n".
  '&nbsp;Select section: <select name="section">'."\n";  
     if (ref($sections)) {  
  foreach (sort (@$sections)) {$result.='<option value="'.$_.'" '.  
  ($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">'.  
  '<option value="Active" '.($saveStatus eq 'Active' ? 'selected' : '').'>Active</option>'.  
  '<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";  
   
     if (ref($sections)) {  
  $result.='&nbsp;(Section "no" implies the students were not assigned a section.)<br />'   
     if (grep /no/,@$sections);  
     }  
     $result.='</td></tr>';  
   
     $result.='<tr bgcolor="#ffffe6"><td colspan="2"><br />'.  
  '<input type="button" onClick="javascript:checkChoice(this.form);" value="View/Grade" />'."\n".  
  '</form></td></tr></table>'."\n".  
  '</td></tr></table>'."\n".   '</td></tr></table>'."\n".
  '</td></tr></table>'."\n";   '</td></tr></table>'."\n";
     return $result;      return $result;
Line 3133  GRADINGMENUJS Line 5392  GRADINGMENUJS
   
 sub handler {  sub handler {
     my $request=$_[0];      my $request=$_[0];
       
     if ($ENV{'browser.mathml'}) {      undef(%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 3179  sub handler { Line 5442  sub handler {
     }      }
  }   }
     } else {      } else {
  $Apache::grades::viewgrades=&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'});   if (!($perm{'vgr'}=&Apache::lonnet::allowed('vgr',$env{'request.course.id'}))) {
  if ($command eq 'submission') {      if ($perm{'vgr'}=&Apache::lonnet::allowed('vgr',$env{'request.course.id'}.'/'.$env{'request.course.sec'})) {
     ($ENV{'form.student'} eq '' ? &listStudents($request) : &submission($request,0,0));   $perm{'vgr_section'}=$env{'request.course.sec'};
  } elsif ($command eq 'pickStudentPage') {      } else {
    delete($perm{'vgr'});
       }
    }
    if (!($perm{'mgr'}=&Apache::lonnet::allowed('mgr',$env{'request.course.id'}))) {
       if ($perm{'mgr'}=&Apache::lonnet::allowed('mgr',$env{'request.course.id'}.'/'.$env{'request.course.sec'})) {
    $perm{'mgr_section'}=$env{'request.course.sec'};
       } else {
    delete($perm{'mgr'});
       }
    }
    if ($command eq 'submission' && $perm{'vgr'}) {
       ($env{'form.student'} eq '' ? &listStudents($request) : &submission($request,0,0));
    } 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_configphase') {    } elsif ($command eq 'scantron_warning' && $perm{'mgr'}) {
     $request->print(&scantron_configphase($request));       $request->print(&scantron_do_warning($request));
  } else {   } elsif ($command eq 'scantron_validate' && $perm{'mgr'}) {
     $request->print("Unknown action: $command:");      $request->print(&scantron_validate_file($request));
    } elsif ($command eq 'scantron_process' && $perm{'mgr'}) {
       $request->print(&scantron_process_students($request));
     } elsif ($command eq 'scantronupload' && 
     (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||
     &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {
        $request->print(&scantron_upload_scantron_data($request)); 
     } elsif ($command eq 'scantronupload_save' &&
     (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||
     &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {
        $request->print(&scantron_upload_scantron_data_save($request));
     } elsif ($command eq 'scantron_download' &&
    &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {
        $request->print(&scantron_download_scantron_data($request));
    } elsif ($command) {
       $request->print("Access Denied ($command)");
  }   }
     }      }
     &send_footer($request);      &send_footer($request);
Line 3240  sub send_header { Line 5531  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.80  
changed lines
  Added in v.1.273


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