File:  [LON-CAPA] / loncom / homework / grades.pm
Revision 1.85: download - view: text, annotated - select for diffs
Sat Apr 19 09:02:57 2003 UTC (21 years, 1 month ago) by albertel
Branches: MAIN
CVS tags: HEAD
- progress window
- unties hashes
- tries to count problems correct/incorrect
  (probably should be a return value of sorts . . .)

# The LearningOnline Network with CAPA
# The LON-CAPA Grading handler
#
# $Id: grades.pm,v 1.85 2003/04/19 09:02:57 albertel Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#
# 2/9,2/13 Guy Albertelli
# 6/8 Gerd Kortemeyer
# 7/26 H.K. Ng
# 8/20 Gerd Kortemeyer
# Year 2002
# June-August H.K. Ng
# Year 2003
# February, March H.K. Ng
#

package Apache::grades;
use strict;
use Apache::style;
use Apache::lonxml;
use Apache::lonnet;
use Apache::loncommon;
use Apache::lonnavmaps;
use Apache::lonhomework;
use Apache::loncoursedata;
use Apache::lonmsg qw(:user_normal_msg);
use Apache::Constants qw(:common);

# ----- These first few routines are general use routines.----
#
# --- Retrieve the parts that matches stores_\d+ from the metadata file.---
sub getpartlist {
    my ($url) = @_;
    my @parts =();
    my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));
    foreach my $key (@metakeys) {
	if ( $key =~ m/stores_(\w+)_.*/) {
	    push(@parts,$key);
	}
    }
    return @parts;
}

# --- Get the symbolic name of a problem and the url
sub get_symb_and_url {
    my ($request) = @_;
    (my $url=$ENV{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
    my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));
    if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
    return ($symb,$url);
}

# --- Retrieve the fullname for a user. Return lastname, first middle ---
# --- Generation is attached next to the lastname if it exists. ---
sub get_fullname {
    my ($uname,$udom) = @_;
    my %name=&Apache::lonnet::get('environment', ['lastname','generation',
						  'firstname','middlename'],
                                  $udom,$uname);
    my $fullname;
    my ($tmp) = keys(%name);
    if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
        $fullname = &Apache::loncoursedata::ProcessFullName
            (@name{qw/lastname generation firstname middlename/});
    } else {
        &Apache::lonnet::logthis('grades.pm: no name data for '.$uname.
                                 '@'.$udom.':'.$tmp);
    }
    return $fullname;
}

#--- Get the partlist and the response type for a given problem. ---
#--- Indicate if a response type is coded handgraded or not. ---
sub response_type {
    my ($url) = shift;
    my $allkeys = &Apache::lonnet::metadata($url,'keys');
    my %seen = ();
    my (@partlist,%handgrade);
    foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) {
	if (/^\w+response_\w+.*/) {
	    my ($responsetype,$part) = split(/_/,$_,2);
	    my ($partid,$respid) = split(/_/,$part);
	    $handgrade{$part} = $responsetype.':'.($allkeys =~ /parameter_$part\_handgrade/ ? 'yes' : 'no');
	    next if ($seen{$partid} > 0);
	    $seen{$partid}++;
	    push @partlist,$partid;
	}
    }
    return \@partlist,\%handgrade;
}

#--- Dumps the class list with usernames,list of sections,
#--- section, ids and fullnames for each user.
sub getclasslist {
    my ($getsec,$filterlist) = @_;
    my $classlist=&Apache::loncoursedata::get_classlist();
    # Bail out if we were unable to get the classlist
    return if (! defined($classlist));
    #
    my %sections;
    my %fullnames;
    foreach (keys(%$classlist)) {
        # the following undefs are for 'domain', and 'username' respectively.
	my (undef,undef,$end,$start,$id,$section,$fullname,$status)=
            @{$classlist->{$_}};
	# filter students according to status selected
	if ($filterlist && $ENV{'form.status'} ne 'Any') {
	    if ($ENV{'form.status'} ne $status) {
		delete ($classlist->{$_});
		next;
	    }
	}
	$section = ($section ne '' ? $section : 'no');
	if ($getsec eq 'all' || $getsec eq $section) {
            $sections{$section}++;
            $fullnames{$_}=$fullname;
        } else {
            delete($classlist->{$_});
        }
    }
    my %seen = ();
    my @sections = sort(keys(%sections));
    return ($classlist,\@sections,\%fullnames);
}

#find user domain
sub finduser {
    my ($name) = @_;
    my $domain = '';
    if ( $Apache::grades::viewgrades eq 'F' ) {
	my %classlist=&Apache::lonnet::dump('classlist',
					    $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
					    $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
	my (@fields) = grep /^$name:/, keys %classlist;
	($name, $domain) = split(/:/,$fields[0]);
	return ($name,$domain);
    } else {
	return ($ENV{'user.name'},$ENV{'user.domain'});
    }
}

#--- Prompts a user to enter a username.
sub moreinfo {
    my ($request,$reason) = @_;
    $request->print("Unable to process request: $reason");
    if ( $Apache::grades::viewgrades eq 'F' ) {
	$request->print('<form action="/adm/grades" method="post">'."\n");
	if ($ENV{'form.url'}) {
	    $request->print('<input type="hidden" name="url" value="'.$ENV{'form.url'}.'" />'."\n");
	}
	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>');
    }
    return '';
}

#--- Retrieve the grade status of a student for all the parts
sub student_gradeStatus {
    my ($url,$symb,$udom,$uname,$partlist) = @_;
    my %record     = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
    my %partstatus = ();
    foreach (@$partlist) {
	my ($status,$foo)    = split(/_/,$record{"resource.$_.solved"},2);
	$status              = 'nothing' if ($status eq '');
	$partstatus{$_}      = $status;
	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="saveState" value="'.$ENV{'form.saveState'}.'" />'."\n".
	'<input type="hidden" name="probTitle" value="'.$ENV{'form.probTitle'}.'" />'."\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;
}

#------------------ End of general use routines --------------------
#-------------------------------------------------------------------

#------------------------------------ Receipt Verification Routines
#
#--- Check whether a receipt number is valid.---
sub verifyreceipt {
    my $request  = shift;

    my $courseid = $ENV{'request.course.id'};
    my $receipt  = unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}).'-'.
	$ENV{'form.receipt'};
    $receipt     =~ s/[^\-\d]//g;
    my $url      = $ENV{'form.url'};
    my $symb     = $ENV{'form.symb'};
    unless ($symb) {
	$symb    = &Apache::lonnet::symbread($url);
    }

    my $title.='<h3><font color="#339933">Verifying Submission Receipt '.
	$receipt.'</h3></font>'."\n".
	'<font size=+1><b>Problem: </b>'.$ENV{'form.probTitle'}.'</font><br><br>'."\n";

    my ($string,$contents,$matches) = ('','',0);
    my (undef,undef,$fullname) = &getclasslist('all','0');

    foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
	my ($uname,$udom)=split(/\:/);
	if ($receipt eq 
	    &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb)) {
	    $contents.='<tr bgcolor="#ffffe6"><td>&nbsp;'."\n".
		'<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.
		'\')"; TARGET=_self>'.$$fullname{$_}.'</a>&nbsp;</td>'."\n".
		'<td>&nbsp;'.$uname.'&nbsp;</td>'.
		'<td>&nbsp;'.$udom.'&nbsp;</td></tr>'."\n";
	    
	    $matches++;
	}
    }
    if ($matches == 0) {
	$string = $title.'No match found for the above receipt.';
    } else {
	$string = &jscriptNform($url,$symb).$title.
	    'The above receipt matches the following student'.
	    ($matches <= 1 ? '.' : 's.')."\n".
	    '<table border="0"><tr><td bgcolor="#777777">'."\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;

    my ($symb,$url) = &get_symb_and_url($request);
    my $cdom      = $ENV{"course.$ENV{'request.course.id'}.domain"};
    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;
    my ($partlist,$handgrade) = &response_type($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>'."\n";

    my $viewgrade = $ENV{'form.handgrade'} eq 'yes' ? 'View/Grade' : 'View';
    $ENV{'form.probTitle'} = $ENV{'form.probTitle'} eq '' ? 
	&Apache::lonnet::gettitle($symb) : $ENV{'form.probTitle'};

    $result='<h3><font color="#339933">&nbsp;'.
	$viewgrade.
	    ' Submissions for a Student or a Group of Students</font></h3>'.
	    '<table border="0"><tr><td colspan=3><font size=+1>'.
		    '<b>Problem: </b>'.$ENV{'form.probTitle'}.'</font></td></tr>'.$result;

    $request->print(<<LISTJAVASCRIPT);
<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 $viewgrade button.");
       return false;
    }
    document.gradesub.submit();
  }
</script>
LISTJAVASCRIPT

    $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" /> one student '."\n".
	'<input type="radio" name="vProb" value="all" /> all students <br />'."\n".
	'&nbsp;<b>Submissions: </b>'."\n";
    if ($ENV{'form.handgrade'} eq 'yes') {
	$gradeTable.='<input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> handgrade only'."\n";
    }
    $gradeTable.='<input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> last sub only'."\n".
	'<input type="radio" name="lastSub" value="last" /> last sub & parts info'."\n".
	'<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="saveState"   value="'.$ENV{'form.saveState'}.'" />'."\n".
	'<input type="hidden" name="probTitle"   value="'.$ENV{'form.probTitle'}.'" />'."\n".
	'<input type="hidden" name="url"  value="'.$url.'" />'."\n".
	'<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
	'To '.lc($viewgrade).' a submission, click on the check box next to the student\'s name. Then '."\n".
	'click on the '.$viewgrade.' button. To view the submissions for a group of students, click'."\n".
	' on the check boxes for the group of students.<br />'."\n".
	'<input type="hidden" name="command" value="processGroup" />'."\n".
	'<input type="button" '."\n".
	'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".
	'value="'.$viewgrade.'" />'."\n";
 
    my (undef,undef,$fullname) = &getclasslist($getsec,$ENV{'form.showgrading'} eq 'yes' ? '1' : '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 {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
	my ($uname,$udom) = split(/:/,$student);
	my (%status) =&student_gradeStatus($url,$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="'.$viewgrade.'" /></form>'."\n";
    if ($ctr == 0) {
	$gradeTable='<br />&nbsp;<font color="red">'.
	    'No submission found for this resource.</font><br />';
    } elsif ($ctr == 1) {
	$gradeTable =~ s/type=checkbox/type=checkbox checked/;
    }
    $gradeTable.=&show_grading_menu_form($symb,$url);
    $request->print($gradeTable);
    return '';
}

#---- Called from the listStudents routine
#     Displays the submissions for one student or a group of students
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 '';
}

