Diff for /loncom/homework/grades.pm between versions 1.33 and 1.46

version 1.33, 2002/06/27 21:34:18 version 1.46, 2002/08/06 20:46:30
Line 30 Line 30
 # 7/26 H.K. Ng  # 7/26 H.K. Ng
 # 8/20 Gerd Kortemeyer  # 8/20 Gerd Kortemeyer
 # Year 2002  # Year 2002
 # June 2002 H.K. Ng  # June-August H.K. Ng
 #  #
   
 package Apache::grades;  package Apache::grades;
Line 40  use Apache::lonxml; Line 40  use Apache::lonxml;
 use Apache::lonnet;  use Apache::lonnet;
 use Apache::loncommon;  use Apache::loncommon;
 use Apache::lonhomework;  use Apache::lonhomework;
   use Apache::lonmsg qw(:user_normal_msg);
 use Apache::Constants qw(:common);  use Apache::Constants qw(:common);
   
 sub moreinfo {  # ----- These first few routines are general use routines.-----
   my ($request,$reason) = @_;  #
   $request->print("Unable to process request: $reason");  # --- Retrieve the parts that matches stores_\d+ from the metadata file.---
   if ( $Apache::grades::viewgrades eq 'F' ) {  sub getpartlist {
     $request->print('<form action="/adm/grades" method="post">'."\n");      my ($url) = @_;
     if ($ENV{'form.url'}) {      my @parts =();
       $request->print('<input type="hidden" name="url" value="'.$ENV{'form.url'}.'" />'."\n");      my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));
     }      foreach my $key (@metakeys) {
     if ($ENV{'form.symb'}) {   if ( $key =~ m/stores_([0-9]+)_.*/) {
       $request->print('<input type="hidden" name="symb" value="'.$ENV{'form.symb'}.'" />'."\n");      push(@parts,$key);
    }
     }      }
     $request->print('<input type="hidden" name="command" value="'.$ENV{'form.command'}.'" />'."\n");      return @parts;
     $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 '';  
 }  }
   
 sub verifyreceipt {  # --- Get the symbolic name of a problem and the url
     my $request=shift;  sub get_symb_and_url {
     my $courseid=$ENV{'request.course.id'};      my ($request) = @_;
     my $cdom=$ENV{"course.$courseid.domain"};      (my $url=$ENV{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
     my $cnum=$ENV{"course.$courseid.num"};      my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));
     my $receipt=unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}).'-'.      if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
                 $ENV{'form.receipt'};      return ($symb,$url);
     $receipt=~s/[^\-\d]//g;  }
     my $symb=$ENV{'form.symb'};  
     unless ($symb) {  # --- Retrieve the fullname for a user. Return lastname, first middle ---
  $symb=&Apache::lonnet::symbread($ENV{'form.url'});  # --- Generation is attached next to the lastname if it exists. ---
     }  sub get_fullname {
     if ((&Apache::lonnet::allowed('mgr',$courseid)) && ($symb)) {      my ($uname,$udom) = @_;
         $request->print('<h1>Verifying Submission Receipt '.$receipt.'</h1>');      my %name=&Apache::lonnet::get('environment', ['lastname','generation',
         my $matches=0;    'firstname','middlename'],$udom,$uname);
         my (%classlist) = &getclasslist($cdom,$cnum,'0');      my $fullname;
         foreach my $student ( sort(@{ $classlist{'allids'} }) ) {      my ($tmp) = keys(%name);
             my ($uname,$udom)=split(/\:/,$student);      if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
             if ($receipt eq    $fullname=$name{'lastname'}.$name{'generation'};
              &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb)) {   if ($fullname =~ /[^\s]+/) { $fullname.=', '; }
                $request->print('Matching '.$student.'<br>');   $fullname.=$name{'firstname'}.' '.$name{'middlename'};
                $matches++;  
    }  
         }  
         $request->printf('<p>'.$matches." match%s</p>",$matches <= 1 ? '' : 'es');  
 # needs to print who is matched  
     }      }
     return '';      return $fullname;
 }  }
   
 sub receiptInput {  #--- Get the partlist and the response type for a given problem. ---
   my ($request) = shift;  #--- Indicate if a response type is coded handgraded or not. ---
   my $cdom=$ENV{"course.$ENV{'request.course.id'}.domain"};  sub response_type {
   my $cnum=$ENV{"course.$ENV{'request.course.id'}.num"};      my ($url) = shift;
   my $hostver=unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'});      my $allkeys = &Apache::lonnet::metadata($url,'keys');
   $request->print(<<ENDHEADER);      my %seen = ();
 <h2><font color="#339933">Verify a Submission Receipt Issued by this Server</font></h2>      my (@partlist,%handgrade);
 <form action="/adm/grades" method="post">      foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) {
 <tt>$hostver-<input type="text" name="receipt" size="4"></tt>   if (/^\w+response_\d+.*/) {
 <input type="submit" name="submit" value="Verify">      my ($responsetype,$part) = split(/_/,$_,2);
 <input type="hidden" name="command" value="verify">      my ($partid,$respid) = split(/_/,$part);
 ENDHEADER      $handgrade{$part} = $responsetype.':'.($allkeys =~ /parameter_$part\_handgrade/ ? 'yes' : 'no');
   if ($ENV{'form.url'}) {      next if ($seen{$partid} > 0);
     $request->print(      $seen{$partid}++;
     '<input type="hidden" name="url" value="'.$ENV{'form.url'}.'" />');      push @partlist,$partid;
   }   }
   if ($ENV{'form.symb'}) {      }
     $request->print(      return \@partlist,\%handgrade;
     '<input type="hidden" name="symb" value="'.$ENV{'form.symb'}.'" />');  
   }  
 #  $request->print(<<ENDTABLEST);  
   $request->print('</form>');  
     return '';  
 }  }
   
 sub student_gradeStatus {  #--- Dumps the class list with usernames,list of sections,
   my ($url,$udom,$uname) = @_;  #--- section, ids and fullnames for each user.
   my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));  sub getclasslist {
   my %record= &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);      my ($getsec,$hideexpired) = @_;
   foreach my $part (&getpartlist($url)) {      my $now = time;
     my ($temp,$part,$type)=split(/_/,$part);      my %classlist=&Apache::lonnet::dump('classlist',
     if ($type eq 'solved') {   $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
       my ($status,$foo)=split(/_/,$record{"resource.$part.$type"},2);   $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
       $status = 'nothing' if ($status eq '');      # codes to check for fields in the classlist
       return $type,$status;      # should contain end:start:id:section:fullname
       for (keys %classlist) {
    my (@fields) = split(/:/,$classlist{$_});
    %classlist   = &reformat_classlist(\%classlist) if (scalar(@fields) <= 2);
    last;
       }
   
       my (@holdsec,@sections,%allids,%stusec,%fullname);
       foreach (keys(%classlist)) {
    my ($end,$start,$id,$section,$fullname)=split(/:/,$classlist{$_});
    # still a student?
    if (($hideexpired) && ($end) && ($end < $now)) {
       next;
    }
    $section = ($section ne '' ? $section : 'no');
    push @holdsec,$section;
    if ($getsec eq 'all' || $getsec eq $section) {
       push (@{ $classlist{$getsec} }, $_);
       $allids{$_}  =$id;
       $stusec{$_}  =$section;
       $fullname{$_}=$fullname;
    }
     }      }
   }      my %seen = ();
   return '';      foreach my $item (@holdsec) {
    push (@sections, $item) unless $seen{$item}++;
       }
       return (\%classlist,\@sections,\%allids,\%stusec,\%fullname);
 }  }
   
 sub listStudents {  # add id, section and fullname to the classlist.db
   my ($request) = shift;  # done to maintain backward compatibility with older versions
   my $cdom=$ENV{"course.$ENV{'request.course.id'}.domain"};  sub reformat_classlist {
   my $cnum=$ENV{"course.$ENV{'request.course.id'}.num"};      my ($classlist) = shift;
       foreach (sort keys(%$classlist)) {
   $request->print(<<ENDTABLEST);   my ($unam,$udom) = split(/:/);
 <h2><font color="#339933">Show Student Submissions on Assessment</font></h2>   my $section      = &Apache::lonnet::usection($udom,$unam,$ENV{'request.course.id'});
    my $fullname     = &get_fullname ($unam,$udom);
 <table border="0"><tr><td bgcolor="#777777">   my %userid       = &Apache::lonnet::idrget($udom,($unam));
 <table border="0">   $$classlist{$_}  = $$classlist{$_}.':'.$userid{$unam}.':'.$section.':'.$fullname;
 <tr bgcolor="#e6ffff"><td colspan="7"><b>Resource: </b> $ENV{'form.url'}</td></tr>      }
 <tr bgcolor="#e6ffff"><td><b>Username</b></td><td><b>Name</b></td><td><b>Domain</b></td>      my $putresult = &Apache::lonnet::put
 <td><b>View Problem</b></td><td><b>Submissions</b></td>   ('classlist',\%$classlist,
 <td><b>Grade Status</b></td><td><b>Action</b></td></tr>   $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
 ENDTABLEST   $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
   my (%classlist) = &getclasslist($cdom,$cnum,'0');  
   foreach my $student ( sort(@{ $classlist{'allids'} }) ) {  
       my ($sname,$sdom) = split(/:/,$student);  
   
       my %name=&Apache::lonnet::get('environment', ['lastname','generation',  
     'firstname','middlename'],  
     $sdom,$sname);  
       my $fullname;  
       my ($tmp) = keys(%name);  
       if ($tmp !~ /^(con_lost|error|no_such_host)/i) {  
  $fullname=$name{'lastname'}.$name{'generation'};  
  if ($fullname =~ /[^\s]+/) { $fullname.=', '; }  
  $fullname.=$name{'firstname'}.' '.$name{'middlename'};  
       }  
       if ( $Apache::grades::viewgrades eq 'F' ) {  
   $request->print("\n".'<tr bgcolor=#ffffe6>'."<td>$sname</td><td>$fullname</td><td align=\"middle\">$sdom</td>".  
   '<form action="/adm/grades" method="post">');  
   if ($ENV{'form.url'}) {  
     $request->print(  
     '<input type="hidden" name="url" value="'.$ENV{'form.url'}.'" />');  
   }  
   if ($ENV{'form.symb'}) {  
     $request->print(  
     '<input type="hidden" name="symb" value="'.$ENV{'form.symb'}.'" />');  
   }  
   $request->print(  
   '<input type="hidden" name="command" value="'.$ENV{'form.command'}.'" />');  
   $request->print(  
   '<input type="hidden" name="student" value="'.$sname.'" />');  
   $request->print(  
   '<input type="hidden" name="fullname" value="'.$fullname.'" />');  
   $request->print(  
   '<input type="hidden" name="domain" value="'.$sdom.'" />');  
   $request->print('<td>'.  
   '<input type="radio" name="vProb" value="no" checked> no '.  
   '<input type="radio" name="vProb" value="yes"> yes </td>');  
   $request->print('<td>'.  
   '<input type="radio" name="lastSub" value="last" checked> last '.  
   '<input type="radio" name="lastSub" value="all"> all </td>');  
   my ($type,$status) = &student_gradeStatus($ENV{'form.url'},$cdom,$sname);  
   $request->print(  
   '<td align="middle">'.$status.'&nbsp;</td>');  
   $request->print(  
                          '<td><input type="submit" name="submit" value="View/Grade" />');  
   $request->print('</td></tr></form>');  
 #  $request->print('</form></td></tr>');  
       }  
   }  
   $request->print('</table></td></tr></table>');  
 }  
   
       return %$classlist;
   }
   
 #FIXME - needs to handle multiple matches  #find user domain
 sub finduser {  sub finduser {
   my ($name) = @_;      my ($name) = @_;
   my $domain = '';      my $domain = '';
       if ( $Apache::grades::viewgrades eq 'F' ) {
   if ( $Apache::grades::viewgrades eq 'F' ) {   my %classlist=&Apache::lonnet::dump('classlist',
     #get classlist      $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
     my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});      $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
     #print "Found $cdom:$cnum<br />";   my (@fields) = grep /^$name:/, keys %classlist;
     my (%classlist) = &getclasslist($cdom,$cnum,'0');   ($name, $domain) = split(/:/,$fields[0]);
     foreach my $student ( sort(@{ $classlist{'allids'} }) ) {   return ($name,$domain);
       my ($posname,$posdomain) = split(/:/,$student);      } else {
       if ($posname =~ $name) { $name=$posname; $domain=$posdomain; last; }   return ($ENV{'user.name'},$ENV{'user.domain'});
     }      }
     return ($name,$domain);  
   } else {  
     return ($ENV{'user.name'},$ENV{'user.domain'});  
   }  
 }  }
   
 sub getclasslist {  #--- Prompts a user to enter a username.
   my ($coursedomain,$coursenum,$hideexpired) = @_;  sub moreinfo {
   my %classlist=&Apache::lonnet::dump('classlist',$coursedomain,$coursenum);      my ($request,$reason) = @_;
   my $now = time;      $request->print("Unable to process request: $reason");
   foreach my $student (keys(%classlist)) {      if ( $Apache::grades::viewgrades eq 'F' ) {
     my ($end,$start)=split(/:/,$classlist{$student});   $request->print('<form action="/adm/grades" method="post">'."\n");
     # still a student?   if ($ENV{'form.url'}) {
     if (($hideexpired) && ($end) && ($end < $now)) {      $request->print('<input type="hidden" name="url" value="'.$ENV{'form.url'}.'" />'."\n");
       #print "Skipping:$name:$end:$now<br />\n";   }
       next;   if ($ENV{'form.symb'}) {
       $request->print('<input type="hidden" name="symb" value="'.$ENV{'form.symb'}.'" />'."\n");
    }
    $request->print('<input type="hidden" name="command" value="'.$ENV{'form.command'}.'" />'."\n");
    $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>');
     }      }
     #print "record=$record<br>";      return '';
     push( @{ $classlist{'allids'} }, $student);  
   }  
   return (%classlist);  
 }  }
   
 sub getpartlist {  #--- Retrieve the grade status of a student for all the parts
   my ($url) = @_;  sub student_gradeStatus {
   my @parts =();      my ($url,$symb,$udom,$uname,$partlist) = @_;
   my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));      my %record     = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
   foreach my $key (@metakeys) {      my %partstatus = ();
     if ( $key =~ m/stores_([0-9]+)_.*/) {      foreach (@$partlist) {
       push(@parts,$key);   my ($status,$foo)    = split(/_/,$record{"resource.$_.solved"},2);
     }   $status              = 'nothing' if ($status eq '');
   }   $partstatus{$_}      = $status;
   return @parts;   my $subkey           = "resource.$_.submitted_by";
    $partstatus{$subkey} = $record{$subkey} if ($record{$subkey} ne '');
       }
       return %partstatus;
   }
   
   # hidden form and javascript that calls the form
   # Use by verifyscript and viewgrades
   # Shows a student's view of problem and submission
   sub jscriptNform {
       my ($url,$symb) = @_;
       my $jscript='<script type="text/javascript" language="javascript">'."\n".
    '    function viewOneStudent(user,domain) {'."\n".
    ' document.onestudent.student.value = user;'."\n".
    ' document.onestudent.userdom.value = domain;'."\n".
    ' document.onestudent.submit();'."\n".
    '    }'."\n".
    '</script>'."\n";
       $jscript.= '<form action="/adm/grades" method="post" name="onestudent">'."\n".
    '<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
    '<input type="hidden" name="url"     value="'.$url.'" />'."\n".
    '<input type="hidden" name="command" value="submission" />'."\n".
    '<input type="hidden" name="student" value="" />'."\n".
    '<input type="hidden" name="userdom" value="" />'."\n".
    '</form>'."\n";
       return $jscript;
 }  }
   
 sub viewstudentgrade {  #------------------ End of general use routines --------------------
   my ($url,$symb,$courseid,$student,@parts) = @_;  #-------------------------------------------------------------------
   my $result ='';  
   my $cellclr = '"#ffffdd"';  #------------------------------------ Receipt Verification Routines
   my ($username,$domain) = split(/:/,$student);  #
   #--- Check whether a receipt number is valid.---
   my (@requests) = ('lastname','firstname','middlename','generation');  sub verifyreceipt {
   my (%name) = &Apache::lonnet::get('environment',\@requests,$domain,$username);      my $request  = shift;
   my %record=&Apache::lonnet::restore($symb,$courseid,$domain,$username);  
       my $courseid = $ENV{'request.course.id'};
   my $fullname=$name{'lastname'}.$name{'generation'};      my $receipt  = unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}).'-'.
   if ($fullname =~ /[^\s]+/) { $fullname.=', '; }   $ENV{'form.receipt'};
   $fullname.=$name{'firstname'}.' '.$name{'middlename'};      $receipt     =~ s/[^\-\d]//g;
       my $url      = $ENV{'form.url'};
   $result.="<tr bgcolor=$cellclr><td>$username</td><td>$fullname</td><td align=\"middle\">$domain</td>\n";      my $symb     = $ENV{'form.symb'};
   foreach my $part (@parts) {      unless ($symb) {
     my ($temp,$part,$type)=split(/_/,$part);   $symb    = &Apache::lonnet::symbread($url);
     my $score=$record{"resource.$part.$type"};  
     if ($type eq 'awarded' || $type eq 'tries') {  
       $result.='<td align="middle"><input type="text" name="GRADE.'.$student.'.'.$part.'.'.$type.  
   '" value="'.$score.'" size="4" /></td>'."\n";  
     } elsif ($type eq 'solved') {  
       my ($status,$foo)=split(/_/,$score,2);  
       $result.="<td align=\"middle\"><select name=\"GRADE.$student.$part.$type\">\n";  
       my $optsel = '<option>correct</option><option>incorrect</option><option>excused</option>'.  
   '<option>ungraded</option><option>partial</option><option>nothing</option>'."\n";  
       $status = 'nothing' if ($status eq '');  
       $optsel =~ s/<option>$status/<option selected="on">$status/;  
       $result.=$optsel;  
       $result.="</select></td>\n";  
     }      }
   }  
   $result.='<td></td></tr>';  
   return $result;  
 }  
   
 #FIXME need to look at the metadata <stores> spec on what type of data to accept and provide an      my $title.='<h3><font color="#339933">Verifying Submission Receipt '.
 #interface based on that, also do that to above function.   $receipt.'</h3></font>'."\n".
 sub setstudentgrade {   '<font size=+1><b>Resource: </b>'.$ENV{'form.url'}.'</font><br><br>'."\n";
   my ($url,$symb,$courseid,$student,@parts) = @_;  
       my ($string,$contents,$matches) = ('','',0);
   my $result ='';      my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist('all','0');
   my ($stuname,$domain) = split(/:/,$student);      
   my %record=&Apache::lonnet::restore($symb,$courseid,$domain,$stuname);      foreach (sort {$$fullname{$a} cmp $$fullname{$b} } keys %$fullname) {
   my %newrecord;   my ($uname,$udom)=split(/\:/);
    if ($receipt eq 
   foreach my $part (@parts) {      &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb)) {
     my ($temp,$part,$type)=split(/_/,$part);      $contents.='<tr bgcolor="#ffffe6"><td>&nbsp;'."\n".
     my $oldscore=$record{"resource.$part.$type"};   '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.
     my $newscore=$ENV{"form.GRADE.$student.$part.$type"};   '\')"; TARGET=_self>'.$$fullname{$_}.'</a>&nbsp;</td>'."\n".
     print "old=$oldscore:new=$newscore:<br>";   '<td>&nbsp;'.$uname.'&nbsp;</td>'.
     if ($type eq 'solved') {   '<td>&nbsp;'.$udom.'&nbsp;</td></tr>'."\n";
       my $update=0;      
       if ($newscore eq 'nothing' ) {      $matches++;
  if ($oldscore ne '') {   }
   $update=1;      }
   $newscore = '';      if ($matches == 0) {
  }   $string = $title.'No match found for the above receipt.';
       } elsif ($oldscore !~ m/^$newscore/) {  
  $update=1;  
  $result.="Updating $stuname to $newscore<br />\n";  
  if ($newscore eq 'correct') { $newscore = 'correct_by_override'; }  
  if ($newscore eq 'incorrect') { $newscore = 'incorrect_by_override'; }  
  if ($newscore eq 'excused') { $newscore = 'excused'; }  
  if ($newscore eq 'ungraded') { $newscore = 'ungraded_attempted'; }  
       } else {  
  #$result.="$stuname:$part:$type:unchanged  $oldscore to $newscore:<br />\n";  
       }  
       if ($update) { $newrecord{"resource.$part.$type"}=$newscore; }  
     } else {      } else {
       if ($oldscore ne $newscore) {   $string = &jscriptNform($url,$symb).$title.
  $newrecord{"resource.$part.$type"}=$newscore;      'The above receipt matches the following student'.
  $result.="Updating $student"."'s status for $part.$type to $newscore<br />\n";      ($matches <= 1 ? '.' : 's.')."\n".
       } else {      '<table border="0"><tr><td bgcolor="#777777">'."\n".
  #$result.="$stuname:$part:$type:unchanged  $oldscore to $newscore:<br />\n";      '<table border="0"><tr bgcolor="#e6ffff">'."\n".
       }      '<td><b>&nbsp;Fullname&nbsp;</b></td>'."\n".
       '<td><b>&nbsp;Username&nbsp;</b></td>'."\n".
       '<td><b>&nbsp;Domain&nbsp;</b></td></tr>'."\n".
       $contents.
       '</table></td></tr></table>'."\n";
       }
       return $string.&show_grading_menu_form ($symb,$url);
   }
   
   #--- This is called by a number of programs.
   #--- Called from the Grading Menu - View/Grade an individual student
   #--- Also called directly when one clicks on the subm button 
   #    on the problem page.
   sub listStudents {
       my ($request) = shift;
       $request->print(<<LISTJAVASCRIPT);
   <script type="text/javascript" language="javascript">
     function checkSelect(checkBox) {
       var ctr=0;
       var sense="";
       if (checkBox.length > 1) {
          for (var i=0; i<checkBox.length; i++) {
     if (checkBox[i].checked) {
        ctr++;
     }
          }
          sense = "a student or group of students";
       } else {
          if (checkBox.checked) {
      ctr = 1;
          }
          sense = "the student";
       }
       if (ctr == 0) {
          alert("Please select "+sense+" before clicking on the View/Grade button.");
          return false;
     }      }
       document.gradesub.submit();
   }    }
   if ( scalar(keys(%newrecord)) > 0 ) {  </script>
     $newrecord{'resource.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";  LISTJAVASCRIPT
     print "grader=$newrecord{'resource.regrader'}:<br>records<br>";  
     while (my ($k,$v) = each %newrecord) {      my $cdom      = $ENV{"course.$ENV{'request.course.id'}.domain"};
  print "k=$k:v=$v:<br>\n";      my $cnum      = $ENV{"course.$ENV{'request.course.id'}.num"};
       my $getsec    = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};
       my $submitonly= $ENV{'form.submitonly'} eq '' ? 'all' : $ENV{'form.submitonly'};
   
       my $result='<h3><font color="#339933">&nbsp;'.
    'View/Grade Submissions for a Student or a Group of Students</font></h3>';
       $result.='<table border="0">';
       $result.='<tr><td colspan=3><font size=+1>'.
    '<b>Resource: </b>'.$ENV{'form.url'}.'</font></td></tr>';
       my ($partlist,$handgrade) = &response_type($ENV{'form.url'});
       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>';
       $request->print($result);
   
       my $checkhdgrade = $ENV{'form.handgrade'} eq 'yes' ? 'checked' : '';
       my $checklastsub = $ENV{'form.handgrade'} eq 'yes' ? '' : 'checked';
   
       my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'."\n".
    '&nbsp;<b>View Problem: </b><input type="radio" name="vProb" value="no" checked> no '."\n".
    '<input type="radio" name="vProb" value="yes"> yes <br />'."\n".
    '&nbsp;<b>Submissions: </b>'."\n".
    '<input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> handgrade only'."\n".
    '<input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> last sub only'."\n".
    '<input type="radio" name="lastSub" value="last" /> last sub & parts info'."\n".
    '<input type="radio" name="lastSub" value="all" /> all details'."\n".
    '<input type="hidden" name="section"     value="'.$getsec.'" />'."\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="showgrading" value="'.$ENV{'form.showgrading'}.'" /><br />'."\n".
    '<input type="hidden" name="url"  value="'.$ENV{'form.url'}.'" />'."\n".
    '<input type="hidden" name="symb" value="'.$ENV{'form.symb'}.'" />'."\n".
    'To view/grade a submission, click on the check box next to the student\'s name. Then '."\n".
    'click on the View/Grade button. To view the submissions for a group of students, click'."\n".
    ' on the check boxes for the group of students.<br />'."\n".
    '<input type="hidden" name="command" value="processGroup" />'."\n".
    '<input type="button" '."\n".
    'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".
    'value="View/Grade" />'."\n";
    
       my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist($getsec,'0');
       
       $gradeTable.='<table border="0"><tr><td bgcolor="#777777">'.
    '<table border="0"><tr bgcolor="#e6ffff">'.
    '<td><b>&nbsp;Select&nbsp;</b></td><td><b>&nbsp;Fullname&nbsp;</b></td>'.
    '<td><b>&nbsp;Username&nbsp;</b></td><td><b>&nbsp;Domain&nbsp;</b></td>';
       foreach (sort(@$partlist)) {
    $gradeTable.='<td><b>&nbsp;Part '.(split(/_/))[0].' Status&nbsp;</b></td>';
       }
       $gradeTable.='</tr>'."\n";
   
       my $ctr = 0;
       foreach my $student (sort {$$fullname{$a} cmp $$fullname{$b} } keys %$fullname) {
    my ($uname,$udom) = split(/:/,$student);
    my (%status) = &student_gradeStatus($ENV{'form.url'},
       $ENV{'form.symb'},$udom,$uname,$partlist);
    my $statusflg = '';
    foreach (keys(%status)) {
       $statusflg = 1 if ($status{$_} ne 'nothing');
       my ($foo,$partid,$foo1) = split(/\./,$_);
       if ($status{'resource.'.$partid.'.submitted_by'} ne '') {
    $statusflg = '';
    $gradeTable.='<input type="hidden" name="'.
       $student.':submitted_by" value="'.
       $status{'resource.'.$partid.'.submitted_by'}.'" />';
       }
    }
    next if ($statusflg eq '' && $submitonly eq 'yes');
   
    $ctr++;
    if ( $Apache::grades::viewgrades eq 'F' ) {
       $gradeTable.='<tr bgcolor="#ffffe6">'.
    '<td align="center"><input type=checkbox name="stuinfo" value="'.
    $student.':'.$$fullname{$student}.'"></td>'."\n".
    '<td>&nbsp;'.$$fullname{$student}.'&nbsp;</td>'."\n".
    '<td>&nbsp;'.$uname.'&nbsp;</td>'."\n".
    '<td align="middle">&nbsp;'.$udom.'&nbsp;</td>'."\n";
       
       foreach (sort keys(%status)) {
    next if (/^resource.*?submitted_by$/);
    $gradeTable.='<td align="middle">&nbsp;'.$status{$_}.'&nbsp;</td>'."\n";
       }
       $gradeTable.='</tr>'."\n";
    }
       }
       $gradeTable.='</table></td></tr></table>'.
    '<input type="button" '.
    'onClick="javascript:checkSelect(this.form.stuinfo);" '.
    'value="View/Grade" /><form />'."\n";
       if ($ctr == 0) {
    $gradeTable='<br />&nbsp;<font color="red">'.
       'No submission found for this resource.</font><br />';
    $gradeTable.=&show_grading_menu_form ($ENV{'form.symb'},$ENV{'form.url'});
       } elsif ($ctr == 1) {
    $gradeTable =~ s/type=checkbox/type=checkbox checked/;
     }      }
     &Apache::lonnet::cstore(\%newrecord,$symb,$courseid,$domain,$stuname);      $request->print($gradeTable);
       return '';
   }
   
     $result.="Stored away ".scalar(keys(%newrecord))." elements.<br />\n";  #---- Called from the listStudents routine
   }  #     Displays the submissions for one student or a group of students
   return $result;  sub processGroup {
       my ($request)  = shift;
       my $ctr        = 0;
       my @stuchecked = (ref($ENV{'form.stuinfo'}) ? @{$ENV{'form.stuinfo'}}
         : ($ENV{'form.stuinfo'}));
       my $total      = scalar(@stuchecked)-1;
   
       foreach (@stuchecked) {
    my ($uname,$udom,$fullname) = split(/:/);
    $ENV{'form.student'}        = $uname;
    $ENV{'form.userdom'}        = $udom;
    $ENV{'form.fullname'}       = $fullname;
    &submission($request,$ctr,$total);
    $ctr++;
       }
       return '';
 }  }
   
   #------------------------------------------------------------------------------------
 #  #
 # --------------------------- show submissions of a student, option to grade --------  #-------------------------- Next few routines handles grading by student, essentially
 sub submission {  #                           handles essay response type problem/part
   my ($request) = @_;  #
   #--- Javascript to handle the submission page functionality ---
   $request->print(<<JAVASCRIPT);  sub sub_page_js {
       my $request = shift;
       $request->print(<<SUBJAVASCRIPT);
 <script type="text/javascript" language="javascript">  <script type="text/javascript" language="javascript">
   function updateRadio(radioButton,formtextbox,formsel,wgt) {    function updateRadio(radioButton,formtextbox,formsel,scores,weight) {
      var pts = formtextbox.value;       var pts = formtextbox.value;
      var resetbox =false;       var resetbox =false;
      if (isNaN(pts) || pts < 0) {       if (isNaN(pts) || pts < 0) {
  alert("A number equal or greater than 0 is expected. Entered value = "+pts);          alert("A number equal or greater than 0 is expected. Entered value = "+pts);
  for (var i=0; i<radioButton.length; i++) {          for (var i=0; i<radioButton.length; i++) {
    if (radioButton[i].checked) {             if (radioButton[i].checked) {
       formtextbox.value = i;        formtextbox.value = i;
       resetbox = true;        resetbox = true;
    }     }
Line 361  sub submission { Line 476  sub submission {
    formtextbox.value = "";     formtextbox.value = "";
  }   }
  return;   return;
        }
   
        if (pts > weight) {
           var resp = confirm("You entered a value ("+pts+
             ") greater than the weight for the part. Accept?");
           if (resp == false) {
              formtextbox.value = "";
              return;
          }
     }      }
   
     for (var i=0; i<radioButton.length; i++) {      for (var i=0; i<radioButton.length; i++) {
Line 369  sub submission { Line 493  sub submission {
    radioButton[i].checked=true;     radioButton[i].checked=true;
  }   }
     }      }
     updateSelect(formsel,pts,wgt);      updateSelect(formsel);
       scores.value = "0";
   }    }
   
   function writeBox(formrad,formsel,pts,wgt) {    function writeBox(formrad,formsel,pts,scores) {
     formrad.value = pts;      formrad.value = pts;
     updateSelect(formsel,pts,wgt);      scores.value = "0";
       updateSelect(formsel,pts);
     return;      return;
   }    }
   
   function updateSelect(formsel,pts,wgt) {    function clearRadBox(radioButton,formbox,formsel,scores) {
     if (pts == 0) {      for (var i=0; i<formsel.length; i++) {
       formsel[1].selected = true;   if (formsel[i].selected) {
     }      var selectx=i;
     if (pts > 0 && pts < wgt) {   }
       formsel[4].selected = true;  
     }      }
     if (pts == wgt) {      if (selectx == scores.value) { return };
       formsel[0].selected = true;      formbox.value = "";
       for (var i=0; i<radioButton.length; i++) {
    radioButton[i].checked=false;
     }      }
       scores.value = selectx;
     }
   
     function updateSelect(formsel) {
       formsel[0].selected = true;
     return;      return;
   }    }
   
 </script>  //=================== Check that a point is assigned for all the parts  ==============
 JAVASCRIPT    function checksubmit(val,total,parttot) {
   (my $url=$ENV{'form.url'})=~s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;       document.SCORE.gradeOpt.value = val;
   if ($ENV{'form.student'} eq '') { &moreinfo($request,"Need student login id"); return ''; }       if (val == "Save & Next") {
   print "formstu=$ENV{'form.student'}:<br>";   for (i=0;i<=total;i++) {
   my ($uname,$udom) = &finduser($ENV{'form.student'});     for (j=0;j<parttot;j++) {
   if ($uname eq '') { &moreinfo($request,"Unable to find student"); return ''; }        var partid = eval("document.SCORE.partid"+i+"_"+j+".value");
         var selopt = eval("document.SCORE.GD_SEL"+i+"_"+partid);
         if (selopt[0].selected) {
    var points = eval("document.SCORE.GD_BOX"+i+"_"+partid+".value");
    if (points == "") {
        var name = eval("document.SCORE.name"+i+".value");
        alert("Please assign a score for "+name+", part "+partid+".");
        return false;
    }
         }
   
   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 ''; }         }
 #  
 # header info       }
   my $result='<h2><font color="#339933">Submission Record</font></h2>';       document.SCORE.submit();
   $result.='<table border="0"><tr><td><b>Username: </b>'.$uname.   }
       '</td><td><b>Fullname: </b>'.$ENV{'form.fullname'}.'</td><td><b>Domain: </b>'.$udom.'</td></tr>';  
   $result.='<tr><td colspan=3><b>Resource: </b>'.$url.'</td></tr></table>';  //===================== Show list of keywords ====================
 #    function keywords(keyform) {
 # option to display problem      var keywds = keyform.value;
   if ($ENV{'form.vProb'} eq 'yes') {      var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",keywds);
       my $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom,      if (nret==null) return;
    $ENV{'request.course.id'});      keyform.value = nret;
       my $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom,  
  $ENV{'request.course.id'});      document.SCORE.refresh.value = "on";
       $result.='<table border="0" width="100%"><tr><td bgcolor="#777777">';      if (document.SCORE.keywords.value != "") {
       $result.='<table border="0" width="100%"><tr><td bgcolor="#e6ffff">';   document.SCORE.submit();
       $result.='<b>Student\'s view of the problem</b></td></tr><tr><td bgcolor="#ffffff">'.$rendered.'<br />';  
       $result.='<b>Correct answer:</b><br />'.$companswer;  
       $result.='</td></tr></table>';  
       $result.='</td></tr></table><br />';  
   }  
   my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : '');  
   my $answer=&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,  
       $ENV{'request.course.id'},$last);  
   $result.=$answer;  
   
   my $wgt   = &Apache::lonnet::EXT('resource.partid.weight',$symb,$udom,$uname);  
   my %record= &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);  
   my $score = $record{'resource.0.awarded'}*$wgt;  
   
   $result.= '<form action="/adm/grades" method="post" name="SCORE">'."\n".  
     '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".  
       '<input type="hidden" name="url" value="'.$url.'" />'."\n".  
         '<input type="hidden" name="vProb" value="'.$ENV{'form.vProb'}.'" />'."\n".  
           '<input type="hidden" name="lastSub" value="'.$last.'" />'."\n".  
     '<input type="hidden" name="command" value="handgrade" />'."\n".  
       '<input type="hidden" name="NCT"'.  
         ' value="'.($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : '1').'" />'."\n";  
   
   my $counter = 0;  
   $result.='<input type="hidden" name="WGT'.$counter.'" value="'.$wgt.'" />'.  
       '<input type="hidden" name="unamedom'.$counter.'" value="'.$uname.':'.$udom.'" />'."\n";  
   $result.='<table border="0"><tr><td><b>Points</b></td><td>';  
   my $ctr = 0;  
   while ($ctr<=$wgt) {  
       $result.= '<input type="radio" name="RADVAL'.$counter.'" '.  
   'onclick="javascript:writeBox(this.form.GRADE_BOX'.$counter.  
     ',this.form.GRADE_SEL'.$counter.',\''.$ctr.'\',\''.$wgt.'\')" '.  
  ($score == $ctr? 'checked':'').' /> '.$ctr."\n";  
       $ctr++;  
   }  
   $result.='</td><td>&nbsp;<b>or</b>&nbsp;</td>';  
   $result.='<td><input type="text" name="GRADE_BOX'.$counter.'"'.  
       ($score ne ''? ' value = "'.$score.'"':'').' size="4" '.  
        'onChange="javascript:updateRadio(this.form.RADVAL'.$counter.  
         ',this.form.GRADE_BOX'.$counter.  
    ',this.form.GRADE_SEL'.$counter.',\''.$wgt.'\')" /></td>'."\n";  
   $result.='<td>/'.($wgt > 0 ? $wgt.' (problem weight)' : '<font color="red">invalid problem weight</font>' ).  
       ' </td><td>';  
   
   foreach my $part (&getpartlist($url)) {  
     my ($temp,$part,$type)=split(/_/,$part);  
     if ($type eq 'solved') {  
       my ($status,$foo)=split(/_/,$record{"resource.$part.$type"},2);  
       $result.='<select name="GRADE_SEL'.$counter.'">'."\n";  
       my $optsel = '<option>correct</option><option>incorrect</option>'.  
   '<option>excused</option><option>ungraded</option>'.  
       '<option>partial</option><option>nothing</option>'."\n";  
       $status = 'nothing' if ($status eq '');  
       $optsel =~ s/<option>$status/<option selected="on">$status/;  
       $result.=$optsel;  
       $result.="</select></td></tr>\n";  
     }      }
       return;
   }    }
   
   $result.='<tr><td colspan="6"><input type="submit" name="gradeOpt" value="Save & Next" />';  //===================== Script to view submitted by ==================
   my $ntstu ='<select name="NTSTU">'.    function viewSubmitter(submitter) {
       '<option>1</option><option>2</option>'.      document.SCORE.refresh.value = "on";
   '<option>3</option><option>5</option>'.      document.SCORE.NCT.value = "1";
       '<option>7</option><option>10</option></select>'."\n";      document.SCORE.unamedom0.value = submitter;
   my $nsel = ($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : '1');      document.SCORE.submit();
   $ntstu =~ s/<option>$nsel/<option selected="on">$nsel/;      return;
   $result.=$ntstu.'student(s) &nbsp;'."\n <br />";    }
   $result.='<input type="submit" name="gradeOpt" value="Next" />&nbsp';  
   $result.='<input type="submit" name="gradeOpt" value="Previous" />&nbsp';  
   $result.='(Next and Previous do not save the scores.)';  
   $result.='</td><tr></table></form>';  
   return $result;  
 }  
   
 sub processHandGrade {  
   my ($request) = @_;  
   my $result='';  
   
   my $url    = $ENV{'form.url'};  //===================== Script to add keyword(s) ==================
   my $symb   = $ENV{'form.symb'};    function getSel() {
   my $button = $ENV{'form.gradeOpt'};      if (document.getSelection) txt = document.getSelection();
   my $ngrade = $ENV{'form.NCT'};      else if (document.selection) txt = document.selection.createRange().text;
   my $ntstu  = $ENV{'form.NTSTU'};      else return;
   my $vProb  = $ENV{'form.vProb'};      var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," ");
   my $lastSub= $ENV{'form.lastSub'};      if (cleantxt=="") {
    alert("Please select a word or group of words from document and then click this link.");
   my (@parts) = sort(&getpartlist($url));   return;
   if ($button eq 'Save & Next') {      }
       my $ctr = 0;      var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt);
       while ($ctr < $ENV{'form.NCT'}) {      if (nret==null) return;
   my $pts    = ($ENV{'form.GRADE_BOX'.$ctr} ne '' ? $ENV{'form.GRADE_BOX'.$ctr} : $ENV{'form.RADVAL'.$ctr});      var curlist = document.SCORE.keywords.value;
   my $wgt    = $ENV{'form.WGT'.$ctr};      document.SCORE.keywords.value = curlist+" "+nret;
   my $sel    = $ENV{'form.GRADE_SEL'.$ctr};      document.SCORE.refresh.value = "on";
   my $score  = $pts/$wgt if ($wgt != 0);      if (document.SCORE.keywords.value != "") {
   my ($uname,$udom) = split(/:/,$ENV{'form.unamedom'.$ctr});   document.SCORE.submit();
   &saveHandGrade($url,$symb,$uname,$udom,$score,@parts);      }
   $ctr++;      return;
       }  
   }    }
   my $firststu = $ENV{'form.unamedom0'};  
   my $laststu  = $ENV{'form.unamedom'.($ngrade-1)};  
   
   #get classlist  //====================== Script for composing message ==============
   my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});    function msgCenter(msgform,usrctr,fullname) {
   my (%classlist) = &getclasslist($cdom,$cnum,'0');      var Nmsg  = msgform.savemsgN.value;
       savedMsgHeader(Nmsg,usrctr,fullname);
   my (@nextlist,@prevlist);      var subject = msgform.msgsub.value;
   my ($nextflg,$prevflg,$ctr) = (0,0,0);      var rtrchk  = eval("document.SCORE.includemsg"+usrctr);
   foreach my $student ( sort(@{ $classlist{'allids'} }) ) {      var msgchk = rtrchk.value;
       my ($uname,$udom) = split(/:/,$student);      re = /msgsub/;
       if ($nextflg == 1 && $button =~ /Next$/) {      var shwsel = "";
   push @nextlist,$uname if ($ctr < $ENV{'form.NTSTU'});      if (re.test(msgchk)) { shwsel = "checked" }
   $ctr++;      displaySubject(subject,shwsel);
       }      for (var i=1; i<=Nmsg; i++) {
       $nextflg = 1 if ($student eq $laststu);   var testpt = "savemsg"+i+",";
       $prevflg = 1 if ($student eq $firststu);   re = /testpt/;
    shwsel = "";
    if (re.test(msgchk)) { shwsel = "checked" }
    var message = eval("document.SCORE.savemsg"+i+".value");
    displaySavedMsg(i,message,shwsel);
       }
       newmsg = eval("document.SCORE.newmsg"+usrctr+".value");
       shwsel = "";
       re = /newmsg/;
       if (re.test(msgchk)) { shwsel = "checked" }
       newMsg(newmsg,shwsel);
       msgTail(); 
       return;
   }    }
   foreach my $student (@nextlist) {  
       $ENV{'form.student'} = $student;    function savedMsgHeader(Nmsg,usrctr,fullname) {
       $request->print(&submission($request));      var height = 30*Nmsg+250;
       var scrollbar = "no";
       if (height > 600) {
    height = 600;
    scrollbar = "yes";
       }
   /*    if (window.pWin)
    window.pWin.close(); */
       pWin = window.open('', 'MessageCenter', 'toolbar=no,location=no,scrollbars='+scrollbar+',screenx=70,screeny=75,width=600,height='+height);
       pWin.document.write("<html><head>");
       pWin.document.write("<title>Message Central</title>");
   
       pWin.document.write("<script language=javascript>");
       pWin.document.write("function checkInput() {");
       pWin.document.write("  opener.document.SCORE.msgsub.value = document.msgcenter.msgsub.value;");
       pWin.document.write("  var nmsg   = opener.document.SCORE.savemsgN.value;");
       pWin.document.write("  var usrctr = document.msgcenter.usrctr.value;");
       pWin.document.write("  var newval = eval(\\"opener.document.SCORE.newmsg\\"+usrctr);");
       pWin.document.write("  newval.value = document.msgcenter.newmsg.value;");
   
       pWin.document.write("  var msgchk = \\"\\";");
       pWin.document.write("  if (document.msgcenter.subchk.checked) {");
       pWin.document.write("     msgchk = \\"msgsub,\\";");
       pWin.document.write("  }");
       pWin.document.write(   "for (var i=1; i<=nmsg; i++) {");
       pWin.document.write("      var opnmsg = eval(\\"opener.document.SCORE.savemsg\\"+i);");
       pWin.document.write("      var frmmsg = eval(\\"document.msgcenter.msg\\"+i);");
       pWin.document.write("      opnmsg.value = frmmsg.value;");
       pWin.document.write("      var chkbox = eval(\\"document.msgcenter.msgn\\"+i);");
       pWin.document.write("      if (chkbox.checked) {");
       pWin.document.write("         msgchk += \\"savemsg\\"+i+\\",\\";");
       pWin.document.write("      }");
       pWin.document.write("  }");
       pWin.document.write("  if (document.msgcenter.newmsgchk.checked) {");
       pWin.document.write("     msgchk += \\"newmsg\\"+usrctr;");
       pWin.document.write("  }");
       pWin.document.write("  var includemsg = eval(\\"opener.document.SCORE.includemsg\\"+usrctr);");
       pWin.document.write("  includemsg.value = msgchk;");
   
       pWin.document.write("  self.close()");
   
       pWin.document.write("}");
   
       pWin.document.write("<");
       pWin.document.write("/script>");
   
       pWin.document.write("</head><body bgcolor=white>");
   
       pWin.document.write("<form action=\\"inactive\\" name=\\"msgcenter\\">");
       pWin.document.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">");
       pWin.document.write("<font color=\\"green\\" size=+1>&nbsp;Compose Message for \"+fullname+\"</font><br><br>");
   
       pWin.document.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
       pWin.document.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
       pWin.document.write("<td><b>Type</b></td><td><b>Include</b></td><td><b>Message</td></tr>");
   }
       function displaySubject(msg,shwsel) {
       pWin.document.write("<tr bgcolor=\\"#ffffdd\\">");
       pWin.document.write("<td>Subject</td>");
       pWin.document.write("<td align=\\"center\\"><input name=\\"subchk\\" type=\\"checkbox\\"" +shwsel+"></td>");
       pWin.document.write("<td><input name=\\"msgsub\\" type=\\"text\\" value=\\""+msg+" \\"size=\\"60\\" maxlength=\\"80\\"></td></tr>");
   }
   
   function displaySavedMsg(ctr,msg,shwsel) {
       pWin.document.write("<tr bgcolor=\\"#ffffdd\\">");
       pWin.document.write("<td align=\\"center\\">"+ctr+"</td>");
       pWin.document.write("<td align=\\"center\\"><input name=\\"msgn"+ctr+"\\" type=\\"checkbox\\"" +shwsel+"></td>");
       pWin.document.write("<td><input name=\\"msg"+ctr+"\\" type=\\"text\\" value=\\""+msg+" \\" size=\\"60\\" maxlength=\\"80\\"></td></tr>");
   }
   
     function newMsg(newmsg,shwsel) {
       pWin.document.write("<tr bgcolor=\\"#ffffdd\\">");
       pWin.document.write("<td align=\\"center\\">New</td>");
       pWin.document.write("<td align=\\"center\\"><input name=\\"newmsgchk\\" type=\\"checkbox\\"" +shwsel+"></td>");
       pWin.document.write("<td><input name=\\"newmsg\\" type=\\"text\\" onchange=\\"javascript:this.form.newmsgchk.checked=true\\" value=\\""+newmsg+" \\" size=\\"60\\" maxlength=\\"80\\"></td></tr>");
   }
   
     function msgTail() {
       pWin.document.write("</table>");
       pWin.document.write("</td></tr></table>&nbsp;");
       pWin.document.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");
       pWin.document.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br><br>");
       pWin.document.write("</form>");
       pWin.document.write("</body></html>");
   }
   
   //====================== Script for keyword highlight options ==============
     function kwhighlight() {
       var kwclr    = document.SCORE.kwclr.value;
       var kwsize   = document.SCORE.kwsize.value;
       var kwstyle  = document.SCORE.kwstyle.value;
       var redsel = "";
       var grnsel = "";
       var blusel = "";
       if (kwclr=="red")   {var redsel="checked"};
       if (kwclr=="green") {var grnsel="checked"};
       if (kwclr=="blue")  {var blusel="checked"};
       var sznsel = "";
       var sz1sel = "";
       var sz2sel = "";
       if (kwsize=="0")  {var sznsel="checked"};
       if (kwsize=="+1") {var sz1sel="checked"};
       if (kwsize=="+2") {var sz2sel="checked"};
       var synsel = "";
       var syisel = "";
       var sybsel = "";
       if (kwstyle=="")    {var synsel="checked"};
       if (kwstyle=="<i>") {var syisel="checked"};
       if (kwstyle=="<b>") {var sybsel="checked"};
       highlightCentral();
       highlightbody('red','red',redsel,'0','normal',sznsel,'','normal',synsel);
       highlightbody('green','green',grnsel,'+1','+1',sz1sel,'<i>','italic',syisel);
       highlightbody('blue','blue',blusel,'+2','+2',sz2sel,'<b>','bold',sybsel);
       highlightend();
       return;
   }    }
   $request->print ("<h2><font color=\"#339933\">Grading</font></h2>");  
   
   #get info for each student  
   foreach my $student ( sort(@{ $classlist{'allids'} }) ) {    function highlightCentral() {
     my $display=&viewstudentgrade($url,$symb,$ENV{'request.course.id'},$student,@parts);      hwdWin = window.open('', 'KeywordHighlightCentral', 'toolbar=no,location=no,scrollbars=no,width=400,height=300,screenx=100,screeny=75');
       print "ID=$ENV{'request.course.id'}:STU=$student:DIS=$display:<br>\n";      hwdWin.document.write("<html><head>");
 #    $result.=&viewstudentgrade($url,$symb,$ENV{'request.course.id'},$student,@parts);      hwdWin.document.write("<title>Highlight Central</title>");
   
       hwdWin.document.write("<script language=javascript>");
       hwdWin.document.write("function updateChoice(flag) {");
       hwdWin.document.write("  opener.document.SCORE.kwclr.value = radioSelection(document.hlCenter.kwdclr);");
       hwdWin.document.write("  opener.document.SCORE.kwsize.value = radioSelection(document.hlCenter.kwdsize);");
       hwdWin.document.write("  opener.document.SCORE.kwstyle.value = radioSelection(document.hlCenter.kwdstyle);");
       hwdWin.document.write("  opener.document.SCORE.refresh.value = \\"on\\";");
       hwdWin.document.write("  if (opener.document.SCORE.keywords.value!=\\"\\"){");
       hwdWin.document.write("     opener.document.SCORE.submit();");
       hwdWin.document.write("  }");
       hwdWin.document.write("  self.close()");
       hwdWin.document.write("}");
   
       hwdWin.document.write("function radioSelection(radioButton) {");
       hwdWin.document.write("    var selection=null;");
       hwdWin.document.write("    for (var i=0; i<radioButton.length; i++) {");
       hwdWin.document.write("        if (radioButton[i].checked) {");
       hwdWin.document.write("            selection=radioButton[i].value;");
       hwdWin.document.write("            return selection;");
       hwdWin.document.write("        }");
       hwdWin.document.write("    }");
       hwdWin.document.write("}");
   
       hwdWin.document.write("<");
       hwdWin.document.write("/script>");
   
       hwdWin.document.write("</head><body bgcolor=white>");
   
       hwdWin.document.write("<form action=\\"inactive\\" name=\\"hlCenter\\">");
       hwdWin.document.write("<font color=\\"green\\" size=+1>&nbsp;Keyword Highlight Options</font><br><br>");
   
       hwdWin.document.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
       hwdWin.document.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
       hwdWin.document.write("<td><b>Text Color</b></td><td><b>Font Size</b></td><td><b>Font Style</td></tr>");
     }
   
     function highlightbody(clrval,clrtxt,clrsel,szval,sztxt,szsel,syval,sytxt,sysel) { 
       hwdWin.document.write("<tr bgcolor=\\"#ffffdd\\">");
       hwdWin.document.write("<td align=\\"left\\">");
       hwdWin.document.write("<input name=\\"kwdclr\\" type=\\"radio\\" value=\\""+clrval+"\\" "+clrsel+">&nbsp;"+clrtxt+"</td>");
       hwdWin.document.write("<td align=\\"left\\">");
       hwdWin.document.write("<input name=\\"kwdsize\\" type=\\"radio\\" value=\\""+szval+"\\" "+szsel+">&nbsp;"+sztxt+"</td>");
       hwdWin.document.write("<td align=\\"left\\">");
       hwdWin.document.write("<input name=\\"kwdstyle\\" type=\\"radio\\" value=\\""+syval+"\\" "+sysel+">&nbsp;"+sytxt+"</td>");
       hwdWin.document.write("</tr>");
     }
   
     function highlightend() { 
       hwdWin.document.write("</table>");
       hwdWin.document.write("</td></tr></table>&nbsp;");
   //    hwdWin.document.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:updateChoice(0)\\">&nbsp;&nbsp;");
       hwdWin.document.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:updateChoice(1)\\">&nbsp;&nbsp;");
       hwdWin.document.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br><br>");
       hwdWin.document.write("</form>");
       hwdWin.document.write("</body></html>");
   }    }
   
   return 'The End';  </script>
 #  return $result;  SUBJAVASCRIPT
 }  }
   
 sub saveHandGrade {  
   my ($url,$symb,$stuname,$domain,$newscore,@parts) = @_;  
   
 #  my ($stuname,$domain) = split(/:/,$student);  # --------------------------- show submissions of a student, option to grade 
   my %record=&Apache::lonnet::restore($symb,$ENV{'request.course.id'},$domain,$stuname);  sub submission {
   my %newrecord;      my ($request,$counter,$total) = @_;
   
   foreach my $part (@parts) {      (my $url=$ENV{'form.url'})=~s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
     my ($temp,$part,$type)=split(/_/,$part);  #    if ($ENV{'form.student'} eq '') { &moreinfo($request,'Need student login id'); return ''; }
     my $oldscore=$record{"resource.$part.$type"};      my ($uname,$udom)     = ($ENV{'form.student'},$ENV{'form.userdom'});
     if ($type eq 'solved') {      ($uname,$udom)        = &finduser($uname) if $udom eq '';
       my $update=0;      $ENV{'form.fullname'} = &get_fullname ($uname,$udom) if $ENV{'form.fullname'} eq '';
       if ($newscore eq 'nothing' ) {  #    if ($uname eq '') { &moreinfo($request,'Unable to find student'); return ''; }
  if ($oldscore ne '') {  
   $update=1;      my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));
   $newscore = '';      if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
  }      my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : '');
       } elsif ($oldscore !~ m/^$newscore/) {      $ENV{'form.vProb'} = $ENV{'form.vProb'} ne '' ? $ENV{'form.vProb'} : 'yes';
  $update=1;      my ($classlist,$seclist,$ids,$stusec,$fullname);
  if ($newscore eq 'correct')   { $newscore = 'correct_by_override'; }  
  if ($newscore eq 'incorrect') { $newscore = 'incorrect_by_override'; }      # header info
  if ($newscore eq 'excused')   { $newscore = 'excused'; }      if ($counter == 0) {
  if ($newscore eq 'ungraded')  { $newscore = 'ungraded_attempted'; }   &sub_page_js($request);
  if ($newscore eq 'partial')   { $newscore = 'partial_correct'; }   $request->print('<h3>&nbsp;<font color="#339933">Submission Record</font></h3>'."\n".
       }   '<font size=+1>&nbsp;<b>Resource: </b>'.$url.'</font>'."\n");
       if ($update) { $newrecord{"resource.$part.$type"}=$newscore; }  
    # option to display problem, only once else it cause problems 
           # with the form later since the problem has a form.
    if ($ENV{'form.vProb'} eq 'yes') {
       my $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom,
         $ENV{'request.course.id'});
       my $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom,
      $ENV{'request.course.id'});
       my $result.='<table border="0" width="100%"><tr><td bgcolor="#777777">';
       $result.='<table border="0" width="100%"><tr><td bgcolor="#e6ffff">';
       $result.='<b> View of the problem - '.$ENV{'form.fullname'}.
    '</b></td></tr><tr><td bgcolor="#ffffff">'.$rendered.'<br />';
       $result.='<b>Correct answer:</b><br />'.$companswer;
       $result.='</td></tr></table>';
       $result.='</td></tr></table><br />';
       $request->print($result);
    }
   
    # kwclr is the only variable that is guaranteed to be non blank 
           # if this subroutine has been called once.
    my %keyhash = ();
    if ($ENV{'form.kwclr'} eq '') {
       %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.msgsub'}   = $keyhash{$symb.'_subject'} ne '' ? 
    $keyhash{$symb.'_subject'} : &Apache::lonnet::metadata($url,'title');
       $ENV{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0';
   
    }
   
    $request->print('<form action="/adm/grades" method="post" name="SCORE">'."\n".
    '<input type="hidden" name="command"    value="handgrade" />'."\n".
    '<input type="hidden" name="refresh"    value="off" />'."\n".
    '<input type="hidden" name="symb"       value="'.$symb.'" />'."\n".
    '<input type="hidden" name="url"        value="'.$url.'" />'."\n".
    '<input type="hidden" name="showgrading" value="'.$ENV{'form.showgrading'}.'" />'."\n".
    '<input type="hidden" name="vProb"      value="'.$ENV{'form.vProb'}.'" />'."\n".
    '<input type="hidden" name="lastSub"    value="'.$ENV{'form.lastSub'}.'" />'."\n".
    '<input type="hidden" name="section"    value="'.$ENV{'form.section'}.'">'."\n".
    '<input type="hidden" name="submitonly" value="'.$ENV{'form.submitonly'}.'">'."\n".
    '<input type="hidden" name="response"   value="'.$ENV{'form.response'}.'">'."\n".
    '<input type="hidden" name="handgrade"  value="'.$ENV{'form.handgrade'}.'">'."\n".
    '<input type="hidden" name="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"'.
    ' value="'.($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : $total+1).'" />'."\n");
   
    my ($cts,$prnmsg) = (1,'');
    while ($cts <= $ENV{'form.savemsgN'}) {
       $prnmsg.='<input type="hidden" name="savemsg'.$cts.'" value="'.
    ($keyhash{$symb.'_savemsg'.$cts} eq '' ? $ENV{'form.savemsg'.$cts} : $keyhash{$symb.'_savemsg'.$cts}).
    '" />'."\n";
       $cts++;
    }
    $request->print($prnmsg);
   
    if ($ENV{'form.handgrade'} eq 'yes' && $ENV{'form.showgrading'} eq 'yes') {
       $request->print(<<KEYWORDS);
   &nbsp;<b>Keyword Options:</b>&nbsp;
   <a href="javascript:keywords(document.SCORE.keywords)"; TARGET=_self>List</a>&nbsp; &nbsp;
   <a href="#" onMouseDown="javascript:getSel(); return false"
    CLASS="page">Paste Selection to List</a>&nbsp; &nbsp;
   <a href="javascript:kwhighlight()"; TARGET=_self>Highlight Attribute</a><br /><br />
   KEYWORDS
           }
       }
   
       my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
       my ($partlist,$handgrade) = &response_type($url);
   
       # Display student info
       $request->print(($counter == 0 ? '' : '<br />'));
       my $result='<table border="0" width=100%><tr><td bgcolor="#777777">'."\n".
    '<table border="0" width=100%><tr bgcolor="#edffff"><td>'."\n";
   
       $result.='<b>Fullname: </b>'.$ENV{'form.fullname'}.
    '<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.
    '" value="'.$ENV{'form.fullname'}.'" />'."\n";
   
       # If this is handgraded, then check for collaborators
       my @col_fullnames;
       if ($ENV{'form.handgrade'} eq 'yes') {
    my @col_list;
    ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist('all','0');
    for (keys (%$handgrade)) {
       my $ncol = &Apache::lonnet::EXT('resource.'.$_.
       '.maxcollaborators',$symb,$udom,$uname);
       if ($ncol > 0) {
    s/\_/\./g;
    if ($record{'resource.'.$_.'.collaborators'} ne '') {
       my (@collaborators) = split(/,?\s+/,
    $record{'resource.'.$_.'.collaborators'});
       my (@badcollaborators);
       if (scalar(@collaborators) != 0) {
    $result.='<b>Collaborators: </b>';
    foreach my $collaborator (@collaborators) {
       $collaborator = $collaborator =~ /\@|:/ ? 
    (split(/@|:/,$collaborator))[0] : $collaborator;
       next if ($collaborator eq $uname);
       if (!grep /^$collaborator:/i,keys %$classlist) {
    push @badcollaborators,$collaborator;
    next;
       }
       push @col_list, $collaborator;
       my ($lastname,$givenn) = split(/,/,$$fullname{$collaborator.':'.$udom});
       push @col_fullnames, $givenn.' '.$lastname;
       $result.=$$fullname{$collaborator.':'.$udom}.'&nbsp; &nbsp; &nbsp;';
    }
    $result.='<br />'."\n";
    $result.='<table border="0"><tr bgcolor="#ffbbbb"><td>'.
       'This student has submitted '.
       (scalar (@badcollaborators) > 1 ? '' : 'an').
       ' invalid collaborator'.(scalar (@badcollaborators) > 1 ? 's. ' : '. ').
       (join ', ',@badcollaborators).'</td></tr></table>' 
       if (scalar(@badcollaborators) > 0);
   
    $result.='<table border="0"><tr bgcolor="#ffbbbb"><td>'.
       'This student has submitted too many collaborators. Maximum is '.
       $ncol.'.</td></tr></table>' if (scalar(@collaborators) > $ncol);
    $result.='<input type="hidden" name="collaborator'.$counter.
       '" value="'.(join ':',@col_list).'" />'."\n";
       }
    }
       }
    }
       }
       $request->print($result."\n");
   
       # print student answer/submission
       # Options are (1) Handgaded submission only
       #             (2) Last submission, includes submission that is not handgraded 
       #                  (for multi-response type part)
       #             (3) Last submission plus the parts info
       #             (4) The whole record for this student
       if ($ENV{'form.lastSub'} =~ /^(lastonly|hdgrade)$/) {
    if ($ENV{'form.'.$uname.':'.$udom.':submitted_by'}) {
       my $submitby=''.
    '<b>Collaborative submission by: </b>'.
    '<a href="javascript:viewSubmitter(\''.
    $ENV{'form.'.$uname.':'.$udom.':submitted_by'}.
    '\')"; TARGET=_self>'.
    $$fullname{$ENV{'form.'.$uname.':'.$udom.':submitted_by'}}.'</a>';
       $request->print($submitby);
    } else {
       my ($string,$timestamp)=
    &get_last_submission (%record);
       my $lastsubonly.=''.
    ($$timestamp eq '' ? '' : '<b>Date Submitted:</b> '.
    $$timestamp).'';
       if ($$timestamp eq '') {
    $lastsubonly.='<tr><td bgcolor="#ffffe6">'.$$string[0].'</td></tr>'."\n";
       } else {
    for my $part (sort keys(%$handgrade)) {
       foreach (@$string) {
    my ($partid,$respid) = /^resource\.(\d+)\.(\d+)\.submission/;
    if ($part eq ($partid.'_'.$respid)) {
       my ($ressub,$subval) = split(/:/,$_,2);
       $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part '.
    $partid.'</b> <font color="#999999">( ID '.$respid.
    ' )</font>&nbsp; &nbsp;<b>Answer: </b>'.
    &keywords_highlight($subval).'</td></tr>'."\n"
    if ($ENV{'form.lastSub'} eq 'lastonly' || 
       ($ENV{'form.lastSub'} eq 'hdgrade' && 
        $$handgrade{$part} =~ /:yes$/));
    }
       }
    }
       }
       $lastsubonly.='</td></tr>'."\n";
       $request->print($lastsubonly);
    }
     } else {      } else {
       if ($oldscore ne $newscore) {   $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,
  $newrecord{"resource.$part.$type"}=$newscore;   $ENV{'request.course.id'},
       }   $last,'.submission',
    'Apache::grades::keywords_highlight'));
       }
       
       # return if view submission with no grading option
       if ($ENV{'form.showgrading'} eq '') {
    $request->print('</td></tr></table></td></tr></table></form>'."\n");
    return;
     }      }
     if ( scalar(keys(%newrecord)) > 0 ) {  
       $newrecord{'resource.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";      # Grading options
 #      print "grader=$newrecord{'resource.regrader'}:<br>records<br>";      $result='<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n".
 #      while (my ($k,$v) = each %newrecord) {   '<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".
 # print "k=$k:v=$v:<br>\n";   '<input type="hidden" name="unamedom'.$counter.'" value="'.$uname.':'
 #      }   .$udom.'" />'."\n";
 #     &Apache::lonnet::cstore(\%newrecord,$symb,$courseid,$domain,$stuname);      my ($lastname,$givenn) = split(/,/,$ENV{'form.fullname'});
       my $msgfor = $givenn.' '.$lastname;
       if (scalar(@col_fullnames) > 0) {
    my $lastone = pop @col_fullnames;
    $msgfor .= ', '.(join ', ',@col_fullnames).' and '.$lastone.'.';
       }
       $result.='<tr><td bgcolor="#ffffff">'."\n".
    '&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.
    ',\''.$msgfor.'\')"; TARGET=_self>'.
    'Compose Message to student'.(scalar(@col_fullnames) >= 1 ? 's' : '').'</a>'.
    '<br />&nbsp;(Message will be sent when you click on Save & Next below.)'."\n" 
    if ($ENV{'form.handgrade'} eq 'yes');
       $request->print($result);
   
       my %seen = ();
       my @partlist;
       for (sort keys(%$handgrade)) {
    my ($partid,$respid) = split(/_/);
    next if ($seen{$partid} > 0);
    $seen{$partid}++;
    next if ($$handgrade{$_} =~ /:no$/);
    push @partlist,$partid;
    my $wgt    = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb,$udom,$uname);
    my $wgtmsg = ($wgt > 0 ? '(problem weight)' : 
         '<font color="red">problem weight assigned by computer</font>');
    $wgt       = ($wgt > 0 ? $wgt : '1');
    my $score  = ($record{'resource.'.$partid.'.awarded'} eq '' ?
         '' : $record{'resource.'.$partid.'.awarded'}*$wgt);
    $result='<input type="hidden" name="WGT'.$counter.'_'.$partid.'" value="'.$wgt.'" />';
    $result.='<table border="0"><tr><td><b>Part </b>'.$partid.' <b>Points: </b></td><td>';
   
    my $ctr = 0;
    $result.='<table border="0"><tr>';  # display radio buttons in a nice table 10 across
    while ($ctr<=$wgt) {
       $result.= '<td><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '.
    'onclick="javascript:writeBox(this.form.GD_BOX'.$counter.'_'.$partid.
    ',this.form.GD_SEL'.$counter.'_'.$partid.','.$ctr.
    ',this.form.stores'.$counter.'_'.$partid.')" '.
    ($score eq $ctr ? 'checked':'').' /> '.$ctr."</td>\n";
       $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
       $ctr++;
    }
    $result.='</tr></table>';
   
    $result.='</td><td>&nbsp;<b>or</b>&nbsp;</td>';
    $result.='<td><input type="text" name="GD_BOX'.$counter.'_'.$partid.'"'.
       ($score ne ''? ' value = "'.$score.'"':'').' size="4" '.
       'onChange="javascript:updateRadio(this.form.RADVAL'.$counter.'_'.$partid.
       ',this.form.GD_BOX'.$counter.'_'.$partid.
       ',this.form.GD_SEL'.$counter.'_'.$partid.
       ',this.form.stores'.$counter.'_'.$partid.
       ','.$wgt.')" /></td>'."\n";
    $result.='<td>/'.$wgt.' '.$wgtmsg.' </td><td>';
   
    $result.='<select name="GD_SEL'.$counter.'_'.$partid.'" '.
       'onChange="javascript:clearRadBox(this.form.RADVAL'.$counter.'_'.$partid.
       ',this.form.GD_BOX'.$counter.'_'.$partid.
       ',this.form.GD_SEL'.$counter.'_'.$partid.
       ',this.form.stores'.$counter.'_'.$partid.')" />'."\n".
       '<option selected="on"> </option>'.
       '<option>excused</option></select>'."&nbsp&nbsp\n";
    $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="0" />';
    $result.='</td></tr></table>'."\n";
    $request->print($result);
       }
       $result='<input type="hidden" name="partlist'.$counter.
    '" value="'.(join ":",@partlist).'" />'."\n";
       my $ctr = 0;
       while ($ctr < scalar(@partlist)) {
    $result.='<input type="hidden" name="partid'.$counter.'_'.$ctr.'" value="'.
       $partlist[$ctr].'" />'."\n";
    $ctr++;
       }
       $request->print($result.'</td></tr></table></td></tr></table>'."\n");
   
       # print end of form
       if ($counter == $total) {
    my $endform='<table border="0"><tr><td>'.
       '<input type="hidden" name="gradeOpt" value="" />'."\n";
    if ($ENV{'form.handgrade'} eq 'yes') {
       $endform.='<input type="button" value="Save & Next" '.
    'onClick="javascript:checksubmit(\'Save & Next\','.
    $total.','.scalar(@partlist).');" TARGET=_self> &nbsp;'."\n";
       my $ntstu ='<select name="NTSTU">'.
    '<option>1</option><option>2</option>'.
    '<option>3</option><option>5</option>'.
    '<option>7</option><option>10</option></select>'."\n";
       my $nsel = ($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : '1');
       $ntstu =~ s/<option>$nsel</<option selected="on">$nsel</;
       $endform.=$ntstu.'student(s) &nbsp;&nbsp;';
    } else {
       $endform.='<input type="hidden" name="NTSTU" value="1" />'."\n";
    }
    $endform.='<input type="button" value="Next" '.
       'onClick="javascript:checksubmit(\'Next\');" TARGET=_self> &nbsp;'."\n".
       '<input type="button" value="Previous" '.
       'onClick="javascript:checksubmit(\'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>';
    $request->print($endform);
     }      }
     return '';      return '';
   }  
 }  }
   
 sub get_symb_and_url {  #--- Retrieve the last submission for all the parts
  my ($request) = @_;  sub get_last_submission {
   my $url=$ENV{'form.url'};      my (%returnhash)=@_;
   $url=~s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;      my (@string,$timestamp);
 #  my $symb=$ENV{'form.symb'};      if ($returnhash{'version'}) {
 #  if (!$symb) { $symb=&Apache::lonnet::symbread($url); }   my %lasthash=();
   my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));   my ($version);
   if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }   for ($version=1;$version<=$returnhash{'version'};$version++) {
  return ($symb,$url);      foreach (sort(split(/\:/,$returnhash{$version.':keys'}))) {
    $lasthash{$_}=$returnhash{$version.':'.$_};
    if ($returnhash{$version.':'.$_} =~ /(SUBMITTED|DRAFT)$/) {
      $timestamp = scalar(localtime($returnhash{$version.':timestamp'}));
          } 
       }
    }
    foreach ((keys %lasthash)) {
       if ($_ =~ /\.submission$/) {
    my ($partid,$foo) = split(/submission$/,$_);
    my $draft  = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ?
       '<font color="red">Draft Copy</font> ' : '';
    push @string, (join(':',$_,$draft.$lasthash{$_}));
       }
    }
       }
       @string = $string[0] eq '' ? 'Nothing submitted - no attempts.' : @string;
       return \@string,\$timestamp;
 }  }
   
 sub view_edit_entire_class_form {  #--- High light keywords, with style choosen by user.
   my ($symb,$url)=@_;  sub keywords_highlight {
   my $result.='<form action="/adm/grades" method="post">'."\n".      my $string    = shift;
     '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".      my $size      = $ENV{'form.kwsize'} eq '0' ? '' : 'size='.$ENV{'form.kwsize'};
       '<input type="hidden" name="url" value="'.$url.'" />'."\n".      my $styleon   = $ENV{'form.kwstyle'} eq ''  ? '' : $ENV{'form.kwstyle'};
  '<input type="hidden" name="command" value="viewgrades" />'."\n".      (my $styleoff = $styleon) =~ s/\</\<\//;
   '<input type="submit" name="submit" value="View/Grade Entire Class" />'."\n".      my @keylist   = split(/[,\s+]/,$ENV{'form.keywords'});
     '</form>'."\n";      foreach (@keylist) {
   return $result;   $string =~ s/\b$_(\b|\.)/\<font color\=$ENV{'form.kwclr'} $size\>$styleon$_$styleoff\<\/font\>/gi;
       }
       return $string;
 }  }
   
 sub show_grading_menu_form {  #--- Called from submission routine
   my ($symb,$url)=@_;  sub processHandGrade {
   my $result.='<form action="/adm/grades" method="post">'."\n".      my ($request) = shift;
     '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".      my $url    = $ENV{'form.url'};
       '<input type="hidden" name="url" value="'.$url.'" />'."\n".      my $symb   = $ENV{'form.symb'};
  '<input type="hidden" name="command" value="gradingmenu" />'."\n".      my $button = $ENV{'form.gradeOpt'};
   '<input type="submit" name="submit" value="Grading Menu" />'."\n".      my $ngrade = $ENV{'form.NCT'};
     '</form>'."\n";      my $ntstu  = $ENV{'form.NTSTU'};
   return $result;  
       if ($button eq 'Save & Next') {
    my $ctr = 0;
    while ($ctr < $ngrade) {
       my ($uname,$udom) = split(/:/,$ENV{'form.unamedom'.$ctr});
       my ($errorflag) = &saveHandGrade($request,$url,$symb,$uname,$udom,$ctr);
   
       my $includemsg = $ENV{'form.includemsg'.$ctr};
       my ($subject,$message,$msgstatus) = ('','','');
       if ($includemsg =~ /savemsg|new$ctr/) {
    $subject = $ENV{'form.msgsub'} if ($includemsg =~ /^msgsub/);
    my (@msgnum) = split(/,/,$includemsg);
    foreach (@msgnum) {
       $message.=$ENV{'form.'.$_} if ($_ =~ /savemsg|newmsg/ && $_ ne '');
    }
    $message =~ s/\s+/ /g;
    $msgstatus = &Apache::lonmsg::user_normal_msg ($uname,$udom,
          $ENV{'form.msgsub'},$message);
       }
       if ($ENV{'form.collaborator'.$ctr}) {
    my (@collaborators) = split(/:/,$ENV{'form.collaborator'.$ctr});
    foreach (@collaborators) {
       &saveHandGrade($request,$url,$symb,$_,$udom,$ctr,
      $ENV{'form.unamedom'.$ctr});
       if ($message ne '') {
    $msgstatus = &Apache::lonmsg::user_normal_msg ($_,$udom,
          $ENV{'form.msgsub'},
          $message);
       }
    }
       }
       $ctr++;
    }
       }
   
       # Keywords sorted in alphabatical order
       my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'};
       my %keyhash = ();
       $ENV{'form.keywords'}           =~ s/,\s{0,}|\s+/ /g;
       $ENV{'form.keywords'}           =~ s/^\s+|\s+$//;
       my (@keywords) = sort(split(/\s+/,$ENV{'form.keywords'}));
       $ENV{'form.keywords'} = join(' ',@keywords);
       $keyhash{$symb.'_keywords'}     = $ENV{'form.keywords'};
       $keyhash{$symb.'_subject'}      = $ENV{'form.msgsub'};
       $keyhash{$loginuser.'_kwclr'}   = $ENV{'form.kwclr'};
       $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.
       # All messages are saved in nohist_handgrade.db
       my ($ctr,$idx) = (1,1);
       while ($ctr <= $ENV{'form.savemsgN'}) {
    if ($ENV{'form.savemsg'.$ctr} ne '') {
       $keyhash{$symb.'_savemsg'.$idx} = $ENV{'form.savemsg'.$ctr};
       $idx++;
    }
    $ctr++;
       }
       $ctr = 0;
       while ($ctr < $ngrade) {
    if ($ENV{'form.newmsg'.$ctr} ne '') {
       $keyhash{$symb.'_savemsg'.$idx} = $ENV{'form.newmsg'.$ctr};
       $ENV{'form.savemsg'.$idx} = $ENV{'form.newmsg'.$ctr};
       $idx++;
    }
    $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'});
   
       # Called by Save & Refresh from Highlight Attribute Window
       if ($ENV{'form.refresh'} eq 'on') {
    my $ctr = 0;
    $ENV{'form.NTSTU'}=$ngrade;
    while ($ctr < $ngrade) {
       ($ENV{'form.student'},$ENV{'form.userdom'}) = split(/:/,$ENV{'form.unamedom'.$ctr});
       &submission($request,$ctr,$ngrade-1);
       $ctr++;
    }
    return '';
       }
   
       # Get the next/previous one or group of students
       my $firststu = $ENV{'form.unamedom0'};
       my $laststu = $ENV{'form.unamedom'.($ngrade-1)};
       $ctr = 2;
       while ($laststu eq '') {
    $laststu  = $ENV{'form.unamedom'.($ngrade-$ctr)};
    $ctr++;
    $laststu = $firststu if ($ctr > $ngrade);
       }
   
       my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist($ENV{'form.section'},'0');
       my (@parsedlist,@nextlist);
       my ($nextflg) = 0;
       foreach (sort {$$fullname{$a} cmp $$fullname{$b} } keys %$fullname) {
    if ($nextflg == 1 && $button =~ /Next$/) {
       push @parsedlist,$_;
    }
    $nextflg = 1 if ($_ eq $laststu);
    if ($button eq 'Previous') {
       last if ($_ eq $firststu);
       push @parsedlist,$_;
    }
       }
       $ctr = 0;
       my ($partlist,$handgrade) = &response_type($ENV{'form.url'});
       @parsedlist = reverse @parsedlist if ($button eq 'Previous');
       foreach my $student (@parsedlist) {
    my ($uname,$udom) = split(/:/,$student);
    if ($ENV{'form.submitonly'} eq 'yes') {
       my (%status) = &student_gradeStatus($ENV{'form.url'},$symb,$udom,$uname,$partlist) ;
       my $statusflg = '';
       foreach (keys(%status)) {
    $statusflg = 1 if ($status{$_} ne 'nothing');
    my ($foo,$partid,$foo1) = split(/\./);
    $statusflg = '' if ($status{'resource.'.$partid.'.submitted_by'} ne '');
       }
       next if ($statusflg eq '');
    }
    push @nextlist,$student if ($ctr < $ntstu);
    $ctr++;
       }
   
       $ctr = 0;
       my $total = scalar(@nextlist)-1;
   
       foreach (sort @nextlist) {
    my ($uname,$udom,$submitter) = split(/:/);
    $ENV{'form.student'}  = $uname;
    $ENV{'form.userdom'}  = $udom;
    $ENV{'form.fullname'} = $$fullname{$_};
   # $ENV{'form.'.$_.':submitted_by'} = $submitter;
   # print "submitter=$ENV{'form.'.$_.':submitted_by'}= $submitter:<br>";
    &submission($request,$ctr,$total);
    $ctr++;
       }
       if ($total < 0) {
    my $the_end = '<h3><font color="red">LON-CAPA User Message</font></h3><br />'."\n";
    $the_end.='<b>Message: </b> No more students for this section or class.<br /><br />'."\n";
    $the_end.='Click on the button below to return to the grading menu.<br /><br />'."\n";
    $the_end.=&show_grading_menu_form ($symb,$url);
    $request->print($the_end);
       }
       return '';
 }  }
   
 sub gradingmenu {  #---- Save the score and award for each student, if changed
   my ($request) = @_;  sub saveHandGrade {
   my ($symb,$url)=&get_symb_and_url($request);      my ($request,$url,$symb,$stuname,$domain,$newflg,$submitter) = @_;
   if (!$symb) {return '';}      my %record=&Apache::lonnet::restore($symb,$ENV{'request.course.id'},$domain,$stuname);
       my %newrecord;
   my $result='<h2>&nbsp;<font color="#339933">Select a Grading Method</font></h2><br />';      foreach (split(/:/,$ENV{'form.partlist'.$newflg})) {
   $result.='<table width=100% border=0><tr><td bgcolor=#777777>'."\n";   if ($ENV{'form.GD_SEL'.$newflg.'_'.$_} eq 'excused') {
   $result.='<table width=100% border=0><tr><td bgcolor=#e6ffff>'."\n";      $newrecord{'resource.'.$_.'.solved'} = 'excused' 
   $result.='&nbsp;<b>Resource :</b> '.$url.'</td></tr>'."\n";   if ($record{'resource.'.$_.'.solved'} ne 'excused');
   $result.='<tr bgcolor=#ffffe6><td>'."\n";   } else {
   $result.=&view_edit_entire_class_form($symb,$url);      my $pts = ($ENV{'form.GD_BOX'.$newflg.'_'.$_} ne '' ? 
   $result.='<form action="/adm/grades" method="post">'."\n".         $ENV{'form.GD_BOX'.$newflg.'_'.$_} : 
      '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".         $ENV{'form.RADVAL'.$newflg.'_'.$_});
       '<input type="hidden" name="url" value="'.$url.'" />'."\n".      my $wgt = $ENV{'form.WGT'.$newflg.'_'.$_} eq '' ? 1 : 
  '<input type="hidden" name="command" value="csvupload" />'."\n".   $ENV{'form.WGT'.$newflg.'_'.$_};
   '<input type="submit" name="submit" value="Upload Scores" />'."\n".      my $partial= $pts/$wgt;
     '</form>'."\n";      $newrecord{'resource.'.$_.'.awarded'}  = $partial 
   $result.='<form action="/adm/grades" method="post">'."\n".   if ($record{'resource.'.$_.'.awarded'} ne $partial);
      '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".      my $reckey = 'resource.'.$_.'.solved';
       '<input type="hidden" name="url" value="'.$url.'" />'."\n".      if ($partial == 0) {
  '<input type="hidden" name="command" value="submission" />'."\n".   $newrecord{$reckey} = 'incorrect_by_override' 
   '<input type="submit" name="submit" value="View/Grade A Student" />'."\n".      if ($record{$reckey} ne 'incorrect_by_override');
     '</form>'."\n";      } else {
   $result.='<form action="/adm/grades" method="post">'."\n".   $newrecord{$reckey} = 'correct_by_override' 
      '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".      if ($record{$reckey} ne 'correct_by_override');
       '<input type="hidden" name="url" value="'.$url.'" />'."\n".      }
  '<input type="hidden" name="command" value="receiptInput" />'."\n".      $newrecord{'resource.'.$_.'.submitted_by'} = $submitter 
   '<input type="submit" name="submit" value="Verify Receipt" />'."\n".   if ($submitter && ($record{'resource.'.$_.'.submitted_by'} ne $submitter));
     '</form>'."\n";   }
   $result.='</td></tr></table>'."\n";      }
   $result.='</td></tr></table>'."\n";  
   return $result;      if (scalar(keys(%newrecord)) > 0) {
    $newrecord{'resource.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
    &Apache::lonnet::cstore(\%newrecord,$symb,
    $ENV{'request.course.id'},$domain,$stuname);
       }
       return '';
 }  }
   
 sub viewgrades {  #--------------------------------------------------------------------------------------
   my ($request) = @_;  #
   my $result='';  #-------------------------- Next few routines handles grading by section or whole class
   #
   #--- Javascript to handle grading by section or whole class
   sub viewgrades_js {
       my ($request) = shift;
   
   #get resource reference      $request->print(<<VIEWJAVASCRIPT);
   my ($symb,$url)=&get_symb_and_url($request);  <script type="text/javascript" language="javascript">
   if (!$symb) {return '';}     function writePoint(partid,weight,point) {
   #get classlist   var radioButton = eval("document.classgrade.RADVAL_"+partid);
   my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});   var textbox = eval("document.classgrade.TEXTVAL_"+partid);
   #print "Found $cdom:$cnum<br />";   if (point == "textval") {
   my (%classlist) = &getclasslist($cdom,$cnum,'0');      var point = eval("document.classgrade.TEXTVAL_"+partid+".value");
   my $headerclr = '"#ccffff"';      if (isNaN(point) || point < 0) {
   my $cellclr = '"#ffffcc"';   alert("A number equal or greater than 0 is expected. Entered value = "+point);
    var resetbox = false;
   #get list of parts for this problem   for (var i=0; i<radioButton.length; i++) {
   my (@parts) = sort(&getpartlist($url));      if (radioButton[i].checked) {
    textbox.value = i;
   $request->print ("<h2><font color=\"#339933\">Manual Grading</font></h2>");   resetbox = true;
       }
   #start the form   }
   $result = '<form action="/adm/grades" method="post">'."\n".   if (!resetbox) {
     '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".      textbox.value = "";
       '<input type="hidden" name="url" value="'.$url.'" />'."\n".   }
  '<input type="hidden" name="command" value="editgrades" />'."\n".   return;
   '<input type="submit" name="submit" value="Submit Changes" />'."\n".      }
     '<table border=0><tr><td bgcolor="#777777">'."\n".      if (point > weight) {
      '<table border=0>'."\n".   var resp = confirm("You entered a value ("+point+
       '<tr bgcolor='.$headerclr.'><td><b>Username</b></td><td><b>Name</b></td><td><b>Domain</b></td>'."\n";     ") greater than the weight for the part. Accept?");
   foreach my $part (@parts) {   if (resp == false) {
      my $display=&Apache::lonnet::metadata($url,$part.'.display');      textbox.value = "";
      if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }      return;
       print "Manual grading:$url:$part:$display:<br>";   }
      $result.='<td><b>'.$display.'</b></td>'."\n";      }
   }      for (var i=0; i<radioButton.length; i++) {
   $result.='</tr>';   radioButton[i].checked=false;
   #get info for each student   if (point == i) {
   foreach my $student ( sort(@{ $classlist{'allids'} }) ) {      radioButton[i].checked=true;
     my $display=&viewstudentgrade($url,$symb,$ENV{'request.course.id'},$student,@parts);   }
 #      print "ID=$ENV{'request.course.id'}:STU=$student:DIS=$display:<br>\n";      }
     $result.=&viewstudentgrade($url,$symb,$ENV{'request.course.id'},$student,@parts);  
   }   } else {
   $result.='</table></td></tr></table>';      textbox.value = point;
   $result.='<input type="submit" name="submit" value="Submit Changes" /></form>';   }
   $result.=&show_grading_menu_form($symb,$url);   for (i=0;i<document.classgrade.total.value;i++) {
   return $result;      var user = eval("document.classgrade.ctr"+i+".value");
       var scorename = eval("document.classgrade.GD_"+user+
    "_"+partid+"_aw");
       var saveval   = eval("document.classgrade.GD_"+user+
    "_"+partid+"_sv_s.value");
       var selname   = eval("document.classgrade.GD_"+user+"_"+partid+"_sv");
       if (saveval != "correct") {
    scorename.value = point;
    if (selname[0].selected != true) {
       selname[0].selected = true;
    }
       }
    }
    var selval   = eval("document.classgrade.SELVAL_"+partid);
    selval[0].selected = true;
       }
   
       function writeRadText(partid,weight) {
    var selval   = eval("document.classgrade.SELVAL_"+partid);
    var radioButton = eval("document.classgrade.RADVAL_"+partid);
    var textbox = eval("document.classgrade.TEXTVAL_"+partid);
    if (selval[1].selected) {
       for (var i=0; i<radioButton.length; i++) {
    radioButton[i].checked=false;
   
       }
       textbox.value = "";
   
       for (i=0;i<document.classgrade.total.value;i++) {
    var user = eval("document.classgrade.ctr"+i+".value");
    var scorename = eval("document.classgrade.GD_"+user+
        "_"+partid+"_aw");
    var saveval   = eval("document.classgrade.GD_"+user+
        "_"+partid+"_sv_s.value");
    var selname   = eval("document.classgrade.GD_"+user+
        "_"+partid+"_sv");
    if (saveval != "correct") {
       scorename.value = "";
       selname[1].selected = true;
    }
       }
    } else {
       for (i=0;i<document.classgrade.total.value;i++) {
    var user = eval("document.classgrade.ctr"+i+".value");
    var scorename = eval("document.classgrade.GD_"+user+
        "_"+partid+"_aw");
    var saveval   = eval("document.classgrade.GD_"+user+
        "_"+partid+"_sv_s.value");
    var selname   = eval("document.classgrade.GD_"+user+
        "_"+partid+"_sv");
    if (saveval != "correct") {
       scorename.value = eval("document.classgrade.GD_"+user+
        "_"+partid+"_aw_s.value");;
       selname[0].selected = true;
    }
       }
    }    
       }
   
       function changeSelect(partid,user) {
    var selval = eval("document.classgrade.GD_"+user+'_'+partid+"_sv");
    var textbox = eval("document.classgrade.GD_"+user+'_'+partid+"_aw");
    var point  = textbox.value;
    var weight = eval("document.classgrade.weight_"+partid+".value");
   
    if (isNaN(point) || point < 0) {
       alert("A number equal or greater than 0 is expected. Entered value = "+point);
       textbox.value = "";
       return;
    }
    if (point > weight) {
       var resp = confirm("You entered a value ("+point+
          ") greater than the weight of the part. Accept?");
       if (resp == false) {
    textbox.value = "";
    return;
       }
    }
    selval[0].selected = true;
       }
   
       function changeOneScore(partid,user) {
    var selval = eval("document.classgrade.GD_"+user+'_'+partid+"_sv");
    if (selval[1].selected) {
       var boxval = eval("document.classgrade.GD_"+user+'_'+partid+"_aw");
       boxval.value = "";
    }
       }
   
       function resetEntry(numpart) {
    for (ctpart=0;ctpart<numpart;ctpart++) {
       var partid = eval("document.classgrade.partid_"+ctpart+".value");
       var radioButton = eval("document.classgrade.RADVAL_"+partid);
       var textbox = eval("document.classgrade.TEXTVAL_"+partid);
       var selval  = eval("document.classgrade.SELVAL_"+partid);
       for (var i=0; i<radioButton.length; i++) {
    radioButton[i].checked=false;
   
       }
       textbox.value = "";
       selval[0].selected = true;
   
       for (i=0;i<document.classgrade.total.value;i++) {
    var user = eval("document.classgrade.ctr"+i+".value");
    var resetscore = eval("document.classgrade.GD_"+user+
         "_"+partid+"_aw");
    resetscore.value = eval("document.classgrade.GD_"+user+
    "_"+partid+"_aw_s.value");
   
    var saveselval   = eval("document.classgrade.GD_"+user+
        "_"+partid+"_sv_s.value");
   
    var selname   = eval("document.classgrade.GD_"+user+"_"+partid+"_sv");
    if (saveselval == "excused") {
       if (selname[1].selected == false) { selname[1].selected = true;}
    } else {
       if (selname[0].selected == false) {selname[0].selected = true};
    }
       }
    }
       }
   
   </script>
   VIEWJAVASCRIPT
 }  }
   
 sub editgrades {  #--- show scores for a section or whole class w/ option to change/update a score
   my ($request) = @_;  sub viewgrades {
   my $result='';      my ($request) = shift;
       &viewgrades_js($request);
   
   my $symb=$ENV{'form.symb'};      my ($symb,$url) = ($ENV{'form.symb'},$ENV{'form.url'}); 
   if ($symb eq '') { $request->print("Unable to handle ambiguous references:$symb:$ENV{'form.url'}"); return ''; }      my $result='<h3><font color="#339933">Manual Grading</font></h3>';
   my $url=$ENV{'form.url'};  
   #get classlist  
   my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});  
   #print "Found $cdom:$cnum<br />";  
   my (%classlist) = &getclasslist($cdom,$cnum,'0');  
   
   #get list of parts for this problem  
   my (@parts) = &getpartlist($url);  
   
   $result.='<form action="/adm/grades" method="post">'."\n".  
     '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".  
       '<input type="hidden" name="url" value="'.$url.'" />'."\n".  
  '<input type="hidden" name="command" value="viewgrades" />'."\n".  
   '<input type="submit" name="submit" value="See Grades" /> <br />'."\n";  
   
   foreach my $student ( sort(@{ $classlist{'allids'} }) ) {      $result.='<font size=+1><b>Resource: </b>'.$ENV{'form.url'}.'</font>'."\n";
     $result.=&setstudentgrade($url,$symb,$ENV{'request.course.id'},$student,@parts);  
   }      #view individual student submission form - called using Javascript viewOneStudent
       $result.=&jscriptNform($url,$symb);
   
       #beginning of class grading form
       $result.= '<form action="/adm/grades" method="post" name="classgrade">'."\n".
    '<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
    '<input type="hidden" name="url"     value="'.$url.'" />'."\n".
    '<input type="hidden" name="command" value="editgrades" />'."\n".
    '<input type="hidden" name="section" value="'.$ENV{'form.section'}.'" />'."\n";
       $result.='To assign the same score for all the students use the radio buttons or '.
    'text box below. To assign scores individually fill in the score boxes for '.
    'each student in the table below. <font color="red">A part that has already '.
    'been graded does not get changed using the radio buttons or text box. '.
    'If needed, it has to be changed individually.</font>';
   
       #radio buttons/text box for assigning points for a section or class.
       #handles different parts of a problem
       my ($partlist,$handgrade) = &response_type($ENV{'form.url'});
       my %weight = ();
       my $ctsparts = 0;
       $result.='<table border="0">';
       my %seen = ();
       for (sort keys(%$handgrade)) {
    my ($partid,$respid) = split (/_/);
    next if $seen{$partid};
    $seen{$partid}++;
    my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});
    my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);
    $weight{$partid} = $wgt eq '' ? '1' : $wgt;
   
    $result.='<input type="hidden" name="partid_'.
       $ctsparts.'" value="'.$partid.'" />'."\n";
    $result.='<input type="hidden" name="weight_'.
       $partid.'" value="'.$weight{$partid}.'" />'."\n";
    $result.='<tr><td><b>Part  '.$partid.'&nbsp; &nbsp;Point:</b> </td><td>';
    $result.='<table border="0"><tr>';  
    my $ctr = 0;
    while ($ctr<=$weight{$partid}) { # display radio buttons in a nice table 10 across
       $result.= '<td><input type="radio" name="RADVAL_'.$partid.'" '.
    'onclick="javascript:writePoint('.$partid.','.$weight{$partid}.
    ','.$ctr.')" />'.$ctr."</td>\n";
       $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
       $ctr++;
    }
    $result.='</tr></table>';
    $result.= '</td><td><b> or </b><input type="text" name="TEXTVAL_'.
       $partid.'" size="4" '.
       'onChange="javascript:writePoint('.$partid.','.$weight{$partid}.
       ',\'textval\')" /> /'.
       $weight{$partid}.' (problem weight)</td>'."\n";
    $result.= '</td><td><select name="SELVAL_'.$partid.'"'.
       'onChange="javascript:writeRadText('.$partid.','.$weight{$partid}.')" /> '.
       '<option selected="on"> </option>'.
       '<option>excused</option></select></td></tr>'."\n";
    $ctsparts++;
       }
       $result.='</table><input type="hidden" name="totalparts" value="'.$ctsparts.'" />';
       $result.='<input type="button" value="Reset" '.
    'onClick="javascript:resetEntry('.$ctsparts.');" TARGET=_self> &nbsp; &nbsp;';
       $result.='<input type="button" value="Submit Changes" '.
    'onClick="javascript:submit();" TARGET=_self />'."\n";
   
       #table listing all the students in a section/class
       #header of table
       $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".
    '<table border=0><tr bgcolor="#deffff">'.
    '<td><b>Fullname</b></td><td><b>Username</b></td><td><b>Domain</b></td>'."\n";
       my (@parts) = sort(&getpartlist($url));
       foreach my $part (@parts) {
    my $display=&Apache::lonnet::metadata($url,$part.'.display');
    next if ($display =~ /^Number of Attempts/);
    if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
    if ($display =~ /^Partial Credit Factor/) {
       $_ = $display;
       my ($partid) = /.*?(\d+).*/;
       $result.='<td><b>Score Part '.$partid.'<br>(weight = '.
    $weight{$partid}.')</b></td>'."\n";
       next;
    }
    $display =~ s/Problem Status/Grade Status<br>/;
    $result.='<td><b>'.$display.'</b></td>'."\n";
       }
       $result.='</tr>';
   
   $result.='<input type="submit" name="submit" value="See Grades" /></table></form>';      #get info for each student
   return $result;      #list all the students - with points and grade status
       my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist($ENV{'form.section'},'0');
       my $ctr = 0;
       foreach (sort {$$fullname{$a} cmp $$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++;
       }
       $result.='</table></td></tr></table>';
       $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n";
       $result.='<input type="button" value="Submit Changes" '.
    'onClick="javascript:submit();" TARGET=_self /></form>'."\n";
       $result.=&show_grading_menu_form($symb,$url);
       return $result;
 }  }
   
 sub csvupload {  #--- call by previous routine to display each student
   my ($request)= @_;  sub viewstudentgrade {
   my $result;      my ($url,$symb,$courseid,$student,$fullname,$parts,$weight) = @_;
   my ($symb,$url)=&get_symb_and_url($request);      my ($uname,$udom) = split(/:/,$student);
   if (!$symb) {return '';}      my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);
   my $upfile_select=&Apache::loncommon::upfile_select_html();      my $result='<tr bgcolor="#ffffdd"><td>'.
   $result.=<<ENDUPFORM;   '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">   '\')"; TARGET=_self>'.$fullname.'</a>'.
 <input type="hidden" name="symb" value="$symb" />   '</td><td>'.$uname.'</td><td align="middle">'.$udom.'</td>'."\n";
 <input type="hidden" name="url" value="$url" />      foreach my $part (@$parts) {
 <input type="hidden" name="command" value="csvuploadmap" />   my ($temp,$part,$type)=split(/_/,$part);
 <hr />   my $score=$record{"resource.$part.$type"};
 <h3>Specify a file containing the class grades for resource $url</h3>   next if $type eq 'tries';
 $upfile_select   if ($type eq 'awarded') {
 <p><input type="submit" name="submit" value="Upload Grades" />      my $pts = $score eq '' ? '' : $score*$$weight{$part};
 ENDUPFORM      $result.='<input type="hidden" name="'.
   return $result;   'GD_'.$uname.'_'.$part.'_aw_s" value="'.$pts.'" />'."\n";
       $result.='<td align="middle"><input type="text" name="'.
    'GD_'.$uname.'_'.$part.'_aw" '.
    'onChange="javascript:changeSelect('.$part.',\''.$uname.
    '\')" value="'.$pts.'" size="4" /></td>'."\n";
    } elsif ($type eq 'solved') {
       my ($status,$foo)=split(/_/,$score,2);
       $status = 'nothing' if ($status eq '');
       $result.='<input type="hidden" name="'.
    'GD_'.$uname.'_'.$part.'_sv_s" value="'.$status.'" />'."\n";
       $result.='<td align="middle"><select name="'.
    'GD_'.$uname.'_'.$part.'_sv" '.
    'onChange="javascript:changeOneScore('.$part.',\''.$uname.'\')" >'."\n";
       my $optsel = '<option selected="on"> </option><option>excused</option>'."\n";
       $optsel = '<option> </option><option selected="on">excused</option>'."\n"
    if ($status eq 'excused');
       $result.=$optsel;
       $result.="</select></td>\n";
    }
       }
       $result.='</tr>';
       return $result;
   }
   
   #--- change scores for all the students in a section/class
   #    record does not get update if unchanged
   sub editgrades {
       my ($request) = @_;
   
       my $symb=$ENV{'form.symb'};
       my $url =$ENV{'form.url'};
       my $title='<h3><font color="#339933">Current Grade Status</font></h3>';
       $title.='<font size=+1><b>Resource: </b>'.$ENV{'form.url'}.'</font><br />'."\n";
       $title.='<font size=+1><b>Section: </b>'.$ENV{'form.section'}.'</font>'."\n";
       $title.= &show_grading_menu_form ($symb,$url);
       my $result= '<table border="0"><tr><td bgcolor="#777777">'."\n";
       $result.= '<table border="0"><tr bgcolor="#deffff">'.
    '<td rowspan=2><b>Username</b></td><td rowspan=2><b>Fullname</b></td>'."\n";
   
       my %scoreptr = (
       'correct'  =>'correct_by_override',
       'incorrect'=>'incorrect_by_override',
       'excused'  =>'excused',
       'ungraded' =>'ungraded_attempted',
       'nothing'  => '',
       );
       my ($classlist,$seclist,$ids,$stusec,$fullname) = &getclasslist($ENV{'form.section'},'0');
   
       my (@partid);
       my %weight = ();
       my ($i,$ctr,$count,$rec_update) = (0,0,0,0);
       while ($ctr < $ENV{'form.totalparts'}) {
    my $partid = $ENV{'form.partid_'.$ctr};
    push @partid,$partid;
    $weight{$partid} = $ENV{'form.weight_'.$partid};
    $ctr++;
    $result .= '<td colspan = 2 align="center"><b>Part '.$partid.
       '</b> (Weight = '.$weight{$partid}.')</td>';
       }
       $result .= '</tr><tr bgcolor="#deffff">';
       foreach (@partid) {
    $result .= '<td align="center">&nbsp;<b>Old Score</b>&nbsp;</td>'.
       '<td align="center">&nbsp;<b>New Score</b>&nbsp;</td>';
       }
       $result .= '</tr>'."\n";
   
       for ($i=0; $i<$ENV{'form.total'}; $i++) {
    my $user = $ENV{'form.ctr'.$i};
    my %newrecord;
    my $updateflag = 0;
    my @userdom = grep /^$user:/,keys %$classlist;
    my ($foo,$udom) = split(/:/,$userdom[0]);
   
    $result .= '<tr bgcolor="#ffffde"><td>'.$user.'&nbsp;</td><td>'.
       $$fullname{$userdom[0]}.'&nbsp;</td>';
   
    foreach (@partid) {
       my $old_aw    = $ENV{'form.GD_'.$user.'_'.$_.'_aw_s'};
       my $old_part  = $old_aw eq '' ? '' : $old_aw/$weight{$_};
       my $old_score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_sv_s'}};
   
       my $awarded   = $ENV{'form.GD_'.$user.'_'.$_.'_aw'};
       my $partial   = $awarded eq '' ? '' : $awarded/$weight{$_};
       my $score;
       if ($partial eq '') {
    $score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_sv_s'}};
       } elsif ($partial > 0) {
    $score = 'correct_by_override';
       } elsif ($partial == 0) {
    $score = 'incorrect_by_override';
       }
       $score = 'excused' if (($ENV{'form.GD_'.$user.'_'.$_.'_sv'} eq 'excused') &&
      ($score ne 'excused'));
       $result .= '<td align="center">'.$old_aw.'&nbsp;</td>'.
    '<td align="center">'.$awarded.
    ($score eq 'excused' ? $score : '').'&nbsp;</td>';
   
       next if ($old_part eq $partial && $old_score eq $score);
   
       $updateflag = 1;
       $newrecord{'resource.'.$_.'.awarded'}  = $partial if $partial ne '';
       $newrecord{'resource.'.$_.'.solved'}   = $score;
       $rec_update++;
    }
    $result .= '</tr>'."\n";
    if ($updateflag) {
       $count++;
       $newrecord{'resource.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
       &Apache::lonnet::cstore(\%newrecord,$symb,$ENV{'request.course.id'},
       $udom,$user);
    }
       }
       $result .= '</table></td></tr></table>'."\n";
       my $msg = '<b>Number of records updated = '.$rec_update.
    ' for '.$count.' student'.($count <= 1 ? '' : 's').'.</b><br />'.
    '<b>Total number of students = '.$ENV{'form.total'}.'</b><br />';
       return $title.$msg.$result;
 }  }
   #------------- end of section for handling grading by section/class ---------
   #
   #----------------------------------------------------------------------------
   
   
   #----------------------------------------------------------------------------
   #
   #-------------------------- Next few routines handles grading by csv upload
   #
   #--- Javascript to handle csv upload
 sub csvupload_javascript_reverse_associate {  sub csvupload_javascript_reverse_associate {
   return(<<ENDPICK);    return(<<ENDPICK);
   function verify(vf) {    function verify(vf) {
Line 829  ENDPICK Line 1878  ENDPICK
 }  }
   
 sub csvuploadmap_header {  sub csvuploadmap_header {
   my ($request,$symb,$url,$datatoken,$distotal)= @_;      my ($request,$symb,$url,$datatoken,$distotal)= @_;
   my $result;      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();      }
   }  
   $request->print(<<ENDPICK);      my $result='<table border="0">';
       $result.='<tr><td colspan=3><font size=+1><b>Resource: </b>'.$url.'</font></td></tr>';
       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>';
       $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>Uploading Class Grades for resource $url</h3>  <h3><font color="#339933">Uploading Class Grades</font></h3>
   $result
 <hr>  <hr>
 <h3>Identify fields</h3>  <h3>Identify fields</h3>
 Total number of records found in file: $distotal <hr />  Total number of records found in file: $distotal <hr />
Line 861  to this page if the data selected is ins Line 1924  to this page if the data selected is ins
 $javascript  $javascript
 </script>  </script>
 ENDPICK  ENDPICK
   return '';  return '';
   
 }  }
   
 sub csvupload_fields {  sub csvupload_fields {
   my ($url) = @_;      my ($url) = @_;
   my (@parts) = &getpartlist($url);      my (@parts) = &getpartlist($url);
   my @fields=(['username','Student Username'],['domain','Student Domain']);      my @fields=(['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);
     push(@fields,\@datum);   push(@fields,\@datum);
   }      }
   return (@fields);      return (@fields);
 }  }
   
 sub csvuploadmap_footer {  sub csvuploadmap_footer {
   my ($request,$i,$keyfields) =@_;      my ($request,$i,$keyfields) =@_;
   $request->print(<<ENDPICK);      $request->print(<<ENDPICK);
 </table>  </table>
 <input type="hidden" name="nfields" value="$i" />  <input type="hidden" name="nfields" value="$i" />
 <input type="hidden" name="keyfields" value="$keyfields" />  <input type="hidden" name="keyfields" value="$keyfields" />
Line 892  ENDPICK Line 1955  ENDPICK
 }  }
   
 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 {  
     $datatoken=$ENV{'form.datatoken'};  
     &Apache::loncommon::load_tmp_file($request);  
   }  
   my @records=&Apache::loncommon::upfile_record_sep();  
   &csvuploadmap_header($request,$symb,$url,$datatoken,$#records+1);  
   my $i;  
   my $keyfields;  
   if (@records) {  
     my @fields=&csvupload_fields($url);  
     if ($ENV{'form.upfile_associate'} eq 'reverse') {  
       &Apache::loncommon::csv_print_samples($request,\@records);  
       $i=&Apache::loncommon::csv_print_select_table($request,\@records,  
     \@fields);  
       foreach (@fields) { $keyfields.=$_->[0].','; }  
       chop($keyfields);  
     } else {      } else {
       unshift(@fields,['none','']);   $datatoken=$ENV{'form.datatoken'};
       $i=&Apache::loncommon::csv_samples_select_table($request,\@records,   &Apache::loncommon::load_tmp_file($request);
       \@fields);  
       my %sone=&Apache::loncommon::record_sep($records[0]);  
       $keyfields=join(',',sort(keys(%sone)));  
     }      }
   }      my @records=&Apache::loncommon::upfile_record_sep();
   &csvuploadmap_footer($request,$i,$keyfields);      &csvuploadmap_header($request,$symb,$url,$datatoken,$#records+1);
   return '';      my ($i,$keyfields);
       if (@records) {
    my @fields=&csvupload_fields($url);
   
    if ($ENV{'form.upfile_associate'} eq 'reverse') {
       &Apache::loncommon::csv_print_samples($request,\@records);
       $i=&Apache::loncommon::csv_print_select_table($request,\@records,
     \@fields);
       foreach (@fields) { $keyfields.=$_->[0].','; }
       chop($keyfields);
    } else {
       unshift(@fields,['none','']);
       $i=&Apache::loncommon::csv_samples_select_table($request,\@records,
       \@fields);
       my %sone=&Apache::loncommon::record_sep($records[0]);
       $keyfields=join(',',sort(keys(%sone)));
    }
       }
       &csvuploadmap_footer($request,$i,$keyfields);
       return '';
 }  }
   
 sub csvuploadassign {  sub csvuploadassign {
   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 '';}
   &Apache::loncommon::load_tmp_file($request);      &Apache::loncommon::load_tmp_file($request);
   my @gradedata=&Apache::loncommon::upfile_record_sep();      my @gradedata = &Apache::loncommon::upfile_record_sep();
   my @keyfields = split(/\,/,$ENV{'form.keyfields'});      my @keyfields = split(/\,/,$ENV{'form.keyfields'});
   my %fields=();      my %fields=();
   for (my $i=0; $i<=$ENV{'form.nfields'}; $i++) {      for (my $i=0; $i<=$ENV{'form.nfields'}; $i++) {
     if ($ENV{'form.upfile_associate'} eq 'reverse') {   if ($ENV{'form.upfile_associate'} eq 'reverse') {
       if ($ENV{'form.f'.$i} ne 'none') {      if ($ENV{'form.f'.$i} ne 'none') {
  $fields{$keyfields[$i]}=$ENV{'form.f'.$i};   $fields{$keyfields[$i]}=$ENV{'form.f'.$i};
       }      }
    } else {
       if ($ENV{'form.f'.$i} ne 'none') {
    $fields{$ENV{'form.f'.$i}}=$keyfields[$i];
       }
    }
       }
       $request->print('<h3>Assigning Grades</h3>');
       my $courseid=$ENV{'request.course.id'};
       my ($classlist) = &getclasslist('all','1');
       my @skipped;
       my $countdone=0;
       foreach my $grade (@gradedata) {
    my %entries=&Apache::loncommon::record_sep($grade);
    my $username=$entries{$fields{'username'}};
    my $domain=$entries{$fields{'domain'}};
    if (!exists($$classlist{"$username:$domain"})) {
       push(@skipped,"$username:$domain");
       next;
    }
    my %grades;
    foreach my $dest (keys(%fields)) {
       if ($dest eq 'username' || $dest eq 'domain') { next; }
       if ($entries{$fields{$dest}} eq '') { 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'}";
    &Apache::lonnet::cstore(\%grades,$symb,$ENV{'request.course.id'},
    $domain,$username);
    $request->print('.');
    $request->rflush();
    $countdone++;
       }
       $request->print("<br />Stored $countdone students\n");
       if (@skipped) {
    $request->print('<br /><font size="+1"><b>Skipped Students</b></font><br />');
    foreach my $student (@skipped) { $request->print("<br />$student"); }
       }
       $request->print(&view_edit_entire_class_form($symb,$url));
       $request->print(&show_grading_menu_form($symb,$url));
       return '';
   }
   #------------- end of section for handling csv file upload ---------
   #
   #-------------------------------------------------------------------
   
   #-------------------------- Menu interface -------------------------
   #
   #--- Show a Grading Menu button - Calls the next routine ---
   sub show_grading_menu_form {
       my ($symb,$url)=@_;
       my $result.='<form action="/adm/grades" method="post">'."\n".
    '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
    '<input type="hidden" name="url" value="'.$url.'" />'."\n".
    '<input type="hidden" name="command" value="gradingmenu" />'."\n".
    '<input type="submit" name="submit" value="Grading Menu" />'."\n".
    '</form>'."\n";
       return $result;
   }
   
   #--- Displays the main menu page -------
   sub gradingmenu {
       my ($request) = @_;
       my ($symb,$url)=&get_symb_and_url($request);
       if (!$symb) {return '';}
       my $result='<h3>&nbsp;<font color="#339933">Select a Grading Method</font></h3>';
       $result.='<table border="0">';
       $result.='<tr><td colspan=3><font size=+1><b>Resource: </b>'.$url.'</font></td></tr>';
       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>';
       $result.=&view_edit_entire_class_form($symb,$url).'<br />';
       $result.=&upcsvScores_form($symb,$url).'<br />';
       $result.=&viewGradeaStu_form($symb,$url,$resptype,$hdgrade).'<br />';
       $result.=&verifyReceipt_form($symb,$url) 
    if ((&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'})) && ($symb));
    
       return $result;
   }
   
   #--- Menu for grading a section or the whole class ---
   sub view_edit_entire_class_form {
       my ($symb,$url)=@_;
       my ($classlist,$sections) = &getclasslist('all','0');
       my $result.='<table width=100% border=0><tr><td bgcolor=#777777>'."\n";
       $result.='<table width=100% border=0><tr bgcolor="#e6ffff"><td>'."\n";
       $result.='&nbsp;<b>View/Grade Entire Section/Class</b></td></tr>'."\n";
       $result.='<tr bgcolor=#ffffe6><td>'."\n";
       $result.='<form action="/adm/grades" method="post">'."\n".
    '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
    '<input type="hidden" name="url" value="'.$url.'" />'."\n".
    '<input type="hidden" name="command" value="viewgrades" />'."\n";
       $result.='&nbsp;<b>Select section:</b> <select name="section">'."\n";
       foreach (sort (@$sections)) {
    $result.= '<option>'.$_.'</option>'."\n";
       }
       $result.='<option selected="on">all</select>'."<br />\n";
       $result.='&nbsp;<input type="button" onClick="submit();" value="View/Grade" /></form>'."\n";
       $result.='</td></tr></table>'."\n";
       $result.='</td></tr></table>'."\n";
       return $result;
   }
   
   #--- Menu to upload a csv scores ---
   sub upcsvScores_form {
       my ($symb,$url) = @_;
       if (!$symb) {return '';}
       my $result = '<script type="text/javascript" language="javascript">'."\n".
    '  function checkUpload(formname) {'."\n".
    '    if (formname.upfile.value == "") {'."\n".
    '       alert("Please use the browse button to select a file from your local directory.");'."\n".
    '       return false;'."\n".
    '    }'."\n".
    '    formname.submit();'."\n".
    '  }'."\n".
    '</script>'."\n";
   
       $result.='<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 above resource</b></td></tr>'."\n";
       $result.='<tr bgcolor=#ffffe6><td>'."\n";
       my $upfile_select=&Apache::loncommon::upfile_select_html();
     $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" />
   $upfile_select
   <br />&nbsp;<input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Grades" />
   </form>
   ENDUPFORM
       $result.='</td></tr></table>'."\n";
       $result.='</td></tr></table>'."\n";
       return $result;
   }
   
   #--- Handgrading problems ---
   sub viewGradeaStu_form {
       my ($symb,$url,$response,$handgrade) = @_;
       my ($classlist,$sections) = &getclasslist('all','0');
       my $result.='<table width=100% border=0><tr><td bgcolor=#777777>'."\n";
       $result.='<table width=100% border=0><tr bgcolor="#e6ffff"><td>'."\n";
       $result.='&nbsp;<b>View/Grade an Individual Student\'s Submission</b></td></tr>'."\n";
       $result.='<tr bgcolor=#ffffe6><td>'."\n";
       $result.='<form action="/adm/grades" method="post">'."\n".
    '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
    '<input type="hidden" name="url" value="'.$url.'" />'."\n".
    '<input type="hidden" name="response" value="'.$response.'" />'."\n".
    '<input type="hidden" name="handgrade" value="'.$handgrade.'" />'."\n".
    '<input type="hidden" name="showgrading" value="yes" />'."\n".
    '<input type="hidden" name="command" value="submission" />'."\n";
   
       $result.='&nbsp;<b>Select section:</b> <select name="section">'."\n";
       foreach (sort (@$sections)) {
    $result.= '<option>'.$_.'</option>'."\n";
       }
       $result.= '<option selected="on">all</select>'."\n";
       $result.='&nbsp;&nbsp;<b>Display students who has: </b>'.
    '<input type="radio" name="submitonly" value="yes" checked> submitted'.
    '<input type="radio" name="submitonly" value="all"> everybody <br />';
       $result.='&nbsp;(Section "no" implies the students were not assigned a section.)<br />' 
    if (grep /no/,@$sections);
      
       $result.='<br />&nbsp;<input type="button" onClick="submit();" value="View/Grade" />'."\n".
    '</form>'."\n";
       $result.='</td></tr></table>'."\n";
       $result.='</td></tr></table>'."\n";
       return $result;
   }
   
   #--- Form to input a receipt number ---
   sub verifyReceipt_form {
       my ($symb,$url) = @_;
       my $result = '<script type="text/javascript" language="javascript">'."\n".
    '  function checkEntry(formname) {'."\n".
    '    var receipt = formname.receipt.value;'."\n".
    '    if (isNaN(receipt) || receipt == "") {'."\n".
    '       alert("Please enter a receipt number given by a student in the box.");'."\n".
    '       return false;'."\n".
    '    }'."\n".
    '    formname.submit();'."\n".
    '  }'."\n".
    '</script>'."\n";
   
       my $hostver=unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'});
   
       $result.='<table width=100% border=0><tr><td bgcolor=#777777>'."\n";
       $result.='<table width=100% border=0><tr><td bgcolor=#e6ffff>'."\n";
       $result.='&nbsp;<b>Verify a Submission Receipt Issued by this Server</td></tr>'."\n";
       $result.='<tr bgcolor=#ffffe6><td>'."\n";
       $result.='<form action="/adm/grades" method="post" name="verifyform">'."\n";
       $result.='&nbsp;<tt>'.$hostver.'-<input type="text" name="receipt" size="4"></tt><br />'."\n";
       $result.='&nbsp;<input type="button" onClick="javascript:checkEntry(this.form);"'.
    ' value="Verify Receipt">'."\n";
       $result.='<input type="hidden" name="command" value="verify">'."\n";
       if ($ENV{'form.url'}) {
    $result.='<input type="hidden" name="url" value="'.$ENV{'form.url'}.'" />';
       }
       if ($ENV{'form.symb'}) {
    $result.='<input type="hidden" name="symb" value="'.$ENV{'form.symb'}.'" />';
       }
       $result.='</form>';
       $result.='</td></tr></table>'."\n";
       $result.='</td></tr></table>'."\n";
       return $result;
   }
   
   sub handler {
       my $request=$_[0];
       
       if ($ENV{'browser.mathml'}) {
    $request->content_type('text/xml');
     } else {      } else {
       if ($ENV{'form.f'.$i} ne 'none') {   $request->content_type('text/html');
  $fields{$ENV{'form.f'.$i}}=$keyfields[$i];  
       }  
     }      }
   }      $request->send_http_header;
   $request->print('<h3>Assigning Grades</h3>');      return '' if $request->header_only;
   my $courseid=$ENV{'request.course.id'};      &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});
   my $cdom=$ENV{"course.$courseid.domain"};      my $url=$ENV{'form.url'};
   my $cnum=$ENV{"course.$courseid.num"};      my $symb=$ENV{'form.symb'};
   my (%classlist) = &getclasslist($cdom,$cnum,'1');      my $command=$ENV{'form.command'};
   my @skipped;      if (!$url) {
   my $countdone=0;   my ($temp1,$temp2);
   foreach my $grade (@gradedata) {   ($temp1,$temp2,$ENV{'form.url'})=split(/___/,$symb);
     my %entries=&Apache::loncommon::record_sep($grade);   $url = $ENV{'form.url'};
     my $username=$entries{$fields{'username'}};      }
     my $domain=$entries{$fields{'domain'}};      &send_header($request);
     if (!exists($classlist{"$username:$domain"})) {      if ($url eq '' && $symb eq '') {
       push(@skipped,"$username:$domain");   if ($ENV{'user.adv'}) {
       next;      if (($ENV{'form.codeone'}) && ($ENV{'form.codetwo'}) &&
     }   ($ENV{'form.codethree'})) {
     my %grades;   my $token=$ENV{'form.codeone'}.'*'.$ENV{'form.codetwo'}.'*'.
     foreach my $dest (keys(%fields)) {      $ENV{'form.codethree'};
       if ($dest eq 'username' || $dest eq 'domain') { next; }   my ($tsymb,$tuname,$tudom,$tcrsid)=
       if ($entries{$fields{$dest}} eq '') { next; }      &Apache::lonnet::checkin($token);
       my $store_key=$dest;   if ($tsymb) {
       $store_key=~s/^stores/resource/;      my ($map,$id,$url)=split(/\_\_\_/,$tsymb);
       $store_key=~s/_/\./g;      if (&Apache::lonnet::allowed('mgr',$tcrsid)) {
       $grades{$store_key}=$entries{$fields{$dest}};   $request->print(
     }   &Apache::lonnet::ssi('/res/'.$url,
     $grades{"resource.regrader"}="$ENV{'user.name'}:$ENV{'user.domain'}";       ('grade_username' => $tuname,
     &Apache::lonnet::cstore(\%grades,$symb,$ENV{'request.course.id'},        'grade_domain' => $tudom,
     $domain,$username);        'grade_courseid' => $tcrsid,
     $request->print('.');        'grade_symb' => $tsymb)));
     $request->rflush();      } else {
     $countdone++;   $request->print('<h3>Not authorized: '.$token.'</h3>');
   }      }           
   $request->print("<br />Stored $countdone students\n");   } else {
   if (@skipped) {      $request->print('<h3>Not a valid DocID: '.$token.'</h3>');
     $request->print('<br /><font size="+1"><b>Skipped Students</b></font><br />');   }
     foreach my $student (@skipped) { $request->print("<br />$student"); }      } else {
   }   $request->print(&Apache::lonxml::tokeninputfield());
   $request->print(&view_edit_entire_class_form($symb,$url));      }
   $request->print(&show_grading_menu_form($symb,$url));   }
   return '';      } else {
    $Apache::grades::viewgrades=&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'});
    if ($command eq 'submission') {
       &listStudents($request) if ($ENV{'form.student'} eq '');
       &submission($request,0,0) if ($ENV{'form.student'} ne '');
    } elsif ($command eq 'processGroup') {
       &processGroup($request);
    } elsif ($command eq 'gradingmenu') {
       $request->print(&gradingmenu($request));
    } elsif ($command eq 'viewgrades') {
       $request->print(&viewgrades($request));
    } elsif ($command eq 'handgrade') {
       $request->print(&processHandGrade($request));
    } elsif ($command eq 'editgrades') {
       $request->print(&editgrades($request));
    } elsif ($command eq 'verify') {
       $request->print(&verifyreceipt($request));
    } elsif ($command eq 'csvupload') {
       $request->print(&csvupload($request));
    } elsif ($command eq 'viewclasslist') {
       $request->print(&viewclasslist($request));
    } elsif ($command eq 'csvuploadmap') {
       $request->print(&csvuploadmap($request));
    } elsif ($command eq 'csvuploadassign') {
       if ($ENV{'form.associate'} ne 'Reverse Association') {
    $request->print(&csvuploadassign($request));
       } else {
    if ( $ENV{'form.upfile_associate'} ne 'reverse' ) {
       $ENV{'form.upfile_associate'} = 'reverse';
    } else {
       $ENV{'form.upfile_associate'} = 'forward';
    }
    $request->print(&csvuploadmap($request));
       }
    } else {
       $request->print("Unknown action: $command:");
    }
       }
       &send_footer($request);
       return '';
 }  }
   
 sub send_header {  sub send_header {
   my ($request)= @_;      my ($request)= @_;
   $request->print(&Apache::lontexconvert::header());      $request->print(&Apache::lontexconvert::header());
 #  $request->print("  #  $request->print("
 #<script>  #<script>
 #remotewindow=open('','homeworkremote');  #remotewindow=open('','homeworkremote');
 #remotewindow.close();  #remotewindow.close();
 #</script>");   #</script>"); 
   $request->print('<body bgcolor="#FFFFFF">');      $request->print('<body bgcolor="#FFFFFF">');
 }  }
   
 sub send_footer {  sub send_footer {
   my ($request)= @_;      my ($request)= @_;
   $request->print('</body>');      $request->print('</body>');
   $request->print(&Apache::lontexconvert::footer());      $request->print(&Apache::lontexconvert::footer());
 }  
   
 sub handler {  
   my $request=$_[0];  
   
   if ($ENV{'browser.mathml'}) {  
     $request->content_type('text/xml');  
   } else {  
     $request->content_type('text/html');  
   }  
   $request->send_http_header;  
   return OK if $request->header_only;  
   &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});  
   my $url=$ENV{'form.url'};  
   my $symb=$ENV{'form.symb'};  
   my $command=$ENV{'form.command'};  
   if (!$url) {  
     my ($temp1,$temp2);  
     ($temp1,$temp2,$ENV{'form.url'})=split(/___/,$symb);  
     $url = $ENV{'form.url'};  
   }  
   &send_header($request);  
   if ($url eq '' && $symb eq '') {  
      if ($ENV{'user.adv'}) {  
          if (($ENV{'form.codeone'}) && ($ENV{'form.codetwo'}) &&  
              ($ENV{'form.codethree'})) {  
              my $token=$ENV{'form.codeone'}.'*'.$ENV{'form.codetwo'}.'*'.  
         $ENV{'form.codethree'};  
              my ($tsymb,$tuname,$tudom,$tcrsid)=  
  &Apache::lonnet::checkin($token);  
              if ($tsymb) {  
                 my ($map,$id,$url)=split(/\_\_\_/,$tsymb);  
                 if (&Apache::lonnet::allowed('mgr',$tcrsid)) {  
                    $request->print(  
                      &Apache::lonnet::ssi('/res/'.$url,  
                         ('grade_username' => $tuname,  
                          'grade_domain' => $tudom,  
                          'grade_courseid' => $tcrsid,  
                          'grade_symb' => $tsymb)));  
                 } else {  
                    $request->print('<h1>Not authorized: '.$token.'</h1>');  
                 }             
     } else {  
                 $request->print('<h1>Not a valid DocID: '.$token.'</h1>');  
             }  
  } else {  
              $request->print(&Apache::lonxml::tokeninputfield());  
          }  
      }  
   } else {  
     #&Apache::lonhomework::showhashsubset(\%ENV,'^form');  
     $Apache::grades::viewgrades=&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'});  
     if ($command eq 'submission') {  
       &listStudents($request) if ($ENV{'form.student'} eq '');  
       $request->print(&submission($request)) if ($ENV{'form.student'} ne '');  
     } elsif ($command eq 'gradingmenu') {  
       $request->print(&gradingmenu($request));  
     } elsif ($command eq 'viewgrades') {  
       $request->print(&viewgrades($request));  
     } elsif ($command eq 'handgrade') {  
       $request->print(&processHandGrade($request));  
     } elsif ($command eq 'editgrades') {  
       $request->print(&editgrades($request));  
     } elsif ($command eq 'verify') {  
       $request->print(&verifyreceipt($request));  
     } elsif ($command eq 'csvupload') {  
       $request->print(&csvupload($request));  
     } elsif ($command eq 'csvuploadmap') {  
       $request->print(&csvuploadmap($request));  
     } elsif ($command eq 'receiptInput') {  
       &receiptInput($request);  
     } elsif ($command eq 'csvuploadassign') {  
       if ($ENV{'form.associate'} ne 'Reverse Association') {  
  $request->print(&csvuploadassign($request));  
       } else {  
  if ( $ENV{'form.upfile_associate'} ne 'reverse' ) {  
   $ENV{'form.upfile_associate'} = 'reverse';  
  } else {  
   $ENV{'form.upfile_associate'} = 'forward';  
  }  
  $request->print(&csvuploadmap($request));  
       }  
     } else {  
       $request->print("Unknown action: $command:");  
     }  
   }  
   &send_footer($request);  
   return OK;  
 }  }
   
 1;  1;

Removed from v.1.33  
changed lines
  Added in v.1.46


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