#------------------------------------------------------------------------------------
#
#-------------------------- Next few routines handles grading by student, essentially
#                           handles essay response type problem/part
#
#--- Javascript to handle the submission page functionality ---
sub sub_page_js {
    my $request = shift;
    $request->print(<<SUBJAVASCRIPT);
<script type="text/javascript" language="javascript">
    function updateRadio(formname,id,weight) {
	var gradeBox = eval("formname.GD_BOX"+id);
	var radioButton = eval("formname.RADVAL"+id);
	var oldpts = eval("formname.oldpts"+id+".value");
	var pts = checkSolved(formname,id) == 'update' ? gradeBox.value : oldpts;
	gradeBox.value = pts;
	var resetbox = false;
	if (isNaN(pts) || pts < 0) {
	    alert("A number equal or greater than 0 is expected. Entered value = "+pts);
	    for (var i=0; i<radioButton.length; i++) {
		if (radioButton[i].checked) {
		    gradeBox.value = i;
		    resetbox = true;
		}
	    }
	    if (!resetbox) {
		formtextbox.value = "";
	    }
	    return;
	}

	if (pts > weight) {
	    var resp = confirm("You entered a value ("+pts+
			       ") greater than the weight for the part. Accept?");
	    if (resp == false) {
		gradeBox.value = "";
		return;
	    }
	}

	for (var i=0; i<radioButton.length; i++) {
	    radioButton[i].checked=false;
	    if (pts == i && pts != "") {
		radioButton[i].checked=true;
	    }
	}
	updateSelect(formname,id);
	var stores = eval("formname.stores"+id);
	stores.value = "0";
    }

    function writeBox(formname,id,pts) {
	var gradeBox = eval("formname.GD_BOX"+id);
	if (checkSolved(formname,id) == 'update') {
	    gradeBox.value = pts;
	} else {
	    var oldpts = eval("formname.oldpts"+id+".value");
	    gradeBox.value = oldpts;
	    var radioButton = eval("formname.RADVAL"+id);
	    for (var i=0; i<radioButton.length; i++) {
		radioButton[i].checked=false;
		if (i == oldpts) {
		    radioButton[i].checked=true;
		}
	    }
	}
	var stores = eval("formname.stores"+id);
	stores.value = "0";
	updateSelect(formname,id);
	return;
    }

    function clearRadBox(formname,id) {
	if (checkSolved(formname,id) == 'noupdate') {
	    updateSelect(formname,id);
	    return;
	}
	gradeSelect = eval("formname.GD_SEL"+id);
	for (var i=0; i<gradeSelect.length; i++) {
	    if (gradeSelect[i].selected) {
		var selectx=i;
	    }
	}
	var stores = eval("formname.stores"+id);
	if (selectx == stores.value) { return };
	var gradeBox = eval("formname.GD_BOX"+id);
	gradeBox.value = "";
	var radioButton = eval("formname.RADVAL"+id);
	for (var i=0; i<radioButton.length; i++) {
	    radioButton[i].checked=false;
	}
	stores.value = selectx;
    }

    function checkSolved(formname,id) {
	if (eval("formname.solved"+id+".value") == "correct_by_student") {
	    alert("This problem has been graded correct by the computer. The score cannot be changed.");
	    return "noupdate";
	}
	return "update";
    }

    function updateSelect(formname,id) {
	var gradeSelect = eval("formname.GD_SEL"+id);
	gradeSelect[0].selected = true;
	return;
    }

//=========== Check that a point is assigned for all the parts (essay grading only) ============
    function checksubmit(formname,val,total,parttot) {
	document.SCORE.gradeOpt.value = val;
	if (val == "Save & Next") {
	    for (i=0;i<=total;i++) {
		for (j=0;j<parttot;j++) {
		    var partid = eval("formname.partid"+i+"_"+j+".value");
		    var selopt = eval("formname.GD_SEL"+i+"_"+partid);
		    if (selopt[0].selected) {
			var points = eval("formname.GD_BOX"+i+"_"+partid+".value");
			if (points == "") {
			    var name = eval("formname.name"+i+".value");
			    var resp = confirm("You did not assign a score for "+name+", part "+partid+". Continue?");
			    if (resp == false) {
				eval("formname.GD_BOX"+i+"_"+partid+".focus()");
				return false;
			    }
			}
		    }
		    
		}
	    }
	    
	}
	formname.submit();
    }

//======= Check that a score is assigned for all the problems (page/sequence grading only) =========
    function checkSubmitPage(formname,total) {
	noscore = new Array(100);
	var ptr = 0;
	for (i=1;i<total;i++) {
	    var partid = eval("formname.q_"+i+".value");
	    var selopt = eval("formname.GD_SEL"+i+"_"+partid);
	    if (selopt[0].selected) {
		var points = eval("formname.GD_BOX"+i+"_"+partid+".value");
		var status = eval("formname.solved"+i+"_"+partid+".value");
		if (points == "" && status != "correct_by_student") {
		    noscore[ptr] = i;
		    ptr++;
		}
	    }
	}
	if (ptr != 0) {
	    var sense = ptr == 1 ? ": " : "s: ";
	    var prolist = "";
	    if (ptr == 1) {
		prolist = noscore[0];
	    } else {
		var i = 0;
		while (i < ptr-1) {
		    prolist += noscore[i]+", ";
		    i++;
		}
		prolist += "and "+noscore[i];
	    }
	    var resp = confirm("You did not assign any score for the following problem"+sense+prolist+". Continue?");
	    if (resp == false) {
		return false;
	    }
	}

	formname.submit();
    }
</script>
SUBJAVASCRIPT
}

#--- javascript for essay type problem --
sub sub_page_kw_js {
    my $request = shift;
    my $iconpath = $request->dir_config('lonIconsURL');
    $request->print(<<SUBJAVASCRIPT);
<script type="text/javascript" language="javascript">

//===================== Show list of keywords ====================
  function keywords(keyform) {
    var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",keyform.value);
    if (nret==null) return;
    keyform.value = nret;

    document.SCORE.refresh.value = "on";
    if (document.SCORE.keywords.value != "") {
	document.SCORE.submit();
    }
    return;
  }

//===================== Script to view submitted by ==================
  function viewSubmitter(submitter) {
    document.SCORE.refresh.value = "on";
    document.SCORE.NCT.value = "1";
    document.SCORE.unamedom0.value = submitter;
    document.SCORE.submit();
    return;
  }

//===================== Script to add keyword(s) ==================
  function getSel() {
    if (document.getSelection) txt = document.getSelection();
    else if (document.selection) txt = document.selection.createRange().text;
    else return;
    var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," ");
    if (cleantxt=="") {
	alert("Please select a word or group of words from document and then click this link.");
	return;
    }
    var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt);
    if (nret==null) return;
    var curlist = document.SCORE.keywords.value;
    document.SCORE.keywords.value = curlist+" "+nret;
    document.SCORE.refresh.value = "on";
    if (document.SCORE.keywords.value != "") {
	document.SCORE.submit();
    }
    return;
  }

//====================== Script for composing message ==============
   // preload images
   img1 = new Image();
   img1.src = "$iconpath/mailbkgrd.gif";
   img2 = new Image();
   img2.src = "$iconpath/mailto.gif";

  function msgCenter(msgform,usrctr,fullname) {
    var Nmsg  = msgform.savemsgN.value;
    savedMsgHeader(Nmsg,usrctr,fullname);
    var subject = msgform.msgsub.value;
    var rtrchk  = eval("document.SCORE.includemsg"+usrctr);
    var msgchk = rtrchk.value;
    re = /msgsub/;
    var shwsel = "";
    if (re.test(msgchk)) { shwsel = "checked" }
    displaySubject(subject,shwsel);
    for (var i=1; i<=Nmsg; i++) {
	var testpt = "savemsg"+i+",";
	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;
  }

//  var pWin = null;
  function savedMsgHeader(Nmsg,usrctr,fullname) {
    var height = 70*Nmsg+250;
    var scrollbar = "no";
    if (height > 600) {
	height = 600;
	scrollbar = "yes";
    }
//    if (window.pWin) {window.pWin.close(); window.pWin=null}
    pWin = window.open('', 'MessageCenter', 'toolbar=no,location=no,scrollbars='+scrollbar+',screenx=70,screeny=75,width=600,height='+height);
    pWin.focus();
    pDoc = pWin.document;
    pDoc.write("<html><head>");
    pDoc.write("<title>Message Central</title>");

    pDoc.write("<script language=javascript>");
    pDoc.write("function checkInput() {");
    pDoc.write("  opener.document.SCORE.msgsub.value = document.msgcenter.msgsub.value;");
    pDoc.write("  var nmsg   = opener.document.SCORE.savemsgN.value;");
    pDoc.write("  var usrctr = document.msgcenter.usrctr.value;");
    pDoc.write("  var newval = eval(\\"opener.document.SCORE.newmsg\\"+usrctr);");
    pDoc.write("  newval.value = document.msgcenter.newmsg.value;");

    pDoc.write("  var msgchk = \\"\\";");
    pDoc.write("  if (document.msgcenter.subchk.checked) {");
    pDoc.write("     msgchk = \\"msgsub,\\";");
    pDoc.write("  }");
    pDoc.write("  var includemsg = 0;");
    pDoc.write("  for (var i=1; i<=nmsg; i++) {");
    pDoc.write("      var opnmsg = eval(\\"opener.document.SCORE.savemsg\\"+i);");
    pDoc.write("      var frmmsg = eval(\\"document.msgcenter.msg\\"+i);");
    pDoc.write("      opnmsg.value = frmmsg.value;");
    pDoc.write("      var chkbox = eval(\\"document.msgcenter.msgn\\"+i);");
    pDoc.write("      if (chkbox.checked) {");
    pDoc.write("         msgchk += \\"savemsg\\"+i+\\",\\";");
    pDoc.write("         includemsg = 1;");
    pDoc.write("      }");
    pDoc.write("  }");
    pDoc.write("  if (document.msgcenter.newmsgchk.checked) {");
    pDoc.write("     msgchk += \\"newmsg\\"+usrctr;");
    pDoc.write("     includemsg = 1;");
    pDoc.write("  }");
    pDoc.write("  imgformname = eval(\\"opener.document.SCORE.mailicon\\"+usrctr);");
    pDoc.write("  imgformname.src = \\"$iconpath/\\"+((includemsg) ? \\"mailto.gif\\" : \\"mailbkgrd.gif\\");");
    pDoc.write("  var includemsg = eval(\\"opener.document.SCORE.includemsg\\"+usrctr);");
    pDoc.write("  includemsg.value = msgchk;");

    pDoc.write("  self.close()");

    pDoc.write("}");

    pDoc.write("<");
    pDoc.write("/script>");

    pDoc.write("</head><body bgcolor=white>");

    pDoc.write("<form action=\\"inactive\\" name=\\"msgcenter\\">");
    pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">");
    pDoc.write("<font color=\\"green\\" size=+1>&nbsp;Compose Message for \"+fullname+\"</font><br><br>");

    pDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
    pDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
    pDoc.write("<td><b>Type</b></td><td><b>Include</b></td><td><b>Message</td></tr>");
}
    function displaySubject(msg,shwsel) {
    pDoc = pWin.document;
    pDoc.write("<tr bgcolor=\\"#ffffdd\\">");
    pDoc.write("<td>Subject</td>");
    pDoc.write("<td align=\\"center\\"><input name=\\"subchk\\" type=\\"checkbox\\"" +shwsel+"></td>");
    pDoc.write("<td><input name=\\"msgsub\\" type=\\"text\\" value=\\""+msg+"\\"size=\\"60\\" maxlength=\\"80\\"></td></tr>");
}

  function displaySavedMsg(ctr,msg,shwsel) {
    pDoc = pWin.document;
    pDoc.write("<tr bgcolor=\\"#ffffdd\\">");
    pDoc.write("<td align=\\"center\\">"+ctr+"</td>");
    pDoc.write("<td align=\\"center\\"><input name=\\"msgn"+ctr+"\\" type=\\"checkbox\\"" +shwsel+"></td>");
    pDoc.write("<td><textarea name=\\"msg"+ctr+"\\" cols=\\"60\\" rows=\\"3\\">"+msg+"</textarea></td></tr>");
}

  function newMsg(newmsg,shwsel) {
    pDoc = pWin.document;
    pDoc.write("<tr bgcolor=\\"#ffffdd\\">");
    pDoc.write("<td align=\\"center\\">New</td>");
    pDoc.write("<td align=\\"center\\"><input name=\\"newmsgchk\\" type=\\"checkbox\\"" +shwsel+"></td>");
    pDoc.write("<td><textarea name=\\"newmsg\\" cols=\\"60\\" rows=\\"3\\" onchange=\\"javascript:this.form.newmsgchk.checked=true\\" >"+newmsg+"</textarea></td></tr>");
}

  function msgTail() {
    pDoc = pWin.document;
    pDoc.write("</table>");
    pDoc.write("</td></tr></table>&nbsp;");
    pDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");
    pDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br><br>");
    pDoc.write("</form>");
    pDoc.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;
  }

//  var hwdWin = null;
  function highlightCentral() {
//    if (window.hwdWin) window.hwdWin.close();
    hwdWin = window.open('', 'KeywordHighlightCentral', 'toolbar=no,location=no,scrollbars=no,width=400,height=300,screenx=100,screeny=75');
    hwdWin.focus();
    var hDoc = hwdWin.document;
    hDoc.write("<html><head>");
    hDoc.write("<title>Highlight Central</title>");

    hDoc.write("<script language=javascript>");
    hDoc.write("function updateChoice(flag) {");
    hDoc.write("  opener.document.SCORE.kwclr.value = radioSelection(document.hlCenter.kwdclr);");
    hDoc.write("  opener.document.SCORE.kwsize.value = radioSelection(document.hlCenter.kwdsize);");
    hDoc.write("  opener.document.SCORE.kwstyle.value = radioSelection(document.hlCenter.kwdstyle);");
    hDoc.write("  opener.document.SCORE.refresh.value = \\"on\\";");
    hDoc.write("  if (opener.document.SCORE.keywords.value!=\\"\\"){");
    hDoc.write("     opener.document.SCORE.submit();");
    hDoc.write("  }");
    hDoc.write("  self.close()");
    hDoc.write("}");

    hDoc.write("function radioSelection(radioButton) {");
    hDoc.write("    var selection=null;");
    hDoc.write("    for (var i=0; i<radioButton.length; i++) {");
    hDoc.write("        if (radioButton[i].checked) {");
    hDoc.write("            selection=radioButton[i].value;");
    hDoc.write("            return selection;");
    hDoc.write("        }");
    hDoc.write("    }");
    hDoc.write("}");

    hDoc.write("<");
    hDoc.write("/script>");

    hDoc.write("</head><body bgcolor=white>");

    hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">");
    hDoc.write("<font color=\\"green\\" size=+1>&nbsp;Keyword Highlight Options</font><br><br>");

    hDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
    hDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
    hDoc.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) { 
    var hDoc = hwdWin.document;
    hDoc.write("<tr bgcolor=\\"#ffffdd\\">");
    hDoc.write("<td align=\\"left\\">");
    hDoc.write("<input name=\\"kwdclr\\" type=\\"radio\\" value=\\""+clrval+"\\" "+clrsel+">&nbsp;"+clrtxt+"</td>");
    hDoc.write("<td align=\\"left\\">");
    hDoc.write("<input name=\\"kwdsize\\" type=\\"radio\\" value=\\""+szval+"\\" "+szsel+">&nbsp;"+sztxt+"</td>");
    hDoc.write("<td align=\\"left\\">");
    hDoc.write("<input name=\\"kwdstyle\\" type=\\"radio\\" value=\\""+syval+"\\" "+sysel+">&nbsp;"+sytxt+"</td>");
    hDoc.write("</tr>");
  }

  function highlightend() { 
    var hDoc = hwdWin.document;
    hDoc.write("</table>");
    hDoc.write("</td></tr></table>&nbsp;");
    hDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:updateChoice(1)\\">&nbsp;&nbsp;");
    hDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br><br>");
    hDoc.write("</form>");
    hDoc.write("</body></html>");
  }

</script>
SUBJAVASCRIPT
}

#--- displays the grading box, used in essay type problem and grading by page/sequence
sub gradeBox {
    my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_;

    my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').
	'/check.gif" height="16" border="0" />';

    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);
    my $result='<input type="hidden" name="WGT'.$counter.'_'.$partid.'" value="'.$wgt.'" />'."\n";

    $result.='<table border="0"><tr><td>'.
	'<b>Part </b>'.$partid.' <b>Points: </b></td><td>'."\n";

    my $ctr = 0;
    $result.='<table border="0"><tr>'."\n";  # 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,\''.$counter.'_'.$partid.'\','.
	    $ctr.')" value="'.$ctr.'" '.
	    ($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>'."\n";
    $result.='<td><input type="text" name="GD_BOX'.$counter.'_'.$partid.'"'.
	($score ne ''? ' value = "'.$score.'"':'').' size="4" '.
	'onChange="javascript:updateRadio(this.form,\''.$counter.'_'.$partid.'\','.
	$wgt.')" /></td>'."\n";
    $result.='<td>/'.$wgt.' '.$wgtmsg.
	($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? '&nbsp;'.$checkIcon : '').
	' </td><td>'."\n";

    $result.='<select name="GD_SEL'.$counter.'_'.$partid.'" '.
	'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n";
    if ($$record{'resource.'.$partid.'.solved'} eq 'excused') {
	$result.='<option> </option>'.
	    '<option selected="on">excused</option></select>'."\n";
    } else {
	$result.='<option selected="on"> </option>'.
	    '<option>excused</option></select>'."\n";
    }
    $result.="&nbsp&nbsp\n";
    $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n".
	'<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n".
	'<input type="hidden" name="solved'.$counter.'_'.$partid.'" value="'.
	$$record{'resource.'.$partid.'.solved'}.'" />'."\n";
    $result.='</td></tr></table>'."\n";
    return $result;
}

sub show_problem {
    my ($request,$symb,$uname,$udom,$removeform,$viewon) = @_;
    my $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom,
						      $ENV{'request.course.id'});
    if ($removeform) {
	$rendered=~s|<form(.*?)>||g;
	$rendered=~s|</form>||g;
	$rendered=~s|name="submit"|name="would_have_been_submit"|g;
    }
    my $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom,
							   $ENV{'request.course.id'});
    if ($removeform) {
	$companswer=~s|<form(.*?)>||g;
	$companswer=~s|</form>||g;
	$rendered=~s|name="submit"|name="would_have_been_submit"|g;
    }
    my $result.='<table border="0" width="100%"><tr><td bgcolor="#777777">';
    $result.='<table border="0" width="100%">';
    $result.='<tr><td bgcolor="#e6ffff"><b> View of the problem - '.$ENV{'form.fullname'}.
	'</b></td></tr>' if ($viewon);
    $result.='<tr><td bgcolor="#ffffff">'.$rendered.'<br />';
    $result.='<b>Correct answer:</b><br />'.$companswer;
    $result.='</td></tr></table>';
    $result.='</td></tr></table><br />';
    return $result;
}

# --------------------------- show submissions of a student, option to grade 
sub submission {
    my ($request,$counter,$total) = @_;

    (my $url=$ENV{'form.url'})=~s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
    my ($uname,$udom)     = ($ENV{'form.student'},$ENV{'form.userdom'});
    ($uname,$udom)        = &finduser($uname) if $udom eq '';
    $ENV{'form.fullname'} = &get_fullname ($uname,$udom) if $ENV{'form.fullname'} eq '';

    my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));
    if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
    my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : '');

    # header info
    if ($counter == 0) {
	&sub_page_js($request);
	&sub_page_kw_js($request);
	$ENV{'form.probTitle'} = $ENV{'form.probTitle'} eq '' ? 
	    &Apache::lonnet::gettitle($symb) : $ENV{'form.probTitle'};

	$request->print('<h3>&nbsp;<font color="#339933">Submission Record</font></h3>'."\n".
			'<font size=+1>&nbsp;<b>Problem: </b>'.$ENV{'form.probTitle'}.'</font>'."\n");

	# 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' or !$ENV{'form.vProb'}) {
	    $request->print(&show_problem($request,$symb,$uname,$udom,0,1));
	}
	
	# 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'} : $ENV{'form.probTitle'};
	    $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="saveState"  value="'.$ENV{'form.saveState'}.'" />'."\n".
			'<input type="hidden" name="probTitle"  value="'.$ENV{'form.probTitle'}.'" />'."\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 '' ? 
		 &Apache::lonfeedback::clear_out_html($ENV{'form.savemsg'.$cts}) :
		 &Apache::lonfeedback::clear_out_html($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
        }
    }

    if ($ENV{'form.vProb'} eq 'all') {
	$request->print('<br /><br /><br />') if ($counter > 0);
	$request->print(&show_problem($request,$symb,$uname,$udom,1,1));
    }

    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;
    my ($classlist,$fullname);
    if ($ENV{'form.handgrade'} eq 'yes') {
	my @col_list;
	($classlist,undef,$fullname) = &getclasslist('all','0');
	for (keys (%$handgrade)) {
	    my $ncol = &Apache::lonnet::EXT('resource.'.$_.
					    '.maxcollaborators',
                                            $symb,$udom,$uname);
	    next if ($ncol <= 0);
            s/\_/\./g;
            next if ($record{'resource.'.$_.'.collaborators'} eq '');
            my (@colList) = split(/,?\s+/,
                                   $record{'resource.'.$_.'.collaborators'});
            my @collaborators = ();
	    foreach (@colList) { #pre-filter list - throw out submitter
		my ($co_name,$co_dom) = split /\@|:/,$_;
		$co_dom = $udom if (! defined($co_dom));
		next if ($co_name eq $uname && $co_dom eq $udom);
		push @collaborators, $_;
	    }
            my (@badcollaborators);
            if (scalar(@collaborators) != 0) {
                $result.='<b>Collaborators: </b>';
                foreach my $collaborator (@collaborators) {
                    my ($co_name,$co_dom) = split /\@|:/,$collaborator;
                    $co_dom = $udom if (! defined($co_dom));
                    # Doing this grep allows 'fuzzy' specification
                    my @Matches = grep /^$co_name:$co_dom$/i,
                    keys %$classlist;
		    if (! scalar(@Matches)) {
                        push @badcollaborators,':'.$collaborator.':';
                        next;
                    }
                    push @col_list, @Matches;
                    foreach (@Matches) {
                        my ($lastname,$givenn) = split(/,/,$$fullname{$_});
                        push @col_fullnames, $givenn.' '.$lastname;
                        $result.=$$fullname{$_}.'&nbsp; &nbsp; &nbsp;';
                    }
                }
                $result.='<br />'."\n";
                if (scalar(@badcollaborators) > 0) {
                    $result.='<table border="0"><tr bgcolor="#ffbbbb"><td>';
                    $result.='This student has submitted ';
                    if (scalar(@badcollaborators) == 1) {
                        $result .= 'an invalid collaborator';
                    } else {
                        $result .= 'invalid collaborators';
                    }
                    $result .= ': '.join(', ',@badcollaborators);
		    $result .= '</td></tr></table>';                 
                }
                if (scalar(@collaborators > $ncol)) {
                    $result .= '<table border="0"><tr bgcolor="#ffbbbb"><td>';
                    $result .= 'This student has submitted too many '.
                        'collaborators.  Maximum is '.$ncol;
                    $result .= '</td></tr></table>';
                }
                $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;'.
                                ($record{"resource.$partid.$respid.uploadedurl"}?
                                '<a href="'.
                                &Apache::lonnet::tokenwrapper($record{"resource.$partid.$respid.uploadedurl"}).
   '"><img src="/adm/lonIcons/unknown.gif" border=0"> File uploaded by student</a> <font color="red" size="1">Like all files provided by users, this file may contain virusses</font><br />':'').
                                '<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 {
	$request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,
								 $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");
	$request->print(&show_grading_menu_form($symb,$url)) 
	    if (($ENV{'form.command'} eq 'submission') || 
		($ENV{'form.command'} eq 'processGroup' && $counter == $total));
	return;
    }

    # Grading options
    $result='<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n".
	'<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".
	'<input type="hidden" name="unamedom'.$counter.'" value="'.$uname.':'
	.$udom.'" />'."\n";
    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.'.';
    }
    $msgfor =~ s/\'/\\'/g;
    $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> &nbsp;'.
	'<img src="'.$request->dir_config('lonIconsURL').
	'/mailbkgrd.gif" width="14" height="10" name="mailicon'.$counter.'" />'."\n".
	'<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;

	$request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));
    }
    $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(this.form,\'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(this.form,\'Next\');" TARGET=_self> &nbsp;'."\n".
	    '<input type="button" value="Previous" '.
	    'onClick="javascript:checksubmit(this.form,\'Previous\');" TARGET=_self> &nbsp;';
	$endform.='(Next and Previous do not save the scores.)'."\n" 
	    if ($ENV{'form.handgrade'} eq 'yes');
	$endform.='</td><tr></table></form>';
	$endform.=&show_grading_menu_form($symb,$url);
	$request->print($endform);
    }
    return '';
}

#--- Retrieve the last submission for all the parts
sub get_last_submission {
    my (%returnhash)=@_;
    my (@string,$timestamp);
    if ($returnhash{'version'}) {
	my %lasthash=();
	my ($version);
	for ($version=1;$version<=$returnhash{'version'};$version++) {
	    foreach (sort(split(/\:/,$returnhash{$version.':keys'}))) {
		$lasthash{$_}=$returnhash{$version.':'.$_};
		   $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;
}

#--- High light keywords, with style choosen by user.
sub keywords_highlight {
    my $string    = shift;
    my $size      = $ENV{'form.kwsize'} eq '0' ? '' : 'size='.$ENV{'form.kwsize'};
    my $styleon   = $ENV{'form.kwstyle'} eq ''  ? '' : $ENV{'form.kwstyle'};
    (my $styleoff = $styleon) =~ s/\</\<\//;
    my @keylist   = split(/[,\s+]/,$ENV{'form.keywords'});
    foreach (@keylist) {
	$string =~ s/\b\Q$_\E(\b|\.)/\<font color\=$ENV{'form.kwclr'} $size\>$styleon$_$styleoff\<\/font\>/gi;
    }
    # This is not really the right place to do this, but I cannot find a
    # better one at this time.  So here we go - the m in the s:::mg causes
    # ^ to match the beginning of a new line.  So we replace(???) the beginning
    # of the line with <br /> to make things formatted a little better.
    $string =~ s:^:<br />:mg;
    return $string;
}

#--- Called from submission routine
sub processHandGrade {
    my ($request) = shift;
    my $url    = $ENV{'form.url'};
    my $symb   = $ENV{'form.symb'};
    my $button = $ENV{'form.gradeOpt'};
    my $ngrade = $ENV{'form.NCT'};
    my $ntstu  = $ENV{'form.NTSTU'};

    if ($button eq 'Save & Next') {
	my $ctr = 0;
	while ($ctr < $ngrade) {
	    my ($uname,$udom) = split(/:/,$ENV{'form.unamedom'.$ctr});
	    my ($errorflag,$pts,$wgt) = &saveHandGrade($request,$url,$symb,$uname,$udom,$ctr);
	    if ($errorflag eq 'no_score') {
		$ctr++;
		next;
	    }
	    my $includemsg = $ENV{'form.includemsg'.$ctr};
	    my ($subject,$message,$msgstatus) = ('','','');
	    if ($includemsg =~ /savemsg|newmsg\Q$ctr\E/) {
		$subject = $ENV{'form.msgsub'} if ($includemsg =~ /^msgsub/);
		my (@msgnum) = split(/,/,$includemsg);
		foreach (@msgnum) {
		    $message.=$ENV{'form.'.$_} if ($_ =~ /savemsg|newmsg/ && $_ ne '');
		}
		$message =&Apache::lonfeedback::clear_out_html($message);
		$message.="\n\nPoint".($pts > 1 ? 's':'').' awarded = '.$pts.' out of '.$wgt;
		$message.=" for <a href=\"".
		    &Apache::lonnet::clutter($url).
		    "?symb=$symb\">$ENV{'form.probTitle'}</a>";
		$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 (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'0');
    my (@parsedlist,@nextlist);
    my ($nextflg) = 0;
    foreach (sort {lc($$fullname{$a}) cmp lc($$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{$_};
	&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 '';
}

#---- Save the score and award for each student, if changed
sub saveHandGrade {
    my ($request,$url,$symb,$stuname,$domain,$newflg,$submitter) = @_;
    my %record     = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$domain,$stuname);
    my %newrecord  = ();
    my ($pts,$wgt) = ('','');
    foreach (split(/:/,$ENV{'form.partlist'.$newflg})) {
	if ($ENV{'form.GD_SEL'.$newflg.'_'.$_} eq 'excused') {
	    if ($record{'resource.'.$_.'.solved'} ne 'excused') {
		$newrecord{'resource.'.$_.'.solved'} = 'excused';
		if (exists($record{'resource.'.$_.'.awarded'})) {
		    $newrecord{'resource.'.$_.'.awarded'} = '';
		}
	    }
	} else {
	    $pts = ($ENV{'form.GD_BOX'.$newflg.'_'.$_} ne '' ? 
		    $ENV{'form.GD_BOX'.$newflg.'_'.$_} : 
		    $ENV{'form.RADVAL'.$newflg.'_'.$_});
	    return 'no_score' if ($pts eq '' && $ENV{'form.GD_SEL'.$newflg.'_'.$_} eq '');
	    $wgt = $ENV{'form.WGT'.$newflg.'_'.$_} eq '' ? 1 : 
		$ENV{'form.WGT'.$newflg.'_'.$_};
	    my $partial= $pts/$wgt;
	    $newrecord{'resource.'.$_.'.awarded'}  = $partial 
		if ($record{'resource.'.$_.'.awarded'} ne $partial);
	    my $reckey = 'resource.'.$_.'.solved';
	    if ($partial == 0) {
		$newrecord{$reckey} = 'incorrect_by_override' 
		    if ($record{$reckey} ne 'incorrect_by_override');
	    } else {
		$newrecord{$reckey} = 'correct_by_override' 
		    if ($record{$reckey} ne 'correct_by_override');
	    }
	    $newrecord{'resource.'.$_.'.submitted_by'} = $submitter 
		if ($submitter && ($record{'resource.'.$_.'.submitted_by'} ne $submitter));
	    $newrecord{'resource.'.$_.'regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
	}
    }

    if (scalar(keys(%newrecord)) > 0) {
	&Apache::lonnet::cstore(\%newrecord,$symb,
				$ENV{'request.course.id'},$domain,$stuname);
    }
    return '',$pts,$wgt;
}

#--------------------------------------------------------------------------------------
#
#-------------------------- 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;

    $request->print(<<VIEWJAVASCRIPT);
<script type="text/javascript" language="javascript">
   function writePoint(partid,weight,point) {
	var radioButton = eval("document.classgrade.RADVAL_"+partid);
	var textbox = eval("document.classgrade.TEXTVAL_"+partid);
	if (point == "textval") {
	    var point = eval("document.classgrade.TEXTVAL_"+partid+".value");
	    if (isNaN(point) || point < 0) {
		alert("A number equal or greater than 0 is expected. Entered value = "+point);
		var resetbox = false;
		for (var i=0; i<radioButton.length; i++) {
		    if (radioButton[i].checked) {
			textbox.value = i;
			resetbox = true;
		    }
		}
		if (!resetbox) {
		    textbox.value = "";
		}
		return;
	    }
	    if (point > weight) {
		var resp = confirm("You entered a value ("+point+
				   ") greater than the weight for the part. Accept?");
		if (resp == false) {
		    textbox.value = "";
		    return;
		}
	    }
	    for (var i=0; i<radioButton.length; i++) {
		radioButton[i].checked=false;
		if (point == i) {
		    radioButton[i].checked=true;
		}
	    }

	} else {
	    textbox.value = point;
	}
	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+"_awarded");
	    var saveval   = eval("document.classgrade.GD_"+user+
				 "_"+partid+"_solved_s.value");
	    var selname   = eval("document.classgrade.GD_"+user+"_"+partid+"_solved");
	    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+"_awarded");
		var saveval   = eval("document.classgrade.GD_"+user+
				     "_"+partid+"_solved_s.value");
		var selname   = eval("document.classgrade.GD_"+user+
				     "_"+partid+"_solved");
		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+"_awarded");
		var saveval   = eval("document.classgrade.GD_"+user+
				     "_"+partid+"_solved_s.value");
		var selname   = eval("document.classgrade.GD_"+user+
				     "_"+partid+"_solved");
		if (saveval != "correct") {
		    scorename.value = eval("document.classgrade.GD_"+user+
				     "_"+partid+"_awarded_s.value");;
		    selname[0].selected = true;
		}
	    }
	}	    
    }

    function changeSelect(partid,user) {
	var selval = eval("document.classgrade.GD_"+user+'_'+partid+"_solved");
	var textbox = eval("document.classgrade.GD_"+user+'_'+partid+"_awarded");
	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+"_solved");
	if (selval[1].selected) {
	    var boxval = eval("document.classgrade.GD_"+user+'_'+partid+"_awarded");
	    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+"_awarded");
		resetscore.value = eval("document.classgrade.GD_"+user+
					"_"+partid+"_awarded_s.value");

		var saveselval   = eval("document.classgrade.GD_"+user+
				     "_"+partid+"_solved_s.value");

		var selname   = eval("document.classgrade.GD_"+user+"_"+partid+"_solved");
		if (saveselval == "excused") {
		    if (selname[1].selected == false) { selname[1].selected = true;}
		} else {
		    if (selname[0].selected == false) {selname[0].selected = true};
		}
	    }
	}
    }

</script>
VIEWJAVASCRIPT
}

#--- show scores for a section or whole class w/ option to change/update a score
sub viewgrades {
    my ($request) = shift;
    &viewgrades_js($request);

    my ($symb,$url) = ($ENV{'form.symb'},$ENV{'form.url'}); 
    my $result='<h3><font color="#339933">Manual Grading</font></h3>';

    $result.='<font size=+1><b>Problem: </b>'.$ENV{'form.probTitle'}.'</font>'."\n";

    #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".
	'<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."\n".
	'<input type="hidden" name="probTitle" value="'.$ENV{'form.probTitle'}.'" />'."\n";

    $result.='<h3>Assign Common Grade To ';
    if ($ENV{'form.section'} eq 'all') {
	$result.='Class </h3>';
    } elsif ($ENV{'form.section'} eq 'no') {
	$result.='Students in no Section </h3>';
    } else {
	$result.='Students in Section '.$ENV{'form.section'}.'</h3>';
    }
    $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".
	'<table border=0><tr bgcolor="#ffffdd"><td>';
    #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 (/_/,$_,2);
	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>'.'</td></tr></table>'.'</td></tr></table>'."\n".
	'<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.= '<h3>Assign Grade to Specific Students in ';
    if ($ENV{'form.section'} eq 'all') {
	$result.='the Class </h3>';
    } elsif ($ENV{'form.section'} eq 'no') {
	$result.='no Section </h3>';
    } else {
	$result.='Section '.$ENV{'form.section'}.'</h3>';
    }
    $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".
	'<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');
	if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
	if ($display =~ /^Partial Credit Factor/) {
	    my ($partid) = &split_part_type($part);
	    $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>';

    #get info for each student
    #list all the students - with points and grade status
    my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'1');
    my $ctr = 0;
    foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
	my ($uname,$udom) = split(/:/);
	$result.='<input type="hidden" name="ctr'.$ctr.'" value="'.$uname.'" />'."\n";
	$result.=&viewstudentgrade($url,$symb,$ENV{'request.course.id'},
				   $_,$$fullname{$_},\@parts,\%weight);
	$ctr++;
    }
    $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;
}

#--- call by previous routine to display each student
sub viewstudentgrade {
    my ($url,$symb,$courseid,$student,$fullname,$parts,$weight) = @_;
    my ($uname,$udom) = split(/:/,$student);
    my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);
    my $result='<tr bgcolor="#ffffdd"><td>'.
	'<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.
	'\')"; TARGET=_self>'.$fullname.'</a>'.
	'</td><td>'.$uname.'</td><td align="middle">'.$udom.'</td>'."\n";
    foreach my $apart (@$parts) {
	my ($part,$type) = &split_part_type($apart);
	my $score=$record{"resource.$part.$type"};
	if ($type eq 'awarded') {
	    my $pts = $score eq '' ? '' : $score*$$weight{$part};
	    $result.='<input type="hidden" name="'.
		'GD_'.$uname.'_'.$part.'_awarded_s" value="'.$pts.'" />'."\n";
	    $result.='<td align="middle"><input type="text" name="'.
		'GD_'.$uname.'_'.$part.'_awarded" '.
		'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.'_solved_s" value="'.$status.'" />'."\n";
	    $result.='<td align="middle"><select name="'.
		'GD_'.$uname.'_'.$part.'_solved" '.
		'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";
	} else {
	    $result.='<input type="hidden" name="'.
		'GD_'.$uname.'_'.$part.'_'.$type.'_s" value="'.$score.'" />'.
		    "\n";
	    $result.='<td align="middle"><input type="text" name="'.
		'GD_'.$uname.'_'.$part.'_'.$type.'" '.
		'value="'.$score.'" size="4" /></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>Problem: </b>'.$ENV{'form.probTitle'}.'</font><br />'."\n";
    $title.='<font size=+1><b>Section: </b>'.$ENV{'form.section'}.'</font>'."\n";
    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,undef,$fullname) = &getclasslist($ENV{'form.section'},'0');

    my (@partid);
    my %weight = ();
    my %columns = ();
    my ($i,$ctr,$count,$rec_update) = (0,0,0,0);

    my (@parts) = sort(&getpartlist($url));
    my $header;
    while ($ctr < $ENV{'form.totalparts'}) {
	my $partid = $ENV{'form.partid_'.$ctr};
	push @partid,$partid;
	$weight{$partid} = $ENV{'form.weight_'.$partid};
	$ctr++;
    }
    foreach my $partid (@partid) {
	$header .= '<td align="center">&nbsp;<b>Old Score</b>&nbsp;</td>'.
	    '<td align="center">&nbsp;<b>New Score</b>&nbsp;</td>';
	$columns{$partid}=2;
	foreach my $stores (@parts) {
	    my ($part,$type) = &split_part_type($stores);
	    if ($part !~ m/^\Q$partid\E/) { next;}
	    if ($type eq 'awarded' || $type eq 'solved') { next; }
	    my $display=&Apache::lonnet::metadata($url,$stores.'.display');
	    $display =~ s/\[Part: (\w)+\]//;
	    $header .= '<td align="center">&nbsp;<b>Old</b> '.$display.'&nbsp;</td>'.
		'<td align="center">&nbsp;<b>New</b> '.$display.'&nbsp;</td>';
	    $columns{$partid}+=2;
	}
    }
    foreach my $partid (@partid) {
	$result .= '<td colspan="'.$columns{$partid}.
	    '" align="center"><b>Part '.$partid.
	    '</b> (Weight = '.$weight{$partid}.')</td>';

    }
    $result .= '</tr><tr bgcolor="#deffff">';
    $result .= $header;
    $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 (undef,$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.'_'.$_.'_awarded_s'};
	    my $old_part_pcr = $old_aw/($weight{$_} ne '0' ? $weight{$_}:1);
	    my $old_part  = $old_aw eq '' ? '' : $old_part_pcr;
	    my $old_score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_solved_s'}};

	    my $awarded   = $ENV{'form.GD_'.$user.'_'.$_.'_awarded'};
	    my $pcr       = $awarded/($weight{$_} ne '0' ? $weight{$_} : 1);
	    my $partial   = $awarded eq '' ? '' : $pcr;
	    my $score;
	    if ($partial eq '') {
		$score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_solved_s'}};
	    } elsif ($partial > 0) {
		$score = 'correct_by_override';
	    } elsif ($partial == 0) {
		$score = 'incorrect_by_override';
	    }
	    $score = 'excused' if (($ENV{'form.GD_'.$user.'_'.$_.'_solved'} eq 'excused') &&
				   ($score ne 'excused'));
	    $result .= '<td align="center">'.$old_aw.'&nbsp;</td>'.
		'<td align="center">'.$awarded.
		($score eq 'excused' ? $score : '').'&nbsp;</td>';

	    if (!($old_part eq $partial && $old_score eq $score)) {
		$updateflag = 1;
		$newrecord{'resource.'.$_.'.awarded'}  = $partial if $partial ne '';
		$newrecord{'resource.'.$_.'.solved'}   = $score;
		$rec_update++;
	    }

	    my $partid=$_;
	    foreach my $stores (@parts) {
		my ($part,$type) = &split_part_type($stores);
		if ($part !~ m/^\Q$partid\E/) { next;}
		if ($type eq 'awarded' || $type eq 'solved') { next; }
		my $old_aw    = $ENV{'form.GD_'.$user.'_'.$part.'_'.$type.'_s'};
		my $awarded   = $ENV{'form.GD_'.$user.'_'.$part.'_'.$type};
		if ($awarded ne '' && $awarded ne $old_aw) {
		    $newrecord{'resource.'.$part.'.'.$type}= $awarded;
		    $newrecord{'resource.'.$part.'regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
		    $updateflag=1;
		}
		$result .= '<td align="center">'.$old_aw.'&nbsp;</td>'.
		    '<td align="center">'.$awarded.'&nbsp;</td>';
	    }
	}
	$result .= '</tr>'."\n";
	if ($updateflag) {
	    $count++;
	    &Apache::lonnet::cstore(\%newrecord,$symb,$ENV{'request.course.id'},
				    $udom,$user);
	}
    }
    $result .= '</table></td></tr></table>'."\n".
	&show_grading_menu_form ($symb,$url);
    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;
}

sub split_part_type {
    my ($partstr) = @_;
    my ($temp,@allparts)=split(/_/,$partstr);
    my $type=pop(@allparts);
    my $part=join('.',@allparts);
    return ($part,$type);
}

#------------- 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 {
  return(<<ENDPICK);
  function verify(vf) {
    var foundsomething=0;
    var founduname=0;
    var founddomain=0;
    for (i=0;i<=vf.nfields.value;i++) {
      tw=eval('vf.f'+i+'.selectedIndex');
      if (i==0 && tw!=0) { founduname=1; }
      if (i==1 && tw!=0) { founddomain=1; }
      if (i!=0 && i!=1 && tw!=0) { foundsomething=1; }
    }
    if (founduname==0 || founddomain==0) {
      alert('You need to specify at both the username and domain');
      return;
    }
    if (foundsomething==0) {
      alert('You need to specify at least one grading field');
      return;
    }
    vf.submit();
  }
  function flip(vf,tf) {
    var nw=eval('vf.f'+tf+'.selectedIndex');
    var i;
    for (i=0;i<=vf.nfields.value;i++) {
      //can not pick the same destination field for both name and domain
      if (((i ==0)||(i ==1)) && 
          ((tf==0)||(tf==1)) && 
          (i!=tf) &&
          (eval('vf.f'+i+'.selectedIndex')==nw)) {
        eval('vf.f'+i+'.selectedIndex=0;')
      }
    }
  }
ENDPICK
}

sub csvupload_javascript_forward_associate {
  return(<<ENDPICK);
  function verify(vf) {
    var foundsomething=0;
    var founduname=0;
    var founddomain=0;
    for (i=0;i<=vf.nfields.value;i++) {
      tw=eval('vf.f'+i+'.selectedIndex');
      if (tw==1) { founduname=1; }
      if (tw==2) { founddomain=1; }
      if (tw>2) { foundsomething=1; }
    }
    if (founduname==0 || founddomain==0) {
      alert('You need to specify at both the username and domain');
      return;
    }
    if (foundsomething==0) {
      alert('You need to specify at least one grading field');
      return;
    }
    vf.submit();
  }
  function flip(vf,tf) {
    var nw=eval('vf.f'+tf+'.selectedIndex');
    var i;
    //can not pick the same destination field twice
    for (i=0;i<=vf.nfields.value;i++) {
      if ((i!=tf) && (eval('vf.f'+i+'.selectedIndex')==nw)) {
        eval('vf.f'+i+'.selectedIndex=0;')
      }
    }
  }
ENDPICK
}

sub csvuploadmap_header {
    my ($request,$symb,$url,$datatoken,$distotal)= @_;
    my $javascript;
    if ($ENV{'form.upfile_associate'} eq 'reverse') {
	$javascript=&csvupload_javascript_reverse_associate();
    } else {
	$javascript=&csvupload_javascript_forward_associate();
    }

    my $result='<table border="0">';
    $result.='<tr><td colspan=3><font size=+1><b>Problem: </b>'.$ENV{'form.probTitle'}.'</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">
<h3><font color="#339933">Uploading Class Grades</font></h3>
$result
<hr>
<h3>Identify fields</h3>
Total number of records found in file: $distotal <hr />
Enter as many fields as you can. The system will inform you and bring you back
to this page if the data selected is insufficient to run your class.<hr />
<input type="button" value="Reverse Association" onClick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" />
<input type="hidden" name="associate"  value="" />
<input type="hidden" name="phase"      value="three" />
<input type="hidden" name="datatoken"  value="$datatoken" />
<input type="hidden" name="fileupload" value="$ENV{'form.fileupload'}" />
<input type="hidden" name="upfiletype" value="$ENV{'form.upfiletype'}" />
<input type="hidden" name="upfile_associate" 
                                       value="$ENV{'form.upfile_associate'}" />
<input type="hidden" name="symb"       value="$symb" />
<input type="hidden" name="url"        value="$url" />
<input type="hidden" name="saveState"  value="$ENV{'form.saveState'}" />
<input type="hidden" name="probTitle"  value="$ENV{'form.probTitle'}" />
<input type="hidden" name="command"    value="csvuploadassign" />
<hr />
<script type="text/javascript" language="Javascript">
$javascript
</script>
ENDPICK
return '';

}

sub csvupload_fields {
    my ($url) = @_;
    my (@parts) = &getpartlist($url);
    my @fields=(['username','Student Username'],['domain','Student Domain']);
    foreach my $part (sort(@parts)) {
	my @datum;
	my $display=&Apache::lonnet::metadata($url,$part.'.display');
	my $name=$part;
	if  (!$display) { $display = $name; }
	@datum=($name,$display);
	push(@fields,\@datum);
    }
    return (@fields);
}

sub csvuploadmap_footer {
    my ($request,$i,$keyfields) =@_;
    $request->print(<<ENDPICK);
</table>
<input type="hidden" name="nfields" value="$i" />
<input type="hidden" name="keyfields" value="$keyfields" />
<input type="button" onClick="javascript:verify(this.form)" value="Assign Grades" /><br />
</form>
ENDPICK
}

sub csvuploadmap {
    my ($request)= @_;
    my ($symb,$url)=&get_symb_and_url($request);
    if (!$symb) {return '';}

    my $datatoken;
    if (!$ENV{'form.datatoken'}) {
	$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,$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);
    $request->print(&show_grading_menu_form($symb,$url));

    return '';
}

sub csvuploadassign {
    my ($request)= @_;
    my ($symb,$url)=&get_symb_and_url($request);
    if (!$symb) {return '';}
    &Apache::loncommon::load_tmp_file($request);
    my @gradedata = &Apache::loncommon::upfile_record_sep();
    my @keyfields = split(/\,/,$ENV{'form.keyfields'});
    my %fields=();
    for (my $i=0; $i<=$ENV{'form.nfields'}; $i++) {
	if ($ENV{'form.upfile_associate'} eq 'reverse') {
	    if ($ENV{'form.f'.$i} ne 'none') {
		$fields{$keyfields[$i]}=$ENV{'form.f'.$i};
	    }
	} else {
	    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 ---------
#
#-------------------------------------------------------------------
#
#-------------- Next few routines handles grading by page/sequence
#
#--- Select a page/sequence and a student to grade
sub pickStudentPage {
    my ($request) = shift;

    $request->print(<<LISTJAVASCRIPT);
<script type="text/javascript" language="javascript">

function checkPickOne(formname) {
    if (radioSelection(formname.student) == null) {
	alert("Please select the student you wish to grade.");
	return;
    }
    var ptr = pullDownSelection(formname.selectpage);
    formname.page.value = eval("formname.page"+ptr+".value");
    formname.title.value = eval("formname.title"+ptr+".value");
    formname.submit();
}

function radioSelection(radioButton) {
    var selection=null;
    if (radioButton.length > 1) {
	for (var i=0; i<radioButton.length; i++) {
	    if (radioButton[i].checked) {
		return radioButton[i].value;
	    }
	}
    } else {
	if (radioButton.checked) return radioButton.value;
    }
    return selection;
}
    
function pullDownSelection(selectOne) {
    var selection="";
    if (selectOne.length > 1) {
	for (var i=0; i<selectOne.length; i++) {
	    if (selectOne[i].selected) {
		return selectOne[i].value;
	    }
	}
    } else {
	if (selectOne.selected) return selectOne.value;
    }
}
</script>
LISTJAVASCRIPT

    my ($symb,$url) = &get_symb_and_url($request);
    my $cdom      = $ENV{"course.$ENV{'request.course.id'}.domain"};
    my $cnum      = $ENV{"course.$ENV{'request.course.id'}.num"};
    my $getsec    = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};

    my $result='<h3><font color="#339933">&nbsp;'.
	'Manual Grading by Page or Sequence</font></h3>';

    $result.='<form action="/adm/grades" method="post" name="displayPage">'."\n";
    $result.='&nbsp;<b>Problems from:</b> <select name="selectpage">'."\n";
    my ($titles,$symbx) = &getSymbMap($request);
    my ($curpage,$type,$mapId) = ($symb =~ /(.*?\.(page|sequence))___(\d+)___/); 
    my $ctr=0;
    foreach (@$titles) {
	my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
	$result.='<option value="'.$ctr.'" '.
	    ($$symbx{$_} =~ /$curpage$/ ? 'selected="on"' : '').
	    '>'.$showtitle.'</option>'."\n";
	$ctr++;
    }
    $result.= '</select>'."<br>\n";
    $ctr=0;
    foreach (@$titles) {
	my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
	$result.='<input type="hidden" name="page'.$ctr.'" value="'.$$symbx{$_}.'" />'."\n";
	$result.='<input type="hidden" name="title'.$ctr.'" value="'.$showtitle.'" />'."\n";
	$ctr++;
    }
    $result.='<input type="hidden" name="page" />'."\n".
	'<input type="hidden" name="title" />'."\n";

    $result.='&nbsp;<b>View Problems: </b><input type="radio" name="vProb" value="no" checked /> no '."\n".
	'<input type="radio" name="vProb" value="yes" /> yes '."<br>\n";

    $result.='&nbsp;<b>Submission Details: </b>'.
	'<input type="radio" name="lastSub" value="none" /> none'."\n".
	'<input type="radio" name="lastSub" value="datesub" checked /> dates and submissions'."\n".
	'<input type="radio" name="lastSub" value="all" /> all details'."\n";

    $result.='<input type="hidden" name="section"     value="'.$getsec.'" />'."\n".
	'<input type="hidden" name="command" value="displayPage" />'."\n".
	'<input type="hidden" name="url"     value="'.$url.'" />'."\n".
	'<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
	'<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."<br />\n";

    $result.='&nbsp;<input type="button" '.
	'onClick="javascript:checkPickOne(this.form);"value="Submit" /><br />'."\n";

    $request->print($result);

    my $studentTable.='&nbsp;<b>Select a student you wish to grade</b><br>'.
	'<table border="0"><tr><td bgcolor="#777777">'.
	'<table border="0"><tr bgcolor="#e6ffff">'.
	'<td><b>&nbsp;Fullname <font color="#999999">(username)</font></b></td>'.
	'<td><b>&nbsp;Fullname <font color="#999999">(username)</font></b></td>'.
	'<td><b>&nbsp;Fullname <font color="#999999">(username)</font></b></td>'.
	'<td><b>&nbsp;Fullname <font color="#999999">(username)</font></b></td></tr>';
 
    my (undef,undef,$fullname) = &getclasslist($getsec,'1');
    my $ptr = 1;
    foreach my $student (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
	my ($uname,$udom) = split(/:/,$student);
	$studentTable.=($ptr%4 == 1 ? '<tr bgcolor="#ffffe6"><td>' : '</td><td>');
	$studentTable.='<input type="radio" name="student" value="'.$student.'" /> '.$$fullname{$student}.
	    '<font color="#999999"> ('.$uname.($udom eq $cdom ? '':':'.$udom).')</font>'."\n";
	$studentTable.=($ptr%4 == 0 ? '</td></tr>' : '');
	$ptr++;
    }
    $studentTable.='</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;' if ($ptr%4 == 2);
    $studentTable.='</td><td>&nbsp;</td><td>&nbsp;' if ($ptr%4 == 3);
    $studentTable.='</td><td>&nbsp;' if ($ptr%4 == 0);
    $studentTable.='</td></tr></table></td></tr></table>'."\n";
    $studentTable.='<br />&nbsp;<input type="button" '.
	'onClick="javascript:checkPickOne(this.form);"value="Submit" /></form>'."\n";

    $studentTable.=&show_grading_menu_form($symb,$url);
    $request->print($studentTable);

    return '';
}

sub getSymbMap {
    my ($request) = @_;
    my $navmap = Apache::lonnavmaps::navmap-> new($ENV{'request.course.fn'}.'.db',
						  $ENV{'request.course.fn'}.'_parms.db',1, 1);

    my $res = $navmap->firstResource(); # temp resource to access constants
    $navmap->init();

    # End navmap using boilerplate

    my $iterator = $navmap->getIterator(undef, undef, undef, 1);
    my $depth = 1;
    $iterator->next(); # ignore first BEGIN_MAP
    my $curRes = $iterator->next();

    my %symbx = ();
    my @titles = ();
    my $minder=0;
    while ($depth > 0) {
        if ($curRes == $iterator->BEGIN_MAP()) {$depth++;}
        if ($curRes == $iterator->END_MAP()) { $depth--; }

        if (ref($curRes) && $curRes->is_map()) {
	    my ($mapUrl, $id, $resUrl) = split(/___/, $curRes->symb()); # check map contains at least one problem
	    my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps

	    my $mapiterator = $navmap->getIterator($map->map_start(),
						   $map->map_finish());

	    my $mapdepth = 1;
	    my $countProblems = 0;
	    $mapiterator->next(); # skip the first BEGIN_MAP
	    my $mapcurRes = $mapiterator->next(); # for "current resource"
	    my $ctr=0;
	    while ($mapdepth > 0 && $ctr < 100) {
		if($mapcurRes == $mapiterator->BEGIN_MAP) { $mapdepth++; }
		if($mapcurRes == $mapiterator->END_MAP) { $mapdepth++; }

		if (ref($mapcurRes) && $mapcurRes->is_problem() && !$mapcurRes->randomout) {
		    $countProblems++;
		}
		$ctr++;
	    }
	    if ($countProblems > 0) {
		my $title = $curRes->compTitle();
		push @titles,$minder.'.'.$title; # minder, just in case two titles are identical
		$symbx{$minder.'.'.$title} = $curRes->symb();
		$minder++;
	    }
       }
        $curRes = $iterator->next();
    }

    $navmap->untieHashes();
    return \@titles,\%symbx;
}

#
#--- Displays a page/sequence w/wo problems, w/wo submissions
sub displayPage {
    my ($request) = shift;

    my ($symb,$url) = &get_symb_and_url($request);
    my $cdom      = $ENV{"course.$ENV{'request.course.id'}.domain"};
    my $cnum      = $ENV{"course.$ENV{'request.course.id'}.num"};
    my $getsec    = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};
    my $pageTitle = $ENV{'form.page'};
    my (undef,undef,$fullname) = &getclasslist($getsec,'1');
    my ($uname,$udom) = split(/:/,$ENV{'form.student'});

    my $result='<h3><font color="#339933">&nbsp;'.$ENV{'form.title'}.'</font></h3>';
    $result.='<h3>&nbsp;Student: '.$$fullname{$ENV{'form.student'}}.
	'<font color="#999999"> ('.$uname.($udom eq $cdom ? '':':'.$udom).')</font></h3>'."\n";

    &sub_page_js($request);
    $request->print($result);

    my $navmap = Apache::lonnavmaps::navmap-> new($ENV{'request.course.fn'}.'.db',
						  $ENV{'request.course.fn'}.'_parms.db',1, 1);
    my ($mapUrl, $id, $resUrl) = split(/___/, $ENV{'form.page'});
    my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps

    my $iterator = $navmap->getIterator($map->map_start(),
					$map->map_finish());

    my $studentTable='<form action="/adm/grades" method="post" name="gradePage">'."\n".
	'<input type="hidden" name="command" value="gradeByPage" />'."\n".
	'<input type="hidden" name="student" value="'.$ENV{'form.student'}.'" />'."\n".
	'<input type="hidden" name="page"    value="'.$pageTitle.'" />'."\n".
	'<input type="hidden" name="title"   value="'.$ENV{'form.title'}.'" />'."\n".
	'<input type="hidden" name="url"     value="'.$url.'" />'."\n".
	'<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
	'<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."\n";

    my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').
	'/check.gif" height="16" border="0" />';

    $studentTable.='&nbsp;<b>Note:</b> A problem graded correct ('.$checkIcon.
	') by the computer cannot be changed.'."\n".
	'<table border="0"><tr><td bgcolor="#777777">'.
	'<table border="0"><tr bgcolor="#e6ffff">'.
	'<td align="center"><b>&nbsp;No&nbsp;</b></td>'.
	'<td><b>&nbsp;'.($ENV{'form.vProb'} eq 'no' ? 'Title' : 'Problem View').'/Grade</b></td></tr>';

    my ($depth,$ctr,$question) = (1,0,1);
    $iterator->next(); # skip the first BEGIN_MAP
    my $curRes = $iterator->next(); # for "current resource"
    while ($depth > 0 && $ctr < 100) { # ctr, just in case it never gets out of loop
        if($curRes == $iterator->BEGIN_MAP) { $depth++; }
        if($curRes == $iterator->END_MAP) { $depth++; }

        if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) {
	    my $parts = $curRes->parts();
	    $parts = &temp_parts_fix($parts); # remove line when lonnavmap is fixed
            my $title = $curRes->compTitle();
	    my $symbx = $curRes->symb();
	    $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$question.
		(scalar(@{$parts}) == 1 ? '' : '<br>('.scalar(@{$parts}).'&nbsp;parts)').'</td>';
	    $studentTable.='<td valign="top">';
	    if ($ENV{'form.vProb'} eq 'yes') {
		$studentTable.=&show_problem($request,$symbx,$uname,$udom,1);
	    } else {
		my $companswer = &Apache::loncommon::get_student_answers(
									 $symbx,$uname,$udom,$ENV{'request.course.id'});
		$companswer =~ s|<form(.*?)>||g;
		$companswer =~ s|</form>||g;

#		while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>
#		    $request->print('match='.$1.'<br>');
#		    $companswer =~ s/$1/ /s;
#		}
#		$companswer =~ s/<table border=\"1\">/<table border=\"0\">/g;
		$studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br>&nbsp;<b>Correct answer:</b><br>'.$companswer;
	    }

	    my %record = &Apache::lonnet::restore($symbx,$ENV{'request.course.id'},$udom,$uname);

	    if ($ENV{'form.lastSub'} eq 'datesub') {
		if ($record{'version'} eq '') {
		    $studentTable.='<br />&nbsp;<font color="red">No recorded submission for this problem</font><br />';
		} else {
		    $studentTable.='<table border="0" width="100%"><tr><td bgcolor="#777777">'.
			'<table border="0" width="100%"><tr bgcolor="#e6ffff">'.
			'<td><b>Date/Time</b></td>'.
			'<td><b>Submission</b></td>'.
			'<td><b>Status&nbsp;</b></td></tr>';
		    my ($version);
		    for ($version=1;$version<=$record{'version'};$version++) {
			my $timestamp = scalar(localtime($record{$version.':timestamp'}));
			$studentTable.='<tr bgcolor="#ffffff" valign="top"><td>'.$timestamp.'</td>';
			my @versionKeys = split(/\:/,$record{$version.':keys'});
			my @displaySub = ();
			foreach my $partid (@{$parts}) {
			    my @matchKey = grep /^resource\.$partid\..*?\.submission$/,@versionKeys;
			    next if ($record{"$version:resource.$partid.solved"} eq '');
#			    next if ($record{"$version:resource.$partid.award"} eq 'APPROX_ANS' && 
#				     $record{"$version:resource.$partid.solved"} eq '');
			    $displaySub[0].=(exists $record{$version.':'.$matchKey[0]}) ? 
				'<b>Part&nbsp;'.$partid.'&nbsp;'.
				($record{"$version:resource.$partid.tries"} eq '' ? 'Trial&nbsp;not&nbsp;counted' :
				'Trial&nbsp;'.$record{"$version:resource.$partid.tries"}).'</b>&nbsp; '.
				$record{$version.':'.$matchKey[0]}.'<br />' : '';
			    $displaySub[1].=(exists $record{"$version:resource.$partid.award"}) ?
				'<b>Part&nbsp;'.$partid.'</b> &nbsp;'.
				$record{"$version:resource.$partid.award"}.'/'.
				$record{"$version:resource.$partid.solved"}.'<br />' : '';
			    $displaySub[2].=(exists $record{"$version:resource.$partid.regrader"}) ?
				$record{"$version:resource.$partid.regrader"}.' (<b>Part:</b> '.$partid.')' : '';
			}
			$displaySub[2].=(exists $record{"$version:resource.regrader"}) ?
			    $record{"$version:resource.regrader"} : '';
			$studentTable.='<td>'.$displaySub[0].'&nbsp;</td><td>'.$displaySub[1].
			    ($displaySub[2] eq '' ? '' : 'Manually graded by '.$displaySub[2]).'&nbsp;</td></tr>';
		    }
		    $studentTable.='</table></td></tr></table>';
		}
	    } elsif ($ENV{'form.lastSub'} eq 'all') {
		my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : '');
		$studentTable.=&Apache::loncommon::get_previous_attempt($symbx,$uname,$udom,
									$ENV{'request.course.id'},
									'','.submission');
 
	    }

	    foreach my $partid (@{$parts}) {
		$studentTable.=&gradeBox($request,$symbx,$uname,$udom,$question,$partid,\%record);
		$studentTable.='<input type="hidden" name="q_'.$question.'" value="'.$partid.'" />'."\n";
		$question++;
	    }
	    $studentTable.='</td></tr>';

       }
        $curRes = $iterator->next();
	$ctr++;
    }

    $studentTable.='</td></tr></table></td></tr></table>'."\n".
	'&nbsp;&nbsp;<input type="button" value="Save" '.
	'onClick="javascript:checkSubmitPage(this.form,'.$question.');" TARGET=_self />'.
	'</form>'."\n";
    $studentTable.=&show_grading_menu_form($symb,$url);
    $request->print($studentTable);

    return '';
}

sub temp_parts_fix { #remove sub once lonnavmap is fixed
    my $parts = shift;
    my %seen = ();
    my @correctParts = ();
    foreach (@{$parts}) {
	next if ($seen{$_} > 0);
	$seen{$_}++;
	push @correctParts,$_;
    }
    return \@correctParts;
}

sub updateGradeByPage {
    my ($request) = shift;

    my $cdom      = $ENV{"course.$ENV{'request.course.id'}.domain"};
    my $cnum      = $ENV{"course.$ENV{'request.course.id'}.num"};
    my $getsec    = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};
    my $pageTitle = $ENV{'form.page'};
    my (undef,undef,$fullname) = &getclasslist($getsec,'1');
    my ($uname,$udom) = split(/:/,$ENV{'form.student'});

    my $result='<h3><font color="#339933">&nbsp;'.$ENV{'form.title'}.'</font></h3>';
    $result.='<h3>&nbsp;Student: '.$$fullname{$ENV{'form.student'}}.
	'<font color="#999999"> ('.$uname.($udom eq $cdom ? '':':'.$udom).')</font></h3>'."\n";

    $request->print($result);

    my $navmap = Apache::lonnavmaps::navmap-> new($ENV{'request.course.fn'}.'.db',
						  $ENV{'request.course.fn'}.'_parms.db',1, 1);
    my ($mapUrl, $id, $resUrl) = split(/___/, $ENV{'form.page'});
    my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps

    my $iterator = $navmap->getIterator($map->map_start(),
					$map->map_finish());

    my $studentTable='<table border="0"><tr><td bgcolor="#777777">'.
	'<table border="0"><tr bgcolor="#e6ffff">'.
	'<td align="center"><b>&nbsp;No&nbsp;</b></td>'.
	'<td><b>&nbsp;Title&nbsp;</b></td>'.
	'<td><b>&nbsp;Previous Score&nbsp;</b></td>'.
	'<td><b>&nbsp;New Score&nbsp;</b></td></tr>';

    $iterator->next(); # skip the first BEGIN_MAP
    my $curRes = $iterator->next(); # for "current resource"
    my ($depth,$ctr,$question,$changeflag)= (1,0,1,0);
    while ($depth > 0 && $ctr < 100) { # ctr, just in case it never gets out of loop
        if($curRes == $iterator->BEGIN_MAP) { $depth++; }
        if($curRes == $iterator->END_MAP) { $depth++; }

        if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) {
	    my $parts = $curRes->parts();
	    $parts = &temp_parts_fix($parts); # remove line when lonnavmap is fixed
            my $title = $curRes->compTitle();
	    my $symbx = $curRes->symb();
	    $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$question.
		(scalar(@{$parts}) == 1 ? '' : '<br>('.scalar(@{$parts}).'&nbsp;parts)').'</td>';
	    $studentTable.='<td valign="top">&nbsp;<b>'.$title.'</b>&nbsp;</td>';

	    my %newrecord=();
	    my @displayPts=();
	    foreach my $partid (@{$parts}) {
		my $newpts = $ENV{'form.GD_BOX'.$question.'_'.$partid};
		my $oldpts = $ENV{'form.oldpts'.$question.'_'.$partid};

		my $wgt = $ENV{'form.WGT'.$question.'_'.$partid} != 0 ? 
		    $ENV{'form.WGT'.$question.'_'.$partid} : 1;
		my $partial = $newpts/$wgt;
		my $score;
		if ($partial > 0) {
		    $score = 'correct_by_override';
		} elsif ($partial == 0) {
		    $score = 'incorrect_by_override';
		}
		if ($ENV{'form.GD_SEL'.$question.'_'.$partid} eq 'excused') {
		    $partial = '';
		    $score = 'excused';
		}
		my $oldstatus = $ENV{'form.solved'.$question.'_'.$partid};
		$displayPts[0].='&nbsp;<b>Part</b> '.$partid.' = '.
		    (($oldstatus eq 'excused') ? 'excused' : $oldpts).
		    '&nbsp;<br>';
		$displayPts[1].='&nbsp;<b>Part</b> '.$partid.' = '.
		    ($oldstatus eq 'correct_by_student' ? $oldpts :
		     (($score eq 'excused') ? 'excused' : $newpts)).
		    '&nbsp;<br>';

		$question++;
		if (($oldstatus eq 'correct_by_student') ||
		    ($newpts eq $oldpts && $score eq $oldstatus))
		{
		    next;
		}
		$newrecord{'resource.'.$partid.'.awarded'}  = $partial if $partial ne '';
		$newrecord{'resource.'.$partid.'.solved'}   = $score;
		$newrecord{'resource.'.$partid.'.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";

		$changeflag++;
	    }
	    if (scalar(keys(%newrecord)) > 0) {
		&Apache::lonnet::cstore(\%newrecord,$symbx,$ENV{'request.course.id'},
					$udom,$uname);
	    }
	    $studentTable.='<td valign="top">'.$displayPts[0].'</td>'.
		'<td valign="top">'.$displayPts[1].'</td>'.
		'</tr>';

	}
        $curRes = $iterator->next();
	$ctr++;
    }

    $studentTable.='</td></tr></table></td></tr></table>';
    $studentTable.=&show_grading_menu_form($ENV{'form.symb'},$ENV{'form.url'});
    my $grademsg=($changeflag == 0 ? 'No score was changed or updated.' :
		  'The scores were changed for '.
		  $changeflag.' problem'.($changeflag == 1 ? '.' : 's.'));
    $request->print($grademsg.$studentTable);

    return '';
}

#-------- end of section for handling grading by page/sequence ---------
#
#-------------------------------------------------------------------

#--------------------Scantron Grading-----------------------------------
#
#------ start of section for handling grading by page/sequence ---------

sub defaultFormData {
    my ($symb,$url)=@_;
    return '
      <input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
     '<input type="hidden" name="url"     value="'.$url.'" />'."\n".
     '<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."\n".
     '<input type="hidden" name="probTitle" value="'.$ENV{'form.probTitle'}.'" />'."\n";
}

sub getSequenceDropDown {
    my ($request,$symb)=@_;
    my $result='<select name="selectpage">'."\n";
    my ($titles,$symbx) = &getSymbMap($request);
    my ($curpage,$type,$mapId) = ($symb =~ /(.*?\.(page|sequence))___(\d+)___/); 
    my $ctr=0;
    foreach (@$titles) {
	my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
	$result.='<option value="'.$$symbx{$_}.'" '.
	    ($$symbx{$_} =~ /$curpage$/ ? 'selected="on"' : '').
	    '>'.$showtitle.'</option>'."\n";
	$ctr++;
    }
    $result.= '</select>';
    return $result;
}

sub scantron_uploads {
    if (!-e $Apache::lonnet::perlvar{'lonScansDir'}) { return ''};
    my $result=	'<select name="scantron_selectfile">';
    opendir(DIR,$Apache::lonnet::perlvar{'lonScansDir'});
    my @files=sort(readdir(DIR));
    foreach my $filename (@files) {
	if ($filename eq '.' or $filename eq '..') { next; }
	$result.="<option>$filename</option>\n";
    }
    closedir(DIR);
    $result.="</select>";
    return $result;
}

sub scantron_scantab {
    my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
    my $result='<select name="scantron_format">'."\n";
    foreach my $line (<$fh>) {
	my ($name,$descrip)=split(/:/,$line);
	if ($name =~ /^\#/) { next; }
	$result.='<option value="'.$name.'">'.$descrip.'</option>'."\n";
    }
    $result.='</select>'."\n";

    return $result;
}

sub scantron_selectphase {
    my ($r) = @_;
    my ($symb,$url)=&get_symb_and_url($r);
    if (!$symb) {return '';}
    my $sequence_selector=&getSequenceDropDown($r,$symb);
    my $default_form_data=&defaultFormData($symb,$url);
    my $grading_menu_button=&show_grading_menu_form($symb,$url);
    my $file_selector=&scantron_uploads();
    my $format_selector=&scantron_scantab();
    my $result;
    $result.= <<SCANTRONFORM;
<form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantro_process">
  <input type="hidden" name="command" value="scantron_process" />
  $default_form_data
  <table width="100%" border="0">
    <tr>
      <td bgcolor="#777777">
        <table width="100%" border="0">
          <tr bgcolor="#e6ffff">
            <td>
              &nbsp;<b>Specify file location and which Folder/Sequence to grade</b>
            </td>
          </tr>
          <tr bgcolor="#ffffe6">
            <td>
               Sequence to grade: $sequence_selector
	    </td>
          </tr>
          <tr bgcolor="#ffffe6">
            <td>
		Filename of scoring office file: $file_selector
	    </td>
          </tr>
          <tr bgcolor="#ffffe6">
            <td>
              Format of data file: $format_selector
	    </td>
          </tr>
        </table>
      </td>
    </tr>
  </table>
  <input type="submit" value="Submit" />
</form>
$grading_menu_button
SCANTRONFORM

    return $result;
}

sub get_scantron_config {
    my ($which) = @_;
    my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
    my %config;
    foreach my $line (<$fh>) {
	my ($name,$descrip)=split(/:/,$line);
	if ($name ne $which ) { next; }
	chomp($line);
	my @config=split(/:/,$line);
	$config{'name'}=$config[0];
	$config{'description'}=$config[1];
	$config{'CODElocation'}=$config[2];
	$config{'CODEstart'}=$config[3];
	$config{'CODElength'}=$config[4];
	$config{'IDstart'}=$config[5];
	$config{'IDlength'}=$config[6];
	$config{'Qstart'}=$config[7];
	$config{'Qlength'}=$config[8];
	$config{'Qoff'}=$config[9];
	$config{'Qon'}=$config[10];
	last;
    }
    return %config;
}

sub username_to_idmap {
    my ($classlist)= @_;
    my %idmap;
    foreach my $student (keys(%$classlist)) {
	$idmap{$classlist->{$student}->[&Apache::loncoursedata::CL_ID]}=
	    $student;
    }
    return %idmap;
}

sub scantron_parse_scanline {
    my ($line,$scantron_config)=@_;
    my %record;
    my $questions=substr($line,$$scantron_config{'Qstart'}-1);
    my $data=substr($line,0,$$scantron_config{'Qstart'}-1);
    if ($$scantron_config{'CODElocation'} ne 0) {
	if ($$scantron_config{'CODElocation'} < 0) {
	    $record{'scantron.CODE'}=substr($data,$$scantron_config{'CODEstart'}-1,
					    $$scantron_config{'CODElength'});
	} else {
	    #FIXME interpret first N questions
	}
    }
    $record{'scantron.ID'}=substr($data,$$scantron_config{'IDstart'}-1,
				  $$scantron_config{'IDlength'});
    my @alphabet=('A'..'Z');
    my $questnum=0;
    while ($questions) {
	$questnum++;
	my $currentquest=substr($questions,0,$$scantron_config{'Qlength'});
	substr($questions,0,$$scantron_config{'Qlength'})='';
	if (length($currentquest) < $$scantron_config{'Qlength'}) { next; }
	my (@array)=split(/$$scantron_config{'Qon'}/,$currentquest);
	if (scalar(@array) gt 2) {
	    #FIXME do something intelligent with double bubbles
	    Apache->request->print("<br ><b>Wha!!!</b> <pre>".scalar(@array).
				   '-'.$currentquest.'-'.$questnum.'</pre><br />');
	}
	if (length($array[0]) eq $$scantron_config{'Qlength'}) {
	    $record{"scantron.$questnum.answer"}='';
	} else {
	    $record{"scantron.$questnum.answer"}=$alphabet[length($array[0])];
	}
    }
    $record{'scantron.maxquest'}=$questnum;
    return \%record;
}

sub scantron_add_delay {
}

sub scantron_find_student {
    my ($scantron_record,$idmap)=@_;
    my $scanID=$$scantron_record{'scantron.ID'};
    foreach my $id (keys(%$idmap)) {
	Apache->request->print('<pre>checking studnet -'.$id.'- againt -'.$scanID.'- </pre>');
	if (lc($id) eq lc($scanID)) { Apache->request->print('success');return $$idmap{$id}; }
    }
    return undef;
}

sub scantron_filter {
    my ($curres)=@_;
    if (ref($curres) && $curres->is_problem() && !$curres->randomout) {
	return 1;
    }
    return 0;
}

sub scantron_process_students {
    my ($r) = @_;
    my (undef,undef,$sequence)=split(/___/,$ENV{'form.selectpage'});
    my ($symb,$url)=&get_symb_and_url($r);
    if (!$symb) {return '';}
    my $default_form_data=&defaultFormData($symb,$url);

    my %scantron_config=&get_scantron_config($ENV{'form.scantron_format'});
    my $scanlines=Apache::File->new($Apache::lonnet::perlvar{'lonScansDir'}."/$ENV{'form.scantron_selectfile'}");
    my @scanlines=<$scanlines>;
    my $classlist=&Apache::loncoursedata::get_classlist();
    my %idmap=&username_to_idmap($classlist);
    my $navmap=Apache::lonnavmaps::navmap->new($ENV{'request.course.fn'}.'.db',$ENV{'request.course.fn'}.'_parms.db',1, 1);
    my $map=$navmap->getResourceByUrl($sequence);
    my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
    $r->print("geto ".scalar(@resources)."<br />");
    my $result= <<SCANTRONFORM;
<form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload">
  <input type="hidden" name="command" value="scantron_configphase" />
  $default_form_data
SCANTRONFORM
    $r->print($result);

    my @delayqueue;
    my $totalcorrect;
    my $totalincorrect;

    my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,
	           'Scantron Status','Scantron Progress',scalar(@scanlines));
    foreach my $line (@scanlines) {
	my $studentcorrect;
	my $studentincorrect;

	chomp($line);
	my $scan_record=&scantron_parse_scanline($line,\%scantron_config);
	my ($uname,$udom);
	if ($uname=&scantron_find_student($scan_record,\%idmap)) {
	    &scantron_add_delay(\@delayqueue,$line,
				'Unable to find a student that matches');
	}
	$r->print('<pre>doing studnet'.$uname.'</pre>');
	($uname,$udom)=split(/:/,$uname);
	&Apache::lonnet::delenv('form.counter');
	&Apache::lonnet::appenv(%$scan_record);
#    &Apache::lonhomework::showhash(%ENV);
    $Apache::lonxml::debug=1;
	&Apache::lonxml::debug("line is $line");
	
	    my $i=0;
	foreach my $resource (@resources) {
	    $i++;
	    my $result=&Apache::lonnet::ssi($resource->src(),
				 ('submitted'     =>'scantron',
				  'grade_target'  =>'grade',
				  'grade_username'=>$uname,
				  'grade_domain'  =>$udom,
				  'grade_courseid'=>$ENV{'request.course.id'},
				  'grade_symb'    =>$resource->symb()));
	    my %score=&Apache::lonnet::restore($resource->symb(),
					       $ENV{'request.course.id'},
					       $udom,$uname);
	    foreach my $part ($resource->{PARTS}) {
		if ($score{'resource.'.$part.'.solved'} =~ /^correct/) {
		    $studentcorrect++;
		    $totalcorrect++;
		} else {
		    $studentincorrect++;
		    $totalincorrect++;
		}
	    }
	    $r->print('<pre>'.
		      $resource->symb().'-'.
		      $resource->src().'-'.'</pre>result is'.$result);
	    &Apache::lonhomework::showhash(%score);
	#    if ($i eq 3) {last;}
	}
	&Apache::lonnet::delenv('form.counter');
	&Apache::lonnet::delenv('scantron\.');
	&Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
             'last student Who got a '.$studentcorrect.' correct and '.
	     $studentincorrect.' incorrect. The class has gotten '.
             $totalcorrect.' correct and '.$totalincorrect.' incorrect');
	last;
	#FIXME
	#get iterator for $sequence
	#foreach question 'submit' the students answer to the server
	#   through grade target {
	#   generate data to pass back that includes grade recevied
	#}
    }
    $Apache::lonxml::debug=0;
    foreach my $delay (@delayqueue) {
	#FIXME
	#print out each delayed student with interface to select how
	#  to repair student provided info
	#Expected errors include
	#  1 bad/no stuid/username
	#  2 invalid bubblings
	
    }
    #FIXME
    # if delay queue exists 2 submits one to process delayed students one
    #     to ignore delayed students, possibly saving the delay queue for later
    
    $navmap->untieHashes();
}
#-------- end of section for handling grading scantron forms -------
#
#-------------------------------------------------------------------


#-------------------------- 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="saveState"  value="'.$ENV{'form.saveState'}.'" />'."\n".
	'<input type="hidden" name="command" value="gradingmenu" />'."\n".
	'<input type="submit" name="submit" value="Grading Menu" />'."\n".
	'</form>'."\n";
    return $result;
}

# -- Retrieve choices for grading form
sub savedState {
    my %savedState = ();
    if ($ENV{'form.saveState'}) {
	foreach (split(/:/,$ENV{'form.saveState'})) {
	    my ($key,$value) = split(/=/,$_,2);
	    $savedState{$key} = $value;
	}
    }
    return \%savedState;
}

#--- Displays the main menu page -------
sub gradingmenu {
    my ($request) = @_;
    my ($symb,$url)=&get_symb_and_url($request);
    if (!$symb) {return '';}
    my $probTitle = &Apache::lonnet::gettitle($symb);

    $request->print(<<GRADINGMENUJS);
<script type="text/javascript" language="javascript">
    function checkChoice(formname) {
	var cmd = formname.command;
	formname.saveState.value = "saveCmd="+radioSelection(cmd)+":saveSec="+pullDownSelection(formname.section)+
	    ":saveSub="+radioSelection(formname.submitonly)+":saveStatus="+pullDownSelection(formname.status);
	if (cmd[0].checked || cmd[1].checked || cmd[2].checked || cmd[4].checked) formname.submit();

	if (cmd[3].checked) browseAndUpload();

	if (cmd[5].checked) {
	    if (!checkReceiptNo(formname,'notOK')) { return false;}
	    formname.submit();
	}
    }

    function checkReceiptNo(formname,nospace) {
	var receiptNo = formname.receipt.value;
	var checkOpt = false;
	if (nospace == "OK" && isNaN(receiptNo)) {checkOpt = true;}
	if (nospace == "notOK" && (isNaN(receiptNo) || receiptNo == "")) {checkOpt = true;}
	if (checkOpt) {
	    alert("Please enter a receipt number given by a student in the receipt box.");
	    formname.receipt.value = "";
	    formname.receipt.focus();
	    return false;
	}
	formname.command[5].checked = true;
	return true;
    }

    function radioSelection(radioButton) {
	var selection=null;
	if (radioButton.length > 1) {
	    for (var i=0; i<radioButton.length; i++) {
		if (radioButton[i].checked) {
		    return radioButton[i].value;
		}
	    }
	} else {
	    if (radioButton.checked) return radioButton.value;
	}
	return selection;
    }

    function pullDownSelection(selectOne) {
	var selection="";
	if (selectOne.length > 1) {
	    for (var i=0; i<selectOne.length; i++) {
		if (selectOne[i].selected) {
		    return selectOne[i].value;
		}
	    }
	} else {
	    if (selectOne.selected) return selectOne.value;
	}
    }

    function browseAndUpload() {
	bNLoad = window.open('', 'BrowseAndUpload', 'toolbar=no,location=no,scrollbars=no,width=550,height=200,screenx=100,screeny=75');
	bNLoad.focus();
	var lDoc = bNLoad.document;
	lDoc.write("<html><head>");
	lDoc.write("<title>Browse And Upload</title>");
	
	lDoc.write("<script language=javascript>");
	lDoc.write("function checkUpload(formname) {");
	
	lDoc.write("  if (formname.upfile.value == \\"\\") {");
	lDoc.write("     alert(\\"Please use the browse button to select a file from your local directory.\\");");
	lDoc.write("     return false;");
	lDoc.write("   }");
	lDoc.write("   var openformname = opener.document.gradingMenu;");
	lDoc.write("   formname.saveState.value = \\"saveCmd=\\"+opener.radioSelection(openformname.command)+\\":saveSec=\\"+opener.pullDownSelection(openformname.section)+\\":saveSub=\\"+opener.radioSelection(openformname.submitonly)+\\":saveStatus=\\"+opener.pullDownSelection(openformname.status);");
	lDoc.write("   document.gradesupload.submit();");
	lDoc.write("   if (navigator.appName !=\\"Netscape\\") {self.close()};");
	lDoc.write("   setTimeout('self.close()',750)"); 
	lDoc.write("}");

	lDoc.write("<");
	lDoc.write("/script>");

	lDoc.write("</head><body bgcolor=white>");
	lDoc.write("<form method=\\"post\\" enctype=\\"multipart/form-data\\" action=\\"/adm/grades\\" name=\\"gradesupload\\" target=\\"LONcatInfo\\">");
	lDoc.write("<input type=\\"hidden\\" name=\\"symb\\"       value=\\"$symb\\">");
	lDoc.write("<input type=\\"hidden\\" name=\\"url\\"        value=\\"$url\\">");
	lDoc.write("<input type=\\"hidden\\" name=\\"probTitle\\"  value=\\"$probTitle\\">");
	lDoc.write("<input type=\\"hidden\\" name=\\"saveState\\"  value=\\"\\">");
	lDoc.write("<input type=\\"hidden\\" name=\\"command\\"    value=\\"csvuploadmap\\">");

	lDoc.write("<font color=\\"green\\" size=+1>&nbsp;<b>Specify a file containing the class scores for problem - $probTitle</b></font><br><br>");

	lDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
	lDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
	lDoc.write("<td>");
	lDoc.write("<input type=\\"file\\" name=\\"upfile\\" size=\\"50\\" />");
	lDoc.write("<br />Type: <select name=\\"upfiletype\\">");
        lDoc.write("<option value=\\"csv\\">CSV (comma separated values, spreadsheet)</option>");
        lDoc.write("<option value=\\"space\\">Space separated</option>");
        lDoc.write("<option value=\\"tab\\">Tabulator separated</option>");
        lDoc.write("<option value=\\"xml\\">HTML/XML</option>");
        lDoc.write("</select>");
        lDoc.write("</td></tr></table>");
        lDoc.write("</td></tr></table>&nbsp;");
        lDoc.write("<input type=\\"button\\" value=\\"Upload Scores\\" onClick=\\"javascript:checkUpload(this.form)\\">&nbsp;&nbsp;");
        lDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br><br>");
        lDoc.write("</form>");
        lDoc.write("</body></html>");
  }
</script>
GRADINGMENUJS

    my $result='<h3>&nbsp;<font color="#339933">Manual Grading/View Submission</font></h3>'.
	'<table border="0">'.
	'<tr><td colspan=3><font size=+1><b>Problem: </b>'.$probTitle.'</font></td></tr>'."\n";
    my ($partlist,$handgrade) = &response_type($url);
    my ($resptype,$hdgrade)=('','no');
    for (sort keys(%$handgrade)) {
	my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});
	$resptype = $responsetype;
	$hdgrade = $handgrade if ($handgrade eq 'yes');
	$result.='<tr><td><b>Part </b>'.(split(/_/))[0].'</td>'.
	    '<td><b>Type: </b>'.$responsetype.'</td>'.
	    '<td><b>Handgrade: </b>'.$handgrade.'</font></td></tr>';
    }
    $result.='</table>'."\n";

    my (undef,$sections) = &getclasslist('all','0');
    my $savedState = &savedState();
    my $saveCmd = ($$savedState{'saveCmd'} eq '' ? 'pickStudentPage' : $$savedState{'saveCmd'});
    my $saveSec = ($$savedState{'saveSec'} eq '' ? 'all' : $$savedState{'saveSec'});
    my $saveSub = ($$savedState{'saveSub'} eq '' ? 'yes' : $$savedState{'saveSub'});
    my $saveStatus = ($$savedState{'saveStatus'} eq '' ? 'Active' : $$savedState{'saveStatus'});

    $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
	'<input type="hidden" name="symb"        value="'.$symb.'" />'."\n".
	'<input type="hidden" name="url"         value="'.$url.'" />'."\n".
	'<input type="hidden" name="response"    value="'.$resptype.'" />'."\n".
	'<input type="hidden" name="handgrade"   value="'.$hdgrade.'" />'."\n".
	'<input type="hidden" name="probTitle"   value="'.$probTitle.'" />'."\n".
	'<input type="hidden" name="saveState"   value="" />'."\n".
	'<input type="hidden" name="showgrading" value="yes" />'."\n";

    $result.='<table width=100% border=0><tr><td bgcolor=#777777>'."\n".
	'<table width=100% border=0><tr bgcolor="#e6ffff"><td>'."\n".
	'&nbsp;<b>Select a Grading/Viewing Option</b></td></tr>'."\n".
	'<tr bgcolor=#ffffe6><td>'."\n";

    $result.='<table width=100% border=0>'.
	'<tr bgcolor="#ffffe6" valign="top"><td colspan="2">'.
	'<input type="radio" name="command" value="pickStudentPage" '.
	($saveCmd eq 'pickStudentPage' ? 'checked' : '').'> '.
	'Handgrade/View Submission for a student by page/sequence</td></tr>'."\n".

	'<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.
	'<input type="radio" name="command" value="viewgrades" '.
	($saveCmd eq 'viewgrades' ? 'checked' : '').'> '.
	'Grade by section or class</td></tr>'."\n".

	'<tr bgcolor="#ffffe6"valign="top"><td><input type="radio" name="command" value="submission" '.
	($saveCmd eq 'submission' ? 'checked' : '').'> '.
	($hdgrade eq 'yes' ? 'View/Grade essay response of' : 'View').
	' an individual student </td>'."\n".
	'<td>-->&nbsp;For students who has: '.
	'<input type="radio" name="submitonly" value="yes" '.
	($saveSub eq 'yes' ? 'checked' : '').' /> submitted'.
	'<input type="radio" name="submitonly" value="all" '.
	($saveSub eq 'all' ? 'checked' : '').' /> everybody</td></tr>'."\n".

	'<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.
	'<input type="radio" name="command" value="csvupload" '.
	($saveCmd eq 'csvupload' ? 'checked' : '').'> '.
	'Upload scores from file</td></tr>'."\n";

    $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.
	'<input type="radio" name="command" value="scantron_selectphase" '.
	($saveCmd eq 'scantron_selectphase' ? 'checked="on"' : '').' /> '.
        'Grade scantron forms</td></tr>'."\n";

    if ((&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'})) && ($symb)) {
	$result.='<tr bgcolor="#ffffe6"valign="top"><td>'.
	    '<input type="radio" name="command" value="verify" onChecked="javascript:this.form.receipt.focus()" '.
	    ($saveCmd eq 'verify' ? 'checked' : '').'> '.
	    'Verify a submission receipt issued by this server</td>'.
	    '<td>-->&nbsp;Receipt no: '.unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}).
	    '-<input type="text" name="receipt" size="4" onChange="javascript:checkReceiptNo(this.form,\'OK\')">'.
	    '</td></tr>'."\n";
    } 

    $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2"><br />'."\n".
	'&nbsp;Select section: <select name="section">'."\n";
    if (ref($sections)) {
	foreach (sort (@$sections)) {$result.='<option value="'.$_.'" '.
					 ($saveSec eq $_ ? 'selected="on"' : '').'>'.$_.'</option>'."\n";}
    }
    $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="on"' : ''). '>all</select> &nbsp; ';

    $result.='Student Status:</b><select name="status">'.
	'<option value="Active" '.($saveStatus eq 'Active' ? 'selected' : '').'>Active</option>'.
	'<option value="Expired" '.($saveStatus eq 'Expired' ? 'selected' : '').'>Expired</option>'.
	'<option value="Any" '.($saveStatus eq 'Any' ? 'selected' : '').'>Any</option>'.
	'</select>';

    $result.=' &nbsp; <font color="red">(Applies to the first three options only.)</font>'."\n";

    if (ref($sections)) {
	$result.='&nbsp;(Section "no" implies the students were not assigned a section.)<br />' 
	    if (grep /no/,@$sections);
    }
    $result.='</td></tr>';

    $result.='<tr bgcolor="#ffffe6"><td colspan="2"><br />'.
	'<input type="button" onClick="javascript:checkChoice(this.form);" value="View/Grade" />'."\n".
	'</form></td></tr></table>'."\n".
	'</td></tr></table>'."\n".
	'</td></tr></table>'."\n";
    return $result;
}

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 '' 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('<h3>Not authorized: '.$token.'</h3>');
		    }           
		} else {
		    $request->print('<h3>Not a valid DocID: '.$token.'</h3>');
		}
	    } else {
		$request->print(&Apache::lonxml::tokeninputfield());
	    }
	}
    } else {
	$Apache::grades::viewgrades=&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'});
	if ($command eq 'submission') {
	    ($ENV{'form.student'} eq '' ? &listStudents($request) : &submission($request,0,0));
	} elsif ($command eq 'pickStudentPage') {
	    &pickStudentPage($request);
	} elsif ($command eq 'displayPage') {
	    &displayPage($request);
	} elsif ($command eq 'gradeByPage') {
	    &updateGradeByPage($request);
	} 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 'csvform') {
	    $request->print(&upcsvScores_form($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));
	    }
	} elsif ($command eq 'scantron_selectphase') {
	    $request->print(&scantron_selectphase($request));
	} elsif ($command eq 'scantron_process') {
	    $request->print(&scantron_process_students($request));
	} else {
	    $request->print("Unknown action: $command:");
	}
    }
    &send_footer($request);
    return '';
}

sub send_header {
    my ($request)= @_;
    $request->print(&Apache::lontexconvert::header());
#  $request->print("
#<script>
#remotewindow=open('','homeworkremote');
#remotewindow.close();
#</script>"); 
    $request->print(&Apache::loncommon::bodytag('Grading'));
}

sub send_footer {
    my ($request)= @_;
    $request->print('</body>');
    $request->print(&Apache::lontexconvert::footer());
}

1;

__END__;

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>
500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.