File:  [LON-CAPA] / loncom / homework / grades.pm
Revision 1.124: download - view: text, annotated - select for diffs
Wed Jul 23 18:04:51 2003 UTC (20 years, 10 months ago) by ng
Branches: MAIN
CVS tags: HEAD
fix bug 1977 - remove ability to select the student status on the list of
students if coming from grading page - this assumes that you are happy with
the original choice.
The ability to select the student status remains if viewing the list of
students when clicking on the SUBM button on homework page.

    1: # The LearningOnline Network with CAPA
    2: # The LON-CAPA Grading handler
    3: #
    4: # $Id: grades.pm,v 1.124 2003/07/23 18:04:51 ng Exp $
    5: #
    6: # Copyright Michigan State University Board of Trustees
    7: #
    8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
    9: #
   10: # LON-CAPA is free software; you can redistribute it and/or modify
   11: # it under the terms of the GNU General Public License as published by
   12: # the Free Software Foundation; either version 2 of the License, or
   13: # (at your option) any later version.
   14: #
   15: # LON-CAPA is distributed in the hope that it will be useful,
   16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   18: # GNU General Public License for more details.
   19: #
   20: # You should have received a copy of the GNU General Public License
   21: # along with LON-CAPA; if not, write to the Free Software
   22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   23: #
   24: # /home/httpd/html/adm/gpl.txt
   25: #
   26: # http://www.lon-capa.org/
   27: #
   28: # 2/9,2/13 Guy Albertelli
   29: # 6/8 Gerd Kortemeyer
   30: # 7/26 H.K. Ng
   31: # 8/20 Gerd Kortemeyer
   32: # Year 2002
   33: # June-August H.K. Ng
   34: # Year 2003
   35: # February, March H.K. Ng
   36: #
   37: 
   38: package Apache::grades;
   39: use strict;
   40: use Apache::style;
   41: use Apache::lonxml;
   42: use Apache::lonnet;
   43: use Apache::loncommon;
   44: use Apache::lonhtmlcommon;
   45: use Apache::lonnavmaps;
   46: use Apache::lonhomework;
   47: use Apache::loncoursedata;
   48: use Apache::lonmsg qw(:user_normal_msg);
   49: use Apache::Constants qw(:common);
   50: use String::Similarity;
   51: 
   52: my %oldessays=();
   53: my %perm=();
   54: 
   55: # ----- These first few routines are general use routines.----
   56: #
   57: # --- Retrieve the parts that matches stores_\d+ from the metadata file.---
   58: sub getpartlist {
   59:     my ($url) = @_;
   60:     my @parts =();
   61:     my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));
   62:     foreach my $key (@metakeys) {
   63: 	if ( $key =~ m/stores_(\w+)_.*/) {
   64: 	    push(@parts,$key);
   65: 	}
   66:     }
   67:     return @parts;
   68: }
   69: 
   70: # --- Get the symbolic name of a problem and the url
   71: sub get_symb_and_url {
   72:     my ($request) = @_;
   73:     (my $url=$ENV{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
   74:     my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));
   75:     if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
   76:     return ($symb,$url);
   77: }
   78: 
   79: # --- Retrieve the fullname for a user. Return lastname, first middle ---
   80: # --- Generation is attached next to the lastname if it exists. ---
   81: sub get_fullname {
   82:     my ($uname,$udom) = @_;
   83:     my %name=&Apache::lonnet::get('environment', ['lastname','generation',
   84: 						  'firstname','middlename'],
   85:                                   $udom,$uname);
   86:     my $fullname;
   87:     my ($tmp) = keys(%name);
   88:     if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
   89:         $fullname = &Apache::loncoursedata::ProcessFullName
   90:             (@name{qw/lastname generation firstname middlename/});
   91:     } else {
   92:         &Apache::lonnet::logthis('grades.pm: no name data for '.$uname.
   93:                                  '@'.$udom.':'.$tmp);
   94:     }
   95:     return $fullname;
   96: }
   97: 
   98: #--- Get the partlist and the response type for a given problem. ---
   99: #--- Indicate if a response type is coded handgraded or not. ---
  100: sub response_type {
  101:     my ($url) = shift;
  102:     my $allkeys = &Apache::lonnet::metadata($url,'keys');
  103:     my %seen = ();
  104:     my (@partlist,%handgrade);
  105:     foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) {
  106: 	if (/^\w+response_\w+.*/) {
  107: 	    my ($responsetype,$part) = split(/_/,$_,2);
  108: 	    my ($partid,$respid) = split(/_/,$part);
  109: 	    $responsetype =~ s/response$//; # make it compatible w/ navmaps - should move to that!!
  110: 	    $handgrade{$part} = $responsetype.':'.($allkeys =~ /parameter_$part\_handgrade/ ? 'yes' : 'no');
  111: 	    next if ($seen{$partid} > 0);
  112: 	    $seen{$partid}++;
  113: 	    push @partlist,$partid;
  114: 	}
  115:     }
  116:     return \@partlist,\%handgrade;
  117: }
  118: 
  119: #--- Show resource title
  120: #--- and parts and response type
  121: sub showResourceInfo {
  122:     my ($url,$probTitle) = @_;
  123:     my $result ='<table border="0">'.
  124: 	'<tr><td colspan=3><font size=+1><b>Current Resource: </b>'.$probTitle.'</font></td></tr>'."\n";
  125:     my ($partlist,$handgrade) = &response_type($url);
  126:     my %resptype = (); #,$hdgrade)=('','no');
  127:     my $hdgrade='no';
  128:     for (sort keys(%$handgrade)) {
  129: 	my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});
  130: 	my $partID = (split(/_/))[0];
  131: 	$resptype{$partID} = $responsetype;
  132: 	$hdgrade = $handgrade if ($handgrade eq 'yes');
  133: 	$result.='<tr><td><b>Part </b>'.$partID.'</td>'.
  134: 	    '<td><b>Type: </b>'.$responsetype.'</td></tr>';
  135: #	    '<td><b>Handgrade: </b>'.$handgrade.'</td></tr>';
  136:     }
  137:     $result.='</table>'."\n";
  138:     return $result,\%resptype,$hdgrade,$partlist,$handgrade;
  139: }
  140: 
  141: #--- Clean response type for display
  142: #--- Currently filters option response type only.
  143: sub cleanRecord {
  144:     my ($answer,$response,$symb) = @_;
  145:     if ($response eq 'option') {
  146: 	my (@IDs,@ans);
  147: 	foreach (split(/\&/,&Apache::lonnet::unescape($answer))) {
  148: 	    my ($optionID,$ans) = split(/=/);
  149: 	    push @IDs,$optionID.'</font>';
  150: 	    push @ans,$ans;
  151: 	}
  152: 	my $grayFont = '<font color="#999999">';
  153: 	return '<table border="1">'.
  154: 	    '<tr valign="top"><td>Answer</td><td>'.
  155: 	    (join '</td><td>',@ans).'</td></tr>'.
  156: 	    '<tr valign="top"><td>'.$grayFont.'Option ID</font></td><td>'.$grayFont.
  157: 	    (join '</td><td>'.$grayFont,@IDs).'</font></td></tr>'.
  158: 	    '</table>';
  159:     }
  160:     if ($response eq 'essay') {
  161: 	if (! exists ($ENV{'form.'.$symb})) {
  162: 	    my (%keyhash) = &Apache::lonnet::dump('nohist_handgrade',
  163: 						  $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
  164: 						  $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
  165: 
  166: 	    my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'};
  167: 	    $ENV{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : '';
  168: 	    $ENV{'form.kwclr'}    = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red';
  169: 	    $ENV{'form.kwsize'}   = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';
  170: 	    $ENV{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
  171: 	    $ENV{'form.'.$symb} = 1; # so that we don't have to read it from disk for multiple sub of the same prob.
  172: 	}
  173: 	return &keywords_highlight($answer);
  174:     }
  175:     return $answer;
  176: }
  177: 
  178: #-- A couple of common js functions
  179: sub commonJSfunctions {
  180:     my $request = shift;
  181:     $request->print(<<COMMONJSFUNCTIONS);
  182: <script type="text/javascript" language="javascript">
  183:     function radioSelection(radioButton) {
  184: 	var selection=null;
  185: 	if (radioButton.length > 1) {
  186: 	    for (var i=0; i<radioButton.length; i++) {
  187: 		if (radioButton[i].checked) {
  188: 		    return radioButton[i].value;
  189: 		}
  190: 	    }
  191: 	} else {
  192: 	    if (radioButton.checked) return radioButton.value;
  193: 	}
  194: 	return selection;
  195:     }
  196: 
  197:     function pullDownSelection(selectOne) {
  198: 	var selection="";
  199: 	if (selectOne.length > 1) {
  200: 	    for (var i=0; i<selectOne.length; i++) {
  201: 		if (selectOne[i].selected) {
  202: 		    return selectOne[i].value;
  203: 		}
  204: 	    }
  205: 	} else {
  206: 	    if (selectOne.selected) return selectOne.value;
  207: 	}
  208:     }
  209: </script>
  210: COMMONJSFUNCTIONS
  211: }
  212: 
  213: #--- Dumps the class list with usernames,list of sections,
  214: #--- section, ids and fullnames for each user.
  215: sub getclasslist {
  216:     my ($getsec,$filterlist) = @_;
  217:     $getsec = $getsec eq '' ? 'all' : $getsec;
  218:     my $classlist=&Apache::loncoursedata::get_classlist();
  219:     # Bail out if we were unable to get the classlist
  220:     return if (! defined($classlist));
  221:     #
  222:     my %sections;
  223:     my %fullnames;
  224:     foreach (keys(%$classlist)) {
  225:         # the following undefs are for 'domain', and 'username' respectively.
  226: 	my (undef,undef,$end,$start,$id,$section,$fullname,$status)=
  227:             @{$classlist->{$_}};
  228: 	# filter students according to status selected
  229: 	if ($filterlist && $ENV{'form.Status'} ne 'Any') {
  230: 	    if ($ENV{'form.Status'} ne $status) {
  231: 		delete ($classlist->{$_});
  232: 		next;
  233: 	    }
  234: 	}
  235: 	$section = ($section ne '' ? $section : 'no');
  236: 	if (&canview($section)) {
  237: 	    if ($getsec eq 'all' || $getsec eq $section) {
  238: 		$sections{$section}++;
  239: 		$fullnames{$_}=$fullname;
  240: 	    } else {
  241: 		delete($classlist->{$_});
  242: 	    }
  243: 	} else {
  244: 	    delete($classlist->{$_});
  245: 	}
  246:     }
  247:     my %seen = ();
  248:     my @sections = sort(keys(%sections));
  249:     return ($classlist,\@sections,\%fullnames);
  250: }
  251: 
  252: sub canmodify {
  253:     my ($sec)=@_;
  254:     if ($perm{'mgr'}) {
  255: 	if (!defined($perm{'mgr_section'})) {
  256: 	    # can modify whole class
  257: 	    return 1;
  258: 	} else {
  259: 	    if ($sec eq $perm{'mgr_section'}) {
  260: 		#can modify the requested section
  261: 		return 1;
  262: 	    } else {
  263: 		# can't modify the request section
  264: 		return 0;
  265: 	    }
  266: 	}
  267:     }
  268:     #can't modify
  269:     return 0;
  270: }
  271: 
  272: sub canview {
  273:     my ($sec)=@_;
  274:     if ($perm{'vgr'}) {
  275: 	if (!defined($perm{'vgr_section'})) {
  276: 	    # can modify whole class
  277: 	    return 1;
  278: 	} else {
  279: 	    if ($sec eq $perm{'vgr_section'}) {
  280: 		#can modify the requested section
  281: 		return 1;
  282: 	    } else {
  283: 		# can't modify the request section
  284: 		return 0;
  285: 	    }
  286: 	}
  287:     }
  288:     #can't modify
  289:     return 0;
  290: }
  291: 
  292: #--- Retrieve the grade status of a student for all the parts
  293: sub student_gradeStatus {
  294:     my ($url,$symb,$udom,$uname,$partlist) = @_;
  295:     my %record     = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
  296:     my %partstatus = ();
  297:     foreach (@$partlist) {
  298: 	my ($status,$foo)    = split(/_/,$record{"resource.$_.solved"},2);
  299: 	$status              = 'nothing' if ($status eq '');
  300: 	$partstatus{$_}      = $status;
  301: 	my $subkey           = "resource.$_.submitted_by";
  302: 	$partstatus{$subkey} = $record{$subkey} if ($record{$subkey} ne '');
  303:     }
  304:     return %partstatus;
  305: }
  306: 
  307: # hidden form and javascript that calls the form
  308: # Use by verifyscript and viewgrades
  309: # Shows a student's view of problem and submission
  310: sub jscriptNform {
  311:     my ($url,$symb) = @_;
  312:     my $jscript='<script type="text/javascript" language="javascript">'."\n".
  313: 	'    function viewOneStudent(user,domain) {'."\n".
  314: 	'	document.onestudent.student.value = user;'."\n".
  315: 	'	document.onestudent.userdom.value = domain;'."\n".
  316: 	'	document.onestudent.submit();'."\n".
  317: 	'    }'."\n".
  318: 	'</script>'."\n";
  319:     $jscript.= '<form action="/adm/grades" method="post" name="onestudent">'."\n".
  320: 	'<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
  321: 	'<input type="hidden" name="url"     value="'.$url.'" />'."\n".
  322: 	'<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."\n".
  323: 	'<input type="hidden" name="probTitle" value="'.$ENV{'form.probTitle'}.'" />'."\n".
  324: 	'<input type="hidden" name="command" value="submission" />'."\n".
  325: 	'<input type="hidden" name="student" value="" />'."\n".
  326: 	'<input type="hidden" name="userdom" value="" />'."\n".
  327: 	'</form>'."\n";
  328:     return $jscript;
  329: }
  330: 
  331: #------------------ End of general use routines --------------------
  332: 
  333: #
  334: # Find most similar essay
  335: #
  336: 
  337: sub most_similar {
  338:     my ($uname,$udom,$uessay)=@_;
  339: 
  340: # ignore spaces and punctuation
  341: 
  342:     $uessay=~s/\W+/ /gs;
  343: 
  344: # these will be returned. Do not care if not at least 50 percent similar
  345:     my $limit=0.6;
  346:     my $sname='';
  347:     my $sdom='';
  348:     my $scrsid='';
  349:     my $sessay='';
  350: # go through all essays ...
  351:     foreach my $tkey (keys %oldessays) {
  352: 	my ($tname,$tdom,$tcrsid)=split(/\./,$tkey);
  353: # ... except the same student
  354:         if (($tname ne $uname) || ($tdom ne $udom)) {
  355: 	    my $tessay=$oldessays{$tkey};
  356:             $tessay=~s/\W+/ /gs;
  357: # String similarity gives up if not even limit
  358:             my $tsimilar=&String::Similarity::similarity($uessay,$tessay,$limit);
  359: # Found one
  360:             if ($tsimilar>$limit) {
  361: 		$limit=$tsimilar;
  362:                 $sname=$tname;
  363:                 $sdom=$tdom;
  364:                 $scrsid=$tcrsid;
  365:                 $sessay=$oldessays{$tkey};
  366:             }
  367:         } 
  368:     }
  369:     if ($limit>0.6) {
  370:        return ($sname,$sdom,$scrsid,$sessay,$limit);
  371:     } else {
  372:        return ('','','','',0);
  373:     }
  374: }
  375: 
  376: #-------------------------------------------------------------------
  377: 
  378: #------------------------------------ Receipt Verification Routines
  379: #
  380: #--- Check whether a receipt number is valid.---
  381: sub verifyreceipt {
  382:     my $request  = shift;
  383: 
  384:     my $courseid = $ENV{'request.course.id'};
  385:     my $receipt  = unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}).'-'.
  386: 	$ENV{'form.receipt'};
  387:     $receipt     =~ s/[^\-\d]//g;
  388:     my $url      = $ENV{'form.url'};
  389:     my $symb     = $ENV{'form.symb'};
  390:     unless ($symb) {
  391: 	$symb    = &Apache::lonnet::symbread($url);
  392:     }
  393: 
  394:     my $title.='<h3><font color="#339933">Verifying Submission Receipt '.
  395: 	$receipt.'</h3></font>'."\n".
  396: 	'<font size=+1><b>Resource: </b>'.$ENV{'form.probTitle'}.'</font><br><br>'."\n";
  397: 
  398:     my ($string,$contents,$matches) = ('','',0);
  399:     my (undef,undef,$fullname) = &getclasslist('all','0');
  400: 
  401:     foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
  402: 	my ($uname,$udom)=split(/\:/);
  403: 	if ($receipt eq 
  404: 	    &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb)) {
  405: 	    $contents.='<tr bgcolor="#ffffe6"><td>&nbsp;'."\n".
  406: 		'<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.
  407: 		'\')"; TARGET=_self>'.$$fullname{$_}.'</a>&nbsp;</td>'."\n".
  408: 		'<td>&nbsp;'.$uname.'&nbsp;</td>'.
  409: 		'<td>&nbsp;'.$udom.'&nbsp;</td></tr>'."\n";
  410: 	    
  411: 	    $matches++;
  412: 	}
  413:     }
  414:     if ($matches == 0) {
  415: 	$string = $title.'No match found for the above receipt.';
  416:     } else {
  417: 	$string = &jscriptNform($url,$symb).$title.
  418: 	    'The above receipt matches the following student'.
  419: 	    ($matches <= 1 ? '.' : 's.')."\n".
  420: 	    '<table border="0"><tr><td bgcolor="#777777">'."\n".
  421: 	    '<table border="0"><tr bgcolor="#e6ffff">'."\n".
  422: 	    '<td><b>&nbsp;Fullname&nbsp;</b></td>'."\n".
  423: 	    '<td><b>&nbsp;Username&nbsp;</b></td>'."\n".
  424: 	    '<td><b>&nbsp;Domain&nbsp;</b></td></tr>'."\n".
  425: 	    $contents.
  426: 	    '</table></td></tr></table>'."\n";
  427:     }
  428:     return $string.&show_grading_menu_form($symb,$url);
  429: }
  430: 
  431: #--- This is called by a number of programs.
  432: #--- Called from the Grading Menu - View/Grade an individual student
  433: #--- Also called directly when one clicks on the subm button 
  434: #    on the problem page.
  435: sub listStudents {
  436:     my ($request) = shift;
  437: 
  438:     my ($symb,$url) = &get_symb_and_url($request);
  439:     my $cdom      = $ENV{"course.$ENV{'request.course.id'}.domain"};
  440:     my $cnum      = $ENV{"course.$ENV{'request.course.id'}.num"};
  441:     my $getsec    = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};
  442:     my $submitonly= $ENV{'form.submitonly'} eq '' ? 'all' : $ENV{'form.submitonly'};
  443: 
  444:     my $viewgrade = $ENV{'form.showgrading'} eq 'yes' ? 'View/Grade/Regrade' : 'View';
  445:     $ENV{'form.probTitle'} = $ENV{'form.probTitle'} eq '' ? 
  446: 	&Apache::lonnet::gettitle($symb) : $ENV{'form.probTitle'};
  447: 
  448:     my $result='<h3><font color="#339933">&nbsp;'.$viewgrade.
  449: 	' Submissions for a Student or a Group of Students</font></h3>';
  450: 
  451:     my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($url,$ENV{'form.probTitle'});
  452:     $result.=$table;
  453: 
  454:     $request->print(<<LISTJAVASCRIPT);
  455: <script type="text/javascript" language="javascript">
  456:     function checkSelect(checkBox) {
  457: 	var ctr=0;
  458: 	var sense="";
  459: 	if (checkBox.length > 1) {
  460: 	    for (var i=0; i<checkBox.length; i++) {
  461: 		if (checkBox[i].checked) {
  462: 		    ctr++;
  463: 		}
  464: 	    }
  465: 	    sense = "a student or group of students";
  466: 	} else {
  467: 	    if (checkBox.checked) {
  468: 		ctr = 1;
  469: 	    }
  470: 	    sense = "the student";
  471: 	}
  472: 	if (ctr == 0) {
  473: 	    alert("Please select "+sense+" before clicking on the $viewgrade button.");
  474: 	    return false;
  475: 	}
  476: 	document.gradesub.submit();
  477:     }
  478: 
  479:     function reLoadList(formname) {
  480: 	if (formname.saveStatusOld.value == pullDownSelection(formname.Status)) {return;}
  481: 	formname.command.value = 'submission';
  482: 	formname.submit();
  483:     }
  484: </script>
  485: LISTJAVASCRIPT
  486: 
  487:     &commonJSfunctions($request);
  488:     $request->print($result);
  489: 
  490:     my $checkhdgrade = ($ENV{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked' : '';
  491:     my $checklastsub = $checkhdgrade eq '' ? 'checked' : '';
  492:     my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'."\n".
  493: 	'&nbsp;<b>View Problem Text: </b><input type="radio" name="vProb" value="no" checked /> no '."\n".
  494: 	'<input type="radio" name="vProb" value="yes" /> one student '."\n".
  495: 	'<input type="radio" name="vProb" value="all" /> all students <br />'."\n".
  496: 	'&nbsp;<b>Submissions: </b>'."\n";
  497:     if ($ENV{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) {
  498: 	$gradeTable.='<input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> essay part only'."\n";
  499:     }
  500: 
  501:     my $saveStatus = $ENV{'form.Status'} eq '' ? 'Active' : $ENV{'form.Status'};
  502:     $ENV{'form.Status'} = $saveStatus;
  503: 
  504:     $gradeTable.='<input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> last sub only'."\n".
  505: 	'<input type="radio" name="lastSub" value="last" /> last sub & parts info'."\n".
  506: 	'<input type="radio" name="lastSub" value="datesub" /> by dates and submissions'."\n".
  507: 	'<input type="radio" name="lastSub" value="all" /> all details'."\n".
  508: 	'<input type="hidden" name="section"     value="'.$getsec.'" />'."\n".
  509: 	'<input type="hidden" name="submitonly"  value="'.$submitonly.'" />'."\n".
  510: 	'<input type="hidden" name="handgrade"   value="'.$ENV{'form.handgrade'}.'" /><br />'."\n".
  511: 	'<input type="hidden" name="showgrading" value="'.$ENV{'form.showgrading'}.'" /><br />'."\n".
  512: 	'<input type="hidden" name="saveState"   value="'.$ENV{'form.saveState'}.'" />'."\n".
  513: 	'<input type="hidden" name="probTitle"   value="'.$ENV{'form.probTitle'}.'" />'."\n".
  514: 	'<input type="hidden" name="url"  value="'.$url.'" />'."\n".
  515: 	'<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
  516: 	'<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n";
  517: 
  518:     if (exists($ENV{'form.gradingMenu'}) && exists($ENV{'form.Status'})) {
  519: 	$gradeTable.='<input type="hidden" name="Status"   value="'.$ENV{'form.Status'}.'" />'."\n";
  520:     } else {
  521: 	$gradeTable.='<b>Student Status:</b> '.
  522: 	    &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);').'<br />';
  523:     }
  524: 
  525:     $gradeTable.='To '.lc($viewgrade).' a submission, click on the check box next to the student\'s name. Then '."\n".
  526: 	'click on the '.$viewgrade.' button. To view the submissions for a group of students, click'."\n".
  527: 	' on the check boxes for the group of students.<br />'."\n".
  528: 	'<input type="hidden" name="command" value="processGroup" />'."\n";
  529:     $gradeTable.='<input type="button" '."\n".
  530: 	'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".
  531: 	'value="'.$viewgrade.'" />'."\n";
  532: 
  533:     my (undef, undef, $fullname) = &getclasslist($getsec,'1');  
  534:     $gradeTable.='<table border="0"><tr><td bgcolor="#777777">'.
  535: 	'<table border="0"><tr bgcolor="#e6ffff">';
  536:     my $loop = 0;
  537:     while ($loop < 2) {
  538: 	$gradeTable.='<td><b>&nbsp;Select&nbsp;</b></td><td><b>&nbsp;Fullname&nbsp;</b>'.
  539: 	    '<font color="#999999">(Username)</font>&nbsp;</td>';
  540: 	if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {
  541: 	    foreach (sort(@$partlist)) {
  542: 		$gradeTable.='<td><b>&nbsp;Part '.(split(/_/))[0].' Status&nbsp;</b></td>';
  543: 	    }
  544: 	}
  545: 	$loop++;
  546:     }
  547:     $gradeTable.='</tr>'."\n";
  548: 
  549:     my $ctr = 0;
  550:     foreach my $student (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
  551: 	my ($uname,$udom) = split(/:/,$student);
  552: 	my %status = ();
  553: 	if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {
  554: 	    (%status) =&student_gradeStatus($url,$symb,$udom,$uname,$partlist);
  555: 	    my $statusflg = '';
  556: 	    foreach (keys(%status)) {
  557: 		$statusflg = 1 if ($status{$_} ne 'nothing');
  558: 		my ($foo,$partid,$foo1) = split(/\./,$_);
  559: 		if ($status{'resource.'.$partid.'.submitted_by'} ne '') {
  560: 		    $statusflg = '';
  561: 		    $gradeTable.='<input type="hidden" name="'.
  562: 			$student.':submitted_by" value="'.
  563: 			$status{'resource.'.$partid.'.submitted_by'}.'" />';
  564: 		}
  565: 	    }
  566: 	    next if ($statusflg eq '' && $submitonly eq 'yes');
  567: 	}
  568: 
  569: 	$ctr++;
  570: 	if ( $perm{'vgr'} eq 'F' ) {
  571: 	    $gradeTable.='<tr bgcolor="#ffffe6">' if ($ctr%2 ==1);
  572: 	    $gradeTable.='<td align="center"><input type=checkbox name="stuinfo" value="'.
  573: 		$student.':'.$$fullname{$student}.'&nbsp;"></td>'."\n".
  574: 		'<td>&nbsp;'.$$fullname{$student}.'&nbsp;'."\n".
  575: 		'<font color="#999999">('.$uname.')</font></td>'."\n";
  576: 
  577: 	    if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {
  578: 		foreach (sort keys(%status)) {
  579: 		    next if (/^resource.*?submitted_by$/);
  580: 		    $gradeTable.='<td align="middle">&nbsp;'.$status{$_}.'&nbsp;</td>'."\n";
  581: 		}
  582: 	    }
  583: 	    $gradeTable.='</tr>'."\n" if ($ctr%2 ==0);
  584: 	}
  585:     }
  586:     if ($ctr%2 ==1) {
  587: 	$gradeTable.='<td>&nbsp;</td><td>&nbsp;</td>';
  588: 	    if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {
  589: 		foreach (@$partlist) {
  590: 		    $gradeTable.='<td>&nbsp;</td>';
  591: 		}
  592: 	    }
  593: 	$gradeTable.='</tr>';
  594:     }
  595: 
  596:     $gradeTable.='</table></td></tr></table>'.
  597: 	'<input type="button" '.
  598: 	'onClick="javascript:checkSelect(this.form.stuinfo);" '.
  599: 	'value="'.$viewgrade.'" /></form>'."\n";
  600:     if ($ctr == 0) {
  601: 	my $num_students=(scalar(keys(%$fullname)));
  602: 	if ($num_students eq 0) {
  603: 	    $gradeTable='<br />&nbsp;<font color="red">There are no students currently enrolled.</font>';
  604: 	} else {
  605: 	    $gradeTable='<br />&nbsp;<font color="red">'.
  606: 		'No submissions found for this resource for any students. ('.$num_students.
  607: 		' checked for submissions</font><br />';
  608: 	}
  609:     } elsif ($ctr == 1) {
  610: 	$gradeTable =~ s/type=checkbox/type=checkbox checked/;
  611:     }
  612:     $gradeTable.=&show_grading_menu_form($symb,$url);
  613:     $request->print($gradeTable);
  614:     return '';
  615: }
  616: 
  617: #---- Called from the listStudents routine
  618: #     Displays the submissions for one student or a group of students
  619: sub processGroup {
  620:     my ($request)  = shift;
  621:     my $ctr        = 0;
  622:     my @stuchecked = (ref($ENV{'form.stuinfo'}) ? @{$ENV{'form.stuinfo'}}
  623: 		      : ($ENV{'form.stuinfo'}));
  624:     my $total      = scalar(@stuchecked)-1;
  625: 
  626:     foreach (@stuchecked) {
  627: 	my ($uname,$udom,$fullname) = split(/:/);
  628: 	$ENV{'form.student'}        = $uname;
  629: 	$ENV{'form.userdom'}        = $udom;
  630: 	$ENV{'form.fullname'}       = $fullname;
  631: 	&submission($request,$ctr,$total);
  632: 	$ctr++;
  633:     }
  634:     return '';
  635: }
  636: 
  637: #------------------------------------------------------------------------------------
  638: #
  639: #-------------------------- Next few routines handles grading by student, essentially
  640: #                           handles essay response type problem/part
  641: #
  642: #--- Javascript to handle the submission page functionality ---
  643: sub sub_page_js {
  644:     my $request = shift;
  645:     $request->print(<<SUBJAVASCRIPT);
  646: <script type="text/javascript" language="javascript">
  647:     function updateRadio(formname,id,weight) {
  648: 	var gradeBox = eval("formname.GD_BOX"+id);
  649: 	var radioButton = eval("formname.RADVAL"+id);
  650: 	var oldpts = eval("formname.oldpts"+id+".value");
  651: 	var pts = checkSolved(formname,id) == 'update' ? gradeBox.value : oldpts;
  652: 	gradeBox.value = pts;
  653: 	var resetbox = false;
  654: 	if (isNaN(pts) || pts < 0) {
  655: 	    alert("A number equal or greater than 0 is expected. Entered value = "+pts);
  656: 	    for (var i=0; i<radioButton.length; i++) {
  657: 		if (radioButton[i].checked) {
  658: 		    gradeBox.value = i;
  659: 		    resetbox = true;
  660: 		}
  661: 	    }
  662: 	    if (!resetbox) {
  663: 		formtextbox.value = "";
  664: 	    }
  665: 	    return;
  666: 	}
  667: 
  668: 	if (pts > weight) {
  669: 	    var resp = confirm("You entered a value ("+pts+
  670: 			       ") greater than the weight for the part. Accept?");
  671: 	    if (resp == false) {
  672: 		gradeBox.value = "";
  673: 		return;
  674: 	    }
  675: 	}
  676: 
  677: 	for (var i=0; i<radioButton.length; i++) {
  678: 	    radioButton[i].checked=false;
  679: 	    if (pts == i && pts != "") {
  680: 		radioButton[i].checked=true;
  681: 	    }
  682: 	}
  683: 	updateSelect(formname,id);
  684: 	var stores = eval("formname.stores"+id);
  685: 	stores.value = "0";
  686:     }
  687: 
  688:     function writeBox(formname,id,pts) {
  689: 	var gradeBox = eval("formname.GD_BOX"+id);
  690: 	if (checkSolved(formname,id) == 'update') {
  691: 	    gradeBox.value = pts;
  692: 	} else {
  693: 	    var oldpts = eval("formname.oldpts"+id+".value");
  694: 	    gradeBox.value = oldpts;
  695: 	    var radioButton = eval("formname.RADVAL"+id);
  696: 	    for (var i=0; i<radioButton.length; i++) {
  697: 		radioButton[i].checked=false;
  698: 		if (i == oldpts) {
  699: 		    radioButton[i].checked=true;
  700: 		}
  701: 	    }
  702: 	}
  703: 	var stores = eval("formname.stores"+id);
  704: 	stores.value = "0";
  705: 	updateSelect(formname,id);
  706: 	return;
  707:     }
  708: 
  709:     function clearRadBox(formname,id) {
  710: 	if (checkSolved(formname,id) == 'noupdate') {
  711: 	    updateSelect(formname,id);
  712: 	    return;
  713: 	}
  714: 	gradeSelect = eval("formname.GD_SEL"+id);
  715: 	for (var i=0; i<gradeSelect.length; i++) {
  716: 	    if (gradeSelect[i].selected) {
  717: 		var selectx=i;
  718: 	    }
  719: 	}
  720: 	var stores = eval("formname.stores"+id);
  721: 	if (selectx == stores.value) { return };
  722: 	var gradeBox = eval("formname.GD_BOX"+id);
  723: 	gradeBox.value = "";
  724: 	var radioButton = eval("formname.RADVAL"+id);
  725: 	for (var i=0; i<radioButton.length; i++) {
  726: 	    radioButton[i].checked=false;
  727: 	}
  728: 	stores.value = selectx;
  729:     }
  730: 
  731:     function checkSolved(formname,id) {
  732: 	if (eval("formname.solved"+id+".value") == "correct_by_student" && formname.overRideScore.value == 'no') {
  733: 	    var reply = confirm("This problem has been graded correct by the computer. Do you want to change the score?");
  734: 	    if (!reply) {return "noupdate";}
  735: 	    formname.overRideScore.value = 'yes';
  736: 	}
  737: 	return "update";
  738:     }
  739: 
  740:     function updateSelect(formname,id) {
  741: 	var gradeSelect = eval("formname.GD_SEL"+id);
  742: 	gradeSelect[0].selected = true;
  743: 	return;
  744:     }
  745: 
  746: //=========== Check that a point is assigned for all the parts  ============
  747:     function checksubmit(formname,val,total,parttot) {
  748: 	formname.gradeOpt.value = val;
  749: 	if (val == "Save & Next") {
  750: 	    for (i=0;i<=total;i++) {
  751: 		for (j=0;j<parttot;j++) {
  752: 		    var partid = eval("formname.partid"+i+"_"+j+".value");
  753: 		    var selopt = eval("formname.GD_SEL"+i+"_"+partid);
  754: 		    if (selopt[0].selected) {
  755: 			var points = eval("formname.GD_BOX"+i+"_"+partid+".value");
  756: 			if (points == "") {
  757: 			    var name = eval("formname.name"+i+".value");
  758: 			    var resp = confirm("You did not assign a score for "+name+", part "+partid+". Continue?");
  759: 			    if (resp == false) {
  760: 				eval("formname.GD_BOX"+i+"_"+partid+".focus()");
  761: 				return false;
  762: 			    }
  763: 			}
  764: 		    }
  765: 		    
  766: 		}
  767: 	    }
  768: 	    
  769: 	}
  770: 	if (val == "Grade Student") {
  771: 	    formname.showgrading.value = "yes";
  772: 	    if (formname.Status.value == "") {
  773: 		formname.Status.value = "Active";
  774: 	    }
  775: 	    formname.studentNo.value = total;
  776: 	}
  777: 	formname.submit();
  778:     }
  779: 
  780: //======= Check that a score is assigned for all the problems (page/sequence grading only) =========
  781:     function checkSubmitPage(formname,total) {
  782: 	noscore = new Array(100);
  783: 	var ptr = 0;
  784: 	for (i=1;i<total;i++) {
  785: 	    var partid = eval("formname.q_"+i+".value");
  786: 	    var selopt = eval("formname.GD_SEL"+i+"_"+partid);
  787: 	    if (selopt[0].selected) {
  788: 		var points = eval("formname.GD_BOX"+i+"_"+partid+".value");
  789: 		var status = eval("formname.solved"+i+"_"+partid+".value");
  790: 		if (points == "" && status != "correct_by_student") {
  791: 		    noscore[ptr] = i;
  792: 		    ptr++;
  793: 		}
  794: 	    }
  795: 	}
  796: 	if (ptr != 0) {
  797: 	    var sense = ptr == 1 ? ": " : "s: ";
  798: 	    var prolist = "";
  799: 	    if (ptr == 1) {
  800: 		prolist = noscore[0];
  801: 	    } else {
  802: 		var i = 0;
  803: 		while (i < ptr-1) {
  804: 		    prolist += noscore[i]+", ";
  805: 		    i++;
  806: 		}
  807: 		prolist += "and "+noscore[i];
  808: 	    }
  809: 	    var resp = confirm("You did not assign any score for the following problem"+sense+prolist+". Continue?");
  810: 	    if (resp == false) {
  811: 		return false;
  812: 	    }
  813: 	}
  814: 
  815: 	formname.submit();
  816:     }
  817: </script>
  818: SUBJAVASCRIPT
  819: }
  820: 
  821: #--- javascript for essay type problem --
  822: sub sub_page_kw_js {
  823:     my $request = shift;
  824:     my $iconpath = $request->dir_config('lonIconsURL');
  825:     &commonJSfunctions($request);
  826:     $request->print(<<SUBJAVASCRIPT);
  827: <script type="text/javascript" language="javascript">
  828: 
  829: //===================== Show list of keywords ====================
  830:   function keywords(formname) {
  831:     var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",formname.keywords.value);
  832:     if (nret==null) return;
  833:     formname.keywords.value = nret;
  834: 
  835:     formname.refresh.value = "on";
  836:     if (formname.keywords.value != "") {
  837: 	formname.submit();
  838:     }
  839:     return;
  840:   }
  841: 
  842: //===================== Script to view submitted by ==================
  843:   function viewSubmitter(submitter) {
  844:     document.SCORE.refresh.value = "on";
  845:     document.SCORE.NCT.value = "1";
  846:     document.SCORE.unamedom0.value = submitter;
  847:     document.SCORE.submit();
  848:     return;
  849:   }
  850: 
  851: //===================== Script to add keyword(s) ==================
  852:   function getSel() {
  853:     if (document.getSelection) txt = document.getSelection();
  854:     else if (document.selection) txt = document.selection.createRange().text;
  855:     else return;
  856:     var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," ");
  857:     if (cleantxt=="") {
  858: 	alert("Please select a word or group of words from document and then click this link.");
  859: 	return;
  860:     }
  861:     var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt);
  862:     if (nret==null) return;
  863:     var curlist = document.SCORE.keywords.value;
  864:     document.SCORE.keywords.value = curlist+" "+nret;
  865:     document.SCORE.refresh.value = "on";
  866:     if (document.SCORE.keywords.value != "") {
  867: 	document.SCORE.submit();
  868:     }
  869:     return;
  870:   }
  871: 
  872: //====================== Script for composing message ==============
  873:    // preload images
  874:    img1 = new Image();
  875:    img1.src = "$iconpath/mailbkgrd.gif";
  876:    img2 = new Image();
  877:    img2.src = "$iconpath/mailto.gif";
  878: 
  879:   function msgCenter(msgform,usrctr,fullname) {
  880:     var Nmsg  = msgform.savemsgN.value;
  881:     savedMsgHeader(Nmsg,usrctr,fullname);
  882:     var subject = msgform.msgsub.value;
  883:     var rtrchk  = eval("document.SCORE.includemsg"+usrctr);
  884:     var msgchk = rtrchk.value;
  885:     re = /msgsub/;
  886:     var shwsel = "";
  887:     if (re.test(msgchk)) { shwsel = "checked" }
  888:     subject = (document.SCORE.shownSub.value == 0 ? checkEntities(subject) : subject);
  889:     displaySubject(checkEntities(subject),shwsel);
  890:     for (var i=1; i<=Nmsg; i++) {
  891: 	var testmsg = "savemsg"+i+",";
  892: 	re = new RegExp(testmsg,"g");
  893: 	shwsel = "";
  894: 	if (re.test(msgchk)) { shwsel = "checked" }
  895: 	var message = eval("document.SCORE.savemsg"+i+".value");
  896: 	var shownOnce = eval("document.SCORE.shownOnce"+i+".value");
  897: 	message = (shownOnce == 0 ? checkEntities(message) : message);
  898: 	displaySavedMsg(i,message,shwsel); //I do not get it. w/o checkEntities on saved messages,
  899: 	                                   //any &lt; is already converted to <, etc. However, only once!!
  900:     }
  901:     newmsg = eval("document.SCORE.newmsg"+usrctr+".value");
  902:     shwsel = "";
  903:     re = /newmsg/;
  904:     if (re.test(msgchk)) { shwsel = "checked" }
  905:     newMsg(newmsg,shwsel);
  906:     msgTail(); 
  907:     return;
  908:   }
  909: 
  910:   function checkEntities(strx) {
  911:     if (strx.length == 0) return strx;
  912:     var orgStr = ["&", "<", ">", '"']; 
  913:     var newStr = ["&amp;", "&lt;", "&gt;", "&quot;"];
  914:     var counter = 0;
  915:     while (counter < 4) {
  916: 	strx = strReplace(strx,orgStr[counter],newStr[counter]);
  917: 	counter++;
  918:     }
  919:     return strx;
  920:   }
  921: 
  922:   function strReplace(strx, orgStr, newStr) {
  923:     return strx.split(orgStr).join(newStr);
  924:   }
  925: 
  926:   function savedMsgHeader(Nmsg,usrctr,fullname) {
  927:     var height = 70*Nmsg+250;
  928:     var scrollbar = "no";
  929:     if (height > 600) {
  930: 	height = 600;
  931: 	scrollbar = "yes";
  932:     }
  933: //    if (window.pWin) {window.pWin.close(); window.pWin=null}
  934:     var xpos = (screen.width-600)/2;
  935:     xpos = (xpos < 0) ? '0' : xpos;
  936:     var ypos = (screen.height-height)/2-30;
  937:     ypos = (ypos < 0) ? '0' : ypos;
  938: 
  939:     pWin = window.open('', 'MessageCenter', 'toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height);
  940:     pWin.focus();
  941:     pDoc = pWin.document;
  942:     pDoc.write("<html><head>");
  943:     pDoc.write("<title>Message Central</title>");
  944: 
  945:     pDoc.write("<script language=javascript>");
  946:     pDoc.write("function checkInput() {");
  947:     pDoc.write("  opener.document.SCORE.msgsub.value = opener.checkEntities(document.msgcenter.msgsub.value);");
  948:     pDoc.write("  var nmsg   = opener.document.SCORE.savemsgN.value;");
  949:     pDoc.write("  var usrctr = document.msgcenter.usrctr.value;");
  950:     pDoc.write("  var newval = eval(\\"opener.document.SCORE.newmsg\\"+usrctr);");
  951:     pDoc.write("  newval.value = opener.checkEntities(document.msgcenter.newmsg.value);");
  952: 
  953:     pDoc.write("  var msgchk = \\"\\";");
  954:     pDoc.write("  if (document.msgcenter.subchk.checked) {");
  955:     pDoc.write("     msgchk = \\"msgsub,\\";");
  956:     pDoc.write("  }");
  957:     pDoc.write("  var includemsg = 0;");
  958:     pDoc.write("  for (var i=1; i<=nmsg; i++) {");
  959:     pDoc.write("      var opnmsg = eval(\\"opener.document.SCORE.savemsg\\"+i);");
  960:     pDoc.write("      var frmmsg = eval(\\"document.msgcenter.msg\\"+i);");
  961:     pDoc.write("      opnmsg.value = opener.checkEntities(frmmsg.value);");
  962:     pDoc.write("      var showflg = eval(\\"opener.document.SCORE.shownOnce\\"+i);");
  963:     pDoc.write("      showflg.value = \\"1\\";");
  964:     pDoc.write("      var chkbox = eval(\\"document.msgcenter.msgn\\"+i);");
  965:     pDoc.write("      if (chkbox.checked) {");
  966:     pDoc.write("         msgchk += \\"savemsg\\"+i+\\",\\";");
  967:     pDoc.write("         includemsg = 1;");
  968:     pDoc.write("      }");
  969:     pDoc.write("  }");
  970:     pDoc.write("  if (document.msgcenter.newmsgchk.checked) {");
  971:     pDoc.write("     msgchk += \\"newmsg\\"+usrctr;");
  972:     pDoc.write("     includemsg = 1;");
  973:     pDoc.write("  }");
  974:     pDoc.write("  imgformname = eval(\\"opener.document.SCORE.mailicon\\"+usrctr);");
  975:     pDoc.write("  imgformname.src = \\"$iconpath/\\"+((includemsg) ? \\"mailto.gif\\" : \\"mailbkgrd.gif\\");");
  976:     pDoc.write("  var includemsg = eval(\\"opener.document.SCORE.includemsg\\"+usrctr);");
  977:     pDoc.write("  includemsg.value = msgchk;");
  978: 
  979:     pDoc.write("  self.close()");
  980: 
  981:     pDoc.write("}");
  982: 
  983:     pDoc.write("<");
  984:     pDoc.write("/script>");
  985: 
  986:     pDoc.write("</head><body bgcolor=white>");
  987: 
  988:     pDoc.write("<form action=\\"inactive\\" name=\\"msgcenter\\">");
  989:     pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">");
  990:     pDoc.write("<font color=\\"green\\" size=+1>&nbsp;Compose Message for \"+fullname+\"</font><br><br>");
  991: 
  992:     pDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
  993:     pDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
  994:     pDoc.write("<td><b>Type</b></td><td><b>Include</b></td><td><b>Message</td></tr>");
  995: }
  996:     function displaySubject(msg,shwsel) {
  997:     pDoc = pWin.document;
  998:     pDoc.write("<tr bgcolor=\\"#ffffdd\\">");
  999:     pDoc.write("<td>Subject</td>");
 1000:     pDoc.write("<td align=\\"center\\"><input name=\\"subchk\\" type=\\"checkbox\\"" +shwsel+"></td>");
 1001:     pDoc.write("<td><input name=\\"msgsub\\" type=\\"text\\" value=\\""+msg+"\\"size=\\"60\\" maxlength=\\"80\\"></td></tr>");
 1002: }
 1003: 
 1004:   function displaySavedMsg(ctr,msg,shwsel) {
 1005:     pDoc = pWin.document;
 1006:     pDoc.write("<tr bgcolor=\\"#ffffdd\\">");
 1007:     pDoc.write("<td align=\\"center\\">"+ctr+"</td>");
 1008:     pDoc.write("<td align=\\"center\\"><input name=\\"msgn"+ctr+"\\" type=\\"checkbox\\"" +shwsel+"></td>");
 1009:     pDoc.write("<td><textarea name=\\"msg"+ctr+"\\" cols=\\"60\\" rows=\\"3\\">"+msg+"</textarea></td></tr>");
 1010: }
 1011: 
 1012:   function newMsg(newmsg,shwsel) {
 1013:     pDoc = pWin.document;
 1014:     pDoc.write("<tr bgcolor=\\"#ffffdd\\">");
 1015:     pDoc.write("<td align=\\"center\\">New</td>");
 1016:     pDoc.write("<td align=\\"center\\"><input name=\\"newmsgchk\\" type=\\"checkbox\\"" +shwsel+"></td>");
 1017:     pDoc.write("<td><textarea name=\\"newmsg\\" cols=\\"60\\" rows=\\"3\\" onchange=\\"javascript:this.form.newmsgchk.checked=true\\" >"+newmsg+"</textarea></td></tr>");
 1018: }
 1019: 
 1020:   function msgTail() {
 1021:     pDoc = pWin.document;
 1022:     pDoc.write("</table>");
 1023:     pDoc.write("</td></tr></table>&nbsp;");
 1024:     pDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");
 1025:     pDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br><br>");
 1026:     pDoc.write("</form>");
 1027:     pDoc.write("</body></html>");
 1028: }
 1029: 
 1030: //====================== Script for keyword highlight options ==============
 1031:   function kwhighlight() {
 1032:     var kwclr    = document.SCORE.kwclr.value;
 1033:     var kwsize   = document.SCORE.kwsize.value;
 1034:     var kwstyle  = document.SCORE.kwstyle.value;
 1035:     var redsel = "";
 1036:     var grnsel = "";
 1037:     var blusel = "";
 1038:     if (kwclr=="red")   {var redsel="checked"};
 1039:     if (kwclr=="green") {var grnsel="checked"};
 1040:     if (kwclr=="blue")  {var blusel="checked"};
 1041:     var sznsel = "";
 1042:     var sz1sel = "";
 1043:     var sz2sel = "";
 1044:     if (kwsize=="0")  {var sznsel="checked"};
 1045:     if (kwsize=="+1") {var sz1sel="checked"};
 1046:     if (kwsize=="+2") {var sz2sel="checked"};
 1047:     var synsel = "";
 1048:     var syisel = "";
 1049:     var sybsel = "";
 1050:     if (kwstyle=="")    {var synsel="checked"};
 1051:     if (kwstyle=="<i>") {var syisel="checked"};
 1052:     if (kwstyle=="<b>") {var sybsel="checked"};
 1053:     highlightCentral();
 1054:     highlightbody('red','red',redsel,'0','normal',sznsel,'','normal',synsel);
 1055:     highlightbody('green','green',grnsel,'+1','+1',sz1sel,'<i>','italic',syisel);
 1056:     highlightbody('blue','blue',blusel,'+2','+2',sz2sel,'<b>','bold',sybsel);
 1057:     highlightend();
 1058:     return;
 1059:   }
 1060: 
 1061:   function highlightCentral() {
 1062: //    if (window.hwdWin) window.hwdWin.close();
 1063:     var xpos = (screen.width-400)/2;
 1064:     xpos = (xpos < 0) ? '0' : xpos;
 1065:     var ypos = (screen.height-330)/2-30;
 1066:     ypos = (ypos < 0) ? '0' : ypos;
 1067: 
 1068:     hwdWin = window.open('', 'KeywordHighlightCentral', 'toolbar=no,location=no,scrollbars=no,width=400,height=300,screenx='+xpos+',screeny='+ypos);
 1069:     hwdWin.focus();
 1070:     var hDoc = hwdWin.document;
 1071:     hDoc.write("<html><head>");
 1072:     hDoc.write("<title>Highlight Central</title>");
 1073: 
 1074:     hDoc.write("<script language=javascript>");
 1075:     hDoc.write("function updateChoice(flag) {");
 1076:     hDoc.write("  opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);");
 1077:     hDoc.write("  opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);");
 1078:     hDoc.write("  opener.document.SCORE.kwstyle.value = opener.radioSelection(document.hlCenter.kwdstyle);");
 1079:     hDoc.write("  opener.document.SCORE.refresh.value = \\"on\\";");
 1080:     hDoc.write("  if (opener.document.SCORE.keywords.value!=\\"\\"){");
 1081:     hDoc.write("     opener.document.SCORE.submit();");
 1082:     hDoc.write("  }");
 1083:     hDoc.write("  self.close()");
 1084:     hDoc.write("}");
 1085: 
 1086:     hDoc.write("<");
 1087:     hDoc.write("/script>");
 1088: 
 1089:     hDoc.write("</head><body bgcolor=white>");
 1090: 
 1091:     hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">");
 1092:     hDoc.write("<font color=\\"green\\" size=+1>&nbsp;Keyword Highlight Options</font><br><br>");
 1093: 
 1094:     hDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
 1095:     hDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
 1096:     hDoc.write("<td><b>Text Color</b></td><td><b>Font Size</b></td><td><b>Font Style</td></tr>");
 1097:   }
 1098: 
 1099:   function highlightbody(clrval,clrtxt,clrsel,szval,sztxt,szsel,syval,sytxt,sysel) { 
 1100:     var hDoc = hwdWin.document;
 1101:     hDoc.write("<tr bgcolor=\\"#ffffdd\\">");
 1102:     hDoc.write("<td align=\\"left\\">");
 1103:     hDoc.write("<input name=\\"kwdclr\\" type=\\"radio\\" value=\\""+clrval+"\\" "+clrsel+">&nbsp;"+clrtxt+"</td>");
 1104:     hDoc.write("<td align=\\"left\\">");
 1105:     hDoc.write("<input name=\\"kwdsize\\" type=\\"radio\\" value=\\""+szval+"\\" "+szsel+">&nbsp;"+sztxt+"</td>");
 1106:     hDoc.write("<td align=\\"left\\">");
 1107:     hDoc.write("<input name=\\"kwdstyle\\" type=\\"radio\\" value=\\""+syval+"\\" "+sysel+">&nbsp;"+sytxt+"</td>");
 1108:     hDoc.write("</tr>");
 1109:   }
 1110: 
 1111:   function highlightend() { 
 1112:     var hDoc = hwdWin.document;
 1113:     hDoc.write("</table>");
 1114:     hDoc.write("</td></tr></table>&nbsp;");
 1115:     hDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:updateChoice(1)\\">&nbsp;&nbsp;");
 1116:     hDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br><br>");
 1117:     hDoc.write("</form>");
 1118:     hDoc.write("</body></html>");
 1119:   }
 1120: 
 1121: </script>
 1122: SUBJAVASCRIPT
 1123: }
 1124: 
 1125: #--- displays the grading box, used in essay type problem and grading by page/sequence
 1126: sub gradeBox {
 1127:     my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_;
 1128: 
 1129:     my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').
 1130: 	'/check.gif" height="16" border="0" />';
 1131: 
 1132:     my $wgt    = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb,$udom,$uname);
 1133:     my $wgtmsg = ($wgt > 0 ? '(problem weight)' : 
 1134: 		  '<font color="red">problem weight assigned by computer</font>');
 1135:     $wgt       = ($wgt > 0 ? $wgt : '1');
 1136:     my $score  = ($$record{'resource.'.$partid.'.awarded'} eq '' ?
 1137: 		  '' : $$record{'resource.'.$partid.'.awarded'}*$wgt);
 1138:     my $result='<input type="hidden" name="WGT'.$counter.'_'.$partid.'" value="'.$wgt.'" />'."\n";
 1139: 
 1140:     $result.='<table border="0"><tr><td>'.
 1141: 	'<b>Part </b>'.$partid.' <b>Points: </b></td><td>'."\n";
 1142: 
 1143:     my $ctr = 0;
 1144:     $result.='<table border="0"><tr>'."\n";  # display radio buttons in a nice table 10 across
 1145:     while ($ctr<=$wgt) {
 1146: 	$result.= '<td><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '.
 1147: 	    'onclick="javascript:writeBox(this.form,\''.$counter.'_'.$partid.'\','.
 1148: 	    $ctr.')" value="'.$ctr.'" '.
 1149: 	    ($score eq $ctr ? 'checked':'').' /> '.$ctr."</td>\n";
 1150: 	$result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
 1151: 	$ctr++;
 1152:     }
 1153:     $result.='</tr></table>';
 1154: 
 1155:     $result.='</td><td>&nbsp;<b>or</b>&nbsp;</td>'."\n";
 1156:     $result.='<td><input type="text" name="GD_BOX'.$counter.'_'.$partid.'"'.
 1157: 	($score ne ''? ' value = "'.$score.'"':'').' size="4" '.
 1158: 	'onChange="javascript:updateRadio(this.form,\''.$counter.'_'.$partid.'\','.
 1159: 	$wgt.')" /></td>'."\n";
 1160:     $result.='<td>/'.$wgt.' '.$wgtmsg.
 1161: 	($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? '&nbsp;'.$checkIcon : '').
 1162: 	' </td><td>'."\n";
 1163: 
 1164:     $result.='<select name="GD_SEL'.$counter.'_'.$partid.'" '.
 1165: 	'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n";
 1166:     if ($$record{'resource.'.$partid.'.solved'} eq 'excused') {
 1167: 	$result.='<option> </option>'.
 1168: 	    '<option selected="on">excused</option></select>'."\n";
 1169:     } else {
 1170: 	$result.='<option selected="on"> </option>'.
 1171: 	    '<option>excused</option></select>'."\n";
 1172:     }
 1173:     $result.="&nbsp&nbsp\n";
 1174:     $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n".
 1175: 	'<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n".
 1176: 	'<input type="hidden" name="solved'.$counter.'_'.$partid.'" value="'.
 1177: 	$$record{'resource.'.$partid.'.solved'}.'" />'."\n";
 1178:     $result.='</td></tr></table>'."\n";
 1179:     return $result;
 1180: }
 1181: 
 1182: sub show_problem {
 1183:     my ($request,$symb,$uname,$udom,$removeform,$viewon) = @_;
 1184:     my $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom,
 1185: 						      $ENV{'request.course.id'});
 1186:     if ($removeform) {
 1187: 	$rendered=~s|<form(.*?)>||g;
 1188: 	$rendered=~s|</form>||g;
 1189: 	$rendered=~s|name="submit"|name="would_have_been_submit"|g;
 1190:     }
 1191:     my $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom,
 1192: 							   $ENV{'request.course.id'});
 1193:     if ($removeform) {
 1194: 	$companswer=~s|<form(.*?)>||g;
 1195: 	$companswer=~s|</form>||g;
 1196: 	$rendered=~s|name="submit"|name="would_have_been_submit"|g;
 1197:     }
 1198:     my $result.='<table border="0" width="100%"><tr><td bgcolor="#777777">';
 1199:     $result.='<table border="0" width="100%">';
 1200:     $result.='<tr><td bgcolor="#e6ffff"><b> View of the problem - '.$ENV{'form.fullname'}.
 1201: 	'</b></td></tr>' if ($viewon);
 1202:     $result.='<tr><td bgcolor="#ffffff">'.$rendered.'<br />';
 1203:     $result.='<b>Correct answer:</b><br />'.$companswer;
 1204:     $result.='</td></tr></table>';
 1205:     $result.='</td></tr></table><br />';
 1206:     return $result;
 1207: }
 1208: 
 1209: # --------------------------- show submissions of a student, option to grade 
 1210: sub submission {
 1211:     my ($request,$counter,$total) = @_;
 1212: 
 1213:     (my $url=$ENV{'form.url'})=~s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
 1214:     my ($uname,$udom)     = ($ENV{'form.student'},$ENV{'form.userdom'});
 1215:     $udom = ($udom eq '' ? $ENV{'user.domain'} : $udom); #has form.userdom changed for a student?
 1216:     my $usec = &Apache::lonnet::getsection($udom,$uname,$ENV{'request.course.id'});
 1217:     $ENV{'form.fullname'} = &get_fullname ($uname,$udom) if $ENV{'form.fullname'} eq '';
 1218: 
 1219:     my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));
 1220:     if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
 1221: 
 1222:     if (!&canview($usec)) {
 1223: 	$request->print('<font color="red">Unable to view requested student.('.
 1224: 			$uname.$udom.$usec.$ENV{'request.course.id'}.')</font>');
 1225: 	$request->print(&show_grading_menu_form($symb,$url));
 1226: 	return;
 1227:     }
 1228: 
 1229:     $ENV{'form.lastSub'} = ($ENV{'form.lastSub'} eq '' ? 'datesub' : $ENV{'form.lastSub'});
 1230:     my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : '');
 1231:     my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').
 1232: 	'/check.gif" height="16" border="0" />';
 1233: 
 1234:     # header info
 1235:     if ($counter == 0) {
 1236: 	&sub_page_js($request);
 1237: 	&sub_page_kw_js($request) if ($ENV{'form.handgrade'} eq 'yes');
 1238: 	$ENV{'form.probTitle'} = $ENV{'form.probTitle'} eq '' ? 
 1239: 	    &Apache::lonnet::gettitle($symb) : $ENV{'form.probTitle'};
 1240: 
 1241: 	$request->print('<h3>&nbsp;<font color="#339933">Submission Record</font></h3>'."\n".
 1242: 			'<font size=+1>&nbsp;<b>Resource: </b>'.$ENV{'form.probTitle'}.'</font>'."\n");
 1243: 
 1244: 	if ($ENV{'form.handgrade'} eq 'no') {
 1245: 	    my $checkMark='<br /><br />&nbsp;<b>Note:</b> Part(s) graded correct by the computer is marked with a '.
 1246: 		$checkIcon.' symbol.'."\n";
 1247: 	    $request->print($checkMark);
 1248: 	}
 1249: 
 1250: 	# option to display problem, only once else it cause problems 
 1251:         # with the form later since the problem has a form.
 1252: 	if ($ENV{'form.vProb'} eq 'yes' or !$ENV{'form.vProb'}) {
 1253: 	    $request->print(&show_problem($request,$symb,$uname,$udom,0,1));
 1254: 	}
 1255: 	
 1256: 	# kwclr is the only variable that is guaranteed to be non blank 
 1257:         # if this subroutine has been called once.
 1258: 	my %keyhash = ();
 1259: 	if ($ENV{'form.kwclr'} eq '' && $ENV{'form.handgrade'} eq 'yes') {
 1260: 	    %keyhash = &Apache::lonnet::dump('nohist_handgrade',
 1261: 					     $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
 1262: 					     $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
 1263: 
 1264: 	    my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'};
 1265: 	    $ENV{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : '';
 1266: 	    $ENV{'form.kwclr'}    = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red';
 1267: 	    $ENV{'form.kwsize'}   = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';
 1268: 	    $ENV{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
 1269: 	    $ENV{'form.msgsub'}   = $keyhash{$symb.'_subject'} ne '' ? 
 1270: 		$keyhash{$symb.'_subject'} : $ENV{'form.probTitle'};
 1271: 	    $ENV{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0';
 1272: 	}
 1273: 	my $overRideScore = $ENV{'form.overRideScore'} eq '' ? 'no' : $ENV{'form.overRideScore'};
 1274: 
 1275: 	$request->print('<form action="/adm/grades" method="post" name="SCORE">'."\n".
 1276: 			'<input type="hidden" name="command"    value="handgrade" />'."\n".
 1277: 			'<input type="hidden" name="saveState"  value="'.$ENV{'form.saveState'}.'" />'."\n".
 1278: 			'<input type="hidden" name="Status"     value="'.$ENV{'form.Status'}.'" />'."\n".
 1279: 			'<input type="hidden" name="overRideScore" value="'.$overRideScore.'" />'."\n".
 1280: 			'<input type="hidden" name="probTitle"  value="'.$ENV{'form.probTitle'}.'" />'."\n".
 1281: 			'<input type="hidden" name="refresh"    value="off" />'."\n".
 1282: 			'<input type="hidden" name="studentNo"  value="" />'."\n".
 1283: 			'<input type="hidden" name="gradeOpt"   value="" />'."\n".
 1284: 			'<input type="hidden" name="symb"       value="'.$symb.'" />'."\n".
 1285: 			'<input type="hidden" name="url"        value="'.$url.'" />'."\n".
 1286: 			'<input type="hidden" name="showgrading" value="'.$ENV{'form.showgrading'}.'" />'."\n".
 1287: 			'<input type="hidden" name="vProb"      value="'.$ENV{'form.vProb'}.'" />'."\n".
 1288: 			'<input type="hidden" name="lastSub"    value="'.$ENV{'form.lastSub'}.'" />'."\n".
 1289: 			'<input type="hidden" name="section"    value="'.$ENV{'form.section'}.'">'."\n".
 1290: 			'<input type="hidden" name="submitonly" value="'.$ENV{'form.submitonly'}.'">'."\n".
 1291: 			'<input type="hidden" name="handgrade"  value="'.$ENV{'form.handgrade'}.'">'."\n".
 1292: 			'<input type="hidden" name="NCT"'.
 1293: 			' value="'.($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : $total+1).'" />'."\n");
 1294: 	if ($ENV{'form.handgrade'} eq 'yes') {
 1295: 	    $request->print('<input type="hidden" name="keywords" value="'.$ENV{'form.keywords'}.'" />'."\n".
 1296: 			    '<input type="hidden" name="kwclr"    value="'.$ENV{'form.kwclr'}.'" />'."\n".
 1297: 			    '<input type="hidden" name="kwsize"   value="'.$ENV{'form.kwsize'}.'" />'."\n".
 1298: 			    '<input type="hidden" name="kwstyle"  value="'.$ENV{'form.kwstyle'}.'" />'."\n".
 1299: 			    '<input type="hidden" name="msgsub"   value="'.$ENV{'form.msgsub'}.'" />'."\n".
 1300: 			    '<input type="hidden" name="shownSub" value="0" />'."\n".
 1301: 			    '<input type="hidden" name="savemsgN" value="'.$ENV{'form.savemsgN'}.'" />'."\n");
 1302: 	}
 1303: 	
 1304: 	my ($cts,$prnmsg) = (1,'');
 1305: 	while ($cts <= $ENV{'form.savemsgN'}) {
 1306: 	    $prnmsg.='<input type="hidden" name="savemsg'.$cts.'" value="'.
 1307: 		(!exists($keyhash{$symb.'_savemsg'.$cts}) ? 
 1308: 		 &Apache::lonfeedback::clear_out_html($ENV{'form.savemsg'.$cts}) :
 1309: 		 &Apache::lonfeedback::clear_out_html($keyhash{$symb.'_savemsg'.$cts})).
 1310: 		'" />'."\n".
 1311: 		'<input type="hidden" name="shownOnce'.$cts.'" value="0" />'."\n";
 1312: 	    $cts++;
 1313: 	}
 1314: 	$request->print($prnmsg);
 1315: 
 1316: 	if ($ENV{'form.handgrade'} eq 'yes' && $ENV{'form.showgrading'} eq 'yes') {
 1317: #
 1318: # Print out the keyword options line
 1319: #
 1320: 	    $request->print(<<KEYWORDS);
 1321: &nbsp;<b>Keyword Options:</b>&nbsp;
 1322: <a href="javascript:keywords(document.SCORE)"; TARGET=_self>List</a>&nbsp; &nbsp;
 1323: <a href="#" onMouseDown="javascript:getSel(); return false"
 1324:  CLASS="page">Paste Selection to List</a>&nbsp; &nbsp;
 1325: <a href="javascript:kwhighlight()"; TARGET=_self>Highlight Attribute</a><br /><br />
 1326: KEYWORDS
 1327: #
 1328: # Load the other essays for similarity check
 1329: #
 1330:             my $essayurl=&Apache::lonnet::declutter($url);
 1331: 	    my ($adom,$aname,$apath)=($essayurl=~/^(\w+)\/(\w+)\/(.*)$/);
 1332: 	    $apath=&Apache::lonnet::escape($apath);
 1333: 	    $apath=~s/\W/\_/gs;
 1334: 	    %oldessays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname);
 1335:         }
 1336:     }
 1337: 
 1338:     if ($ENV{'form.vProb'} eq 'all') {
 1339: 	$request->print('<br /><br /><br />') if ($counter > 0);
 1340: 	$request->print(&show_problem($request,$symb,$uname,$udom,1,1));
 1341:     }
 1342:     my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
 1343:     my ($partlist,$handgrade) = &response_type($url);
 1344: 
 1345:     # Display student info
 1346:     $request->print(($counter == 0 ? '' : '<br />'));
 1347:     my $result='<table border="0" width=100%><tr><td bgcolor="#777777">'."\n".
 1348: 	'<table border="0" width=100%><tr bgcolor="#edffff"><td>'."\n";
 1349: 
 1350:     $result.='<b>Fullname: </b>'.$ENV{'form.fullname'}.
 1351: 	'<font color="#999999">&nbsp; &nbsp;Username: '.$uname.
 1352: 	($ENV{'user.domain'} eq $udom ? '' : ' ('.$udom.')').'</font><br />'."\n";
 1353:     $result.='<input type="hidden" name="name'.$counter.
 1354: 	'" value="'.$ENV{'form.fullname'}.'" />'."\n";
 1355: 
 1356:     # If any part of the problem is an essay-response (handgraded), then check for collaborators
 1357:     my @col_fullnames;
 1358:     my ($classlist,$fullname);
 1359:     if ($ENV{'form.handgrade'} eq 'yes') {
 1360: 	($classlist,undef,$fullname) = &getclasslist('all','0');
 1361: 	for (keys (%$handgrade)) {
 1362: 	    my $ncol = &Apache::lonnet::EXT('resource.'.$_.
 1363: 					    '.maxcollaborators',
 1364:                                             $symb,$udom,$uname);
 1365: 	    next if ($ncol <= 0);
 1366:             s/\_/\./g;
 1367:             next if ($record{'resource.'.$_.'.collaborators'} eq '');
 1368:             my @goodcollaborators = ();
 1369:             my @badcollaborators  = ();
 1370: 	    foreach (split(/,?\s+/,$record{'resource.'.$_.'.collaborators'})) { 
 1371: 		$_ =~ s/[\$\^\(\)]//g;
 1372: 		next if ($_ eq '');
 1373: 		my ($co_name,$co_dom) = split /\@|:/,$_;
 1374: 		$co_dom = $udom if (! defined($co_dom) || $co_dom =~ /^domain$/i);
 1375: 		next if ($co_name eq $uname && $co_dom eq $udom);
 1376: 		# Doing this grep allows 'fuzzy' specification
 1377: 		my @Matches = grep /^$co_name:$co_dom$/i,keys %$classlist;
 1378: 		if (! scalar(@Matches)) {
 1379: 		    push @badcollaborators,$_;
 1380: 		} else {
 1381: 		    push @goodcollaborators, @Matches;
 1382: 		}
 1383: 	    }
 1384:             if (scalar(@goodcollaborators) != 0) {
 1385:                 $result.='<b>Collaborators: </b>';
 1386:                 foreach (@goodcollaborators) {
 1387: 		    my ($lastname,$givenn) = split(/,/,$$fullname{$_});
 1388: 		    push @col_fullnames, $givenn.' '.$lastname;
 1389: 		    $result.=$$fullname{$_}.'&nbsp; &nbsp; &nbsp;';
 1390: 		}
 1391:                 $result.='<br />'."\n";
 1392: 		$result.='<input type="hidden" name="collaborator'.$counter.
 1393: 		    '" value="'.(join ':',@goodcollaborators).'" />'."\n";
 1394: 	    }
 1395: 	    if (scalar(@badcollaborators) > 0) {
 1396: 		$result.='<table border="0"><tr bgcolor="#ffbbbb"><td>';
 1397: 		$result.='This student has submitted ';
 1398: 		$result.=(scalar(@badcollaborators) == 1) ? 'an invalid collaborator' : 'invalid collaborators';
 1399: 		$result .= ': '.join(', ',@badcollaborators);
 1400: 		$result .= '</td></tr></table>';
 1401: 	    }         
 1402: 	    if (scalar(@badcollaborators > $ncol)) {
 1403: 		$result .= '<table border="0"><tr bgcolor="#ffbbbb"><td>';
 1404: 		$result .= 'This student has submitted too many '.
 1405: 		    'collaborators.  Maximum is '.$ncol.'.';
 1406: 		$result .= '</td></tr></table>';
 1407: 	    }
 1408: 	}
 1409:     }
 1410:     $request->print($result."\n");
 1411: 
 1412:     # print student answer/submission
 1413:     # Options are (1) Handgaded submission only
 1414:     #             (2) Last submission, includes submission that is not handgraded 
 1415:     #                  (for multi-response type part)
 1416:     #             (3) Last submission plus the parts info
 1417:     #             (4) The whole record for this student
 1418:     if ($ENV{'form.lastSub'} =~ /^(lastonly|hdgrade)$/) {
 1419: 	if ($ENV{'form.'.$uname.':'.$udom.':submitted_by'}) {
 1420: 	    my $submitby=''.
 1421: 		'<b>Collaborative submission by: </b>'.
 1422: 		'<a href="javascript:viewSubmitter(\''.
 1423: 		$ENV{'form.'.$uname.':'.$udom.':submitted_by'}.
 1424: 		'\')"; TARGET=_self>'.
 1425: 		$$fullname{$ENV{'form.'.$uname.':'.$udom.':submitted_by'}}.'</a>';
 1426: 	    $request->print($submitby);
 1427: 	} else {
 1428: 	    my ($string,$timestamp)= &get_last_submission (\%record);
 1429: 	    my $lastsubonly=''.
 1430: 		($$timestamp eq '' ? '' : '<b>Date Submitted:</b> '.
 1431: 		 $$timestamp)."</td></tr>\n";
 1432: 	    if ($$timestamp eq '') {
 1433: 		$lastsubonly.='<tr><td bgcolor="#ffffe6">'.$$string[0]; 
 1434: 	    } else {
 1435: 		for my $part (sort keys(%$handgrade)) {
 1436: 		    my ($responsetype,$foo) = split(/:/,$$handgrade{$part});
 1437: 		    my ($partid,$respid) = split(/_/,$part);
 1438: 		    if (!exists($record{'resource.'.$partid.'.'.$respid.'.submission'})) {
 1439: 			$lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part '.
 1440: 			    $partid.'</b> <font color="#999999">( ID '.$respid.
 1441: 			    ' )</font>&nbsp; &nbsp;Nothing submitted - no attempts<br /><br />';
 1442:  		    } else {
 1443: 			foreach (@$string) {
 1444: 			    my ($partid,$respid) = /^resource\.(\w+)\.(\w+)\.submission/;
 1445: 			    if ($part eq ($partid.'_'.$respid)) {
 1446: 				my ($ressub,$subval) = split(/:/,$_,2);
 1447:                             # Similarity check
 1448: 				my $similar='';
 1449: 				my ($oname,$odom,$ocrsid,$oessay,$osim)=&most_similar($uname,$udom,$subval);
 1450: 				if ($osim) {
 1451: 				    $osim=int($osim*100.0);
 1452: 				    $similar='<hr /><h3><font color="#FF0000">Essay is '.$osim.
 1453: 					'% similar to an essay by '.&Apache::loncommon::plainname($oname,$odom).
 1454: 					'</font></h3><blockquote><i>'.
 1455: 					&keywords_highlight($oessay).'</i></blockquote><hr />';
 1456: 				}
 1457: 				$lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part '.
 1458: 				    $partid.'</b> <font color="#999999">( ID '.$respid.
 1459: 				    ' )</font>&nbsp; &nbsp;'.
 1460: 				    ($record{"resource.$partid.$respid.uploadedurl"}?
 1461: 				     '<a href="'.
 1462: 				     &Apache::lonnet::tokenwrapper($record{"resource.$partid.$respid.uploadedurl"}).
 1463: 				     '"><img src="/adm/lonIcons/unknown.gif" border=0"> File uploaded by student</a> '.
 1464: 				     '<font color="red" size="1">Like all files provided by users, '.
 1465: 				     'this file may contain virusses</font><br />':'').
 1466: 				     '<b>Submitted Answer: </b>'.($responsetype =~ /^(essay|option)$/ ? 
 1467: 								  '<blockquote>' : '').
 1468: #				     &cleanRecord(&keywords_highlight($subval),$responsetype).
 1469: 				     &cleanRecord($subval,$responsetype,$symb).
 1470: 				     ($responsetype =~ /^(essay|option)$/ ? '</blockquote><br />' : 
 1471: 				      '<br /><br />').$similar."\n"
 1472: 				     if ($ENV{'form.lastSub'} eq 'lastonly' || 
 1473: 					 ($ENV{'form.lastSub'} eq 'hdgrade' && 
 1474: 					  $$handgrade{$part} =~ /:yes$/));
 1475: 			    }
 1476: 			}
 1477: 		    }
 1478: 		}
 1479: 	    }
 1480: 	    $lastsubonly.='</td></tr><tr bgcolor="#ffffff"><td>'."\n";
 1481: 	    $request->print($lastsubonly);
 1482: 	}
 1483:     } elsif ($ENV{'form.lastSub'} eq 'datesub') {
 1484: 	my (undef,$responseType,undef,$parts) = &showResourceInfo($url);
 1485: 	$request->print(&displaySubByDates(\$symb,\%record,$parts,$responseType,$checkIcon));
 1486:     } elsif ($ENV{'form.lastSub'} =~ /^(last|all)$/) {
 1487: 	$request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,
 1488: 								 $ENV{'request.course.id'},
 1489: 								 $last,'.submission',
 1490: 								 'Apache::grades::keywords_highlight'));
 1491:     }
 1492: 
 1493:     $request->print('<input type="hidden" name="unamedom'.$counter.'" value="'.$uname.':'
 1494: 	.$udom.'" />'."\n");
 1495:     
 1496:     # return if view submission with no grading option
 1497:     if ($ENV{'form.showgrading'} eq '' || (!&canmodify($usec))) {
 1498: 	my $toGrade.='<input type="button" value="Grade Student" '.
 1499: 	    'onClick="javascript:checksubmit(this.form,\'Grade Student\',\''
 1500: 	    .$counter.'\');" TARGET=_self> &nbsp;'."\n" if (&canmodify($usec));
 1501: 	$toGrade.='</td></tr></table></td></tr></table></form>'."\n";
 1502: 	$toGrade.=&show_grading_menu_form($symb,$url) 
 1503: 	    if (($ENV{'form.command'} eq 'submission') || 
 1504: 		($ENV{'form.command'} eq 'processGroup' && $counter == $total));
 1505: 	$request = print($toGrade);
 1506: 	return;
 1507:     }
 1508: 
 1509:     # essay grading message center
 1510:     if ($ENV{'form.handgrade'} eq 'yes') {
 1511: 	my ($lastname,$givenn) = split(/,/,$ENV{'form.fullname'});
 1512: 	my $msgfor = $givenn.' '.$lastname;
 1513: 	if (scalar(@col_fullnames) > 0) {
 1514: 	    my $lastone = pop @col_fullnames;
 1515: 	    $msgfor .= ', '.(join ', ',@col_fullnames).' and '.$lastone.'.';
 1516: 	}
 1517: 	$msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript
 1518: #	$result.='<tr><td bgcolor="#ffffff">'."\n".
 1519: 	$result='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".
 1520: 	    '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n";
 1521: 	$result.='&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.
 1522: 	    ',\''.$msgfor.'\')"; TARGET=_self>'.
 1523: 	    'Compose Message to student'.(scalar(@col_fullnames) >= 1 ? 's' : '').'</a> &nbsp;'.
 1524: 	    '<img src="'.$request->dir_config('lonIconsURL').
 1525: 	    '/mailbkgrd.gif" width="14" height="10" name="mailicon'.$counter.'" />'."\n".
 1526: 	    '<br />&nbsp;(Message will be sent when you click on Save & Next below.)'."\n" 
 1527: 	    if ($ENV{'form.handgrade'} eq 'yes');
 1528: 	$request->print($result);
 1529:     }
 1530: 
 1531:     my %seen = ();
 1532:     my @partlist;
 1533:     for (sort keys(%$handgrade)) {
 1534: 	my ($partid,$respid) = split(/_/);
 1535: 	next if ($seen{$partid} > 0);
 1536: 	$seen{$partid}++;
 1537: 	next if ($$handgrade{$_} =~ /:no$/ && $ENV{'form.lastSub'} =~ /^(hdgrade)$/);
 1538: 	push @partlist,$partid;
 1539: 
 1540: 	$request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));
 1541:     }
 1542:     $result='<input type="hidden" name="partlist'.$counter.
 1543: 	'" value="'.(join ":",@partlist).'" />'."\n";
 1544:     my $ctr = 0;
 1545:     while ($ctr < scalar(@partlist)) {
 1546: 	$result.='<input type="hidden" name="partid'.$counter.'_'.$ctr.'" value="'.
 1547: 	    $partlist[$ctr].'" />'."\n";
 1548: 	$ctr++;
 1549:     }
 1550:     $request->print($result.'</td></tr></table></td></tr></table>'."\n");
 1551: 
 1552:     # print end of form
 1553:     if ($counter == $total) {
 1554: 	my $endform='<table border="0"><tr><td>'."\n";
 1555: 	$endform.='<input type="button" value="Save & Next" '.
 1556: 	    'onClick="javascript:checksubmit(this.form,\'Save & Next\','.
 1557: 	    $total.','.scalar(@partlist).');" TARGET=_self> &nbsp;'."\n";
 1558: 	my $ntstu ='<select name="NTSTU">'.
 1559: 	    '<option>1</option><option>2</option>'.
 1560: 	    '<option>3</option><option>5</option>'.
 1561: 	    '<option>7</option><option>10</option></select>'."\n";
 1562: 	my $nsel = ($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : '1');
 1563: 	$ntstu =~ s/<option>$nsel</<option selected="on">$nsel</;
 1564: 	$endform.=$ntstu.'student(s) &nbsp;&nbsp;';
 1565: 	$endform.='<input type="button" value="Next" '.
 1566: 	    'onClick="javascript:checksubmit(this.form,\'Next\');" TARGET=_self> &nbsp;'."\n".
 1567: 	    '<input type="button" value="Previous" '.
 1568: 	    'onClick="javascript:checksubmit(this.form,\'Previous\');" TARGET=_self> &nbsp;';
 1569: 	$endform.='(Next and Previous do not save the scores.)'."\n" ;
 1570: 	$endform.='</td><tr></table></form>';
 1571: 	$endform.=&show_grading_menu_form($symb,$url);
 1572: 	$request->print($endform);
 1573:     }
 1574:     return '';
 1575: }
 1576: 
 1577: #--- Retrieve the last submission for all the parts
 1578: sub get_last_submission {
 1579:     my ($returnhash)=@_;
 1580:     my (@string,$timestamp);
 1581:     if ($$returnhash{'version'}) {
 1582: 	my %lasthash=();
 1583: 	my ($version);
 1584: 	for ($version=1;$version<=$$returnhash{'version'};$version++) {
 1585: 	    foreach (sort(split(/\:/,$$returnhash{$version.':keys'}))) {
 1586: 		$lasthash{$_}=$$returnhash{$version.':'.$_};
 1587: 		   $timestamp = scalar(localtime($$returnhash{$version.':timestamp'}));
 1588: 	    }
 1589: 	}
 1590: 	foreach ((keys %lasthash)) {
 1591: 	    if ($_ =~ /\.submission$/) {
 1592: 		my ($partid,$foo) = split(/submission$/,$_);
 1593: 		my $draft  = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ?
 1594: 		    '<font color="red">Draft Copy</font> ' : '';
 1595: 		push @string, (join(':',$_,$draft.$lasthash{$_}));
 1596: 	    }
 1597: 	}
 1598:     }
 1599:     @string = $string[0] eq '' ? 'Nothing submitted - no attempts.' : @string;
 1600:     return \@string,\$timestamp;
 1601: }
 1602: 
 1603: #--- High light keywords, with style choosen by user.
 1604: sub keywords_highlight {
 1605:     my $string    = shift;
 1606:     my $size      = $ENV{'form.kwsize'} eq '0' ? '' : 'size='.$ENV{'form.kwsize'};
 1607:     my $styleon   = $ENV{'form.kwstyle'} eq ''  ? '' : $ENV{'form.kwstyle'};
 1608:     (my $styleoff = $styleon) =~ s/\</\<\//;
 1609:     my @keylist   = split(/[,\s+]/,$ENV{'form.keywords'});
 1610:     foreach (@keylist) {
 1611: 	$string =~ s/\b\Q$_\E(\b|\.)/<font color\=$ENV{'form.kwclr'} $size\>$styleon$_$styleoff<\/font>/gi;
 1612:     }
 1613:     return $string;
 1614: }
 1615: 
 1616: #--- Called from submission routine
 1617: sub processHandGrade {
 1618:     my ($request) = shift;
 1619:     my $url    = $ENV{'form.url'};
 1620:     my $symb   = $ENV{'form.symb'};
 1621:     my $button = $ENV{'form.gradeOpt'};
 1622:     my $ngrade = $ENV{'form.NCT'};
 1623:     my $ntstu  = $ENV{'form.NTSTU'};
 1624:     if ($button eq 'Save & Next') {
 1625: 	my $ctr = 0;
 1626: 	while ($ctr < $ngrade) {
 1627: 	    my ($uname,$udom) = split(/:/,$ENV{'form.unamedom'.$ctr});
 1628: 	    my ($errorflag,$pts,$wgt) = &saveHandGrade($request,$url,$symb,$uname,$udom,$ctr);
 1629: 	    if ($errorflag eq 'no_score') {
 1630: 		$ctr++;
 1631: 		next;
 1632: 	    }
 1633: 	    if ($errorflag eq 'not_allowed') {
 1634: 		$request->print("<font color=\"red\">Not allowed to modify grades for $uname:$udom</font>");
 1635: 		$ctr++;
 1636: 		next;
 1637: 	    }
 1638: 	    my $includemsg = $ENV{'form.includemsg'.$ctr};
 1639: 	    my ($subject,$message,$msgstatus) = ('','','');
 1640: 	    if ($includemsg =~ /savemsg|newmsg\Q$ctr\E/) {
 1641: 		$subject = $ENV{'form.msgsub'} if ($includemsg =~ /^msgsub/);
 1642: 		my (@msgnum) = split(/,/,$includemsg);
 1643: 		foreach (@msgnum) {
 1644: 		    $message.=$ENV{'form.'.$_} if ($_ =~ /savemsg|newmsg/ && $_ ne '');
 1645: 		}
 1646: 		$message =&Apache::lonfeedback::clear_out_html($message);
 1647: 		$message.="\n\nPoint".($pts > 1 ? 's':'').' awarded = '.$pts.' out of '.$wgt;
 1648: 		$message.=" for <a href=\"".
 1649: 		    &Apache::lonnet::clutter($url).
 1650: 		    "?symb=$symb\">$ENV{'form.probTitle'}</a>";
 1651: 		$msgstatus = &Apache::lonmsg::user_normal_msg ($uname,$udom,
 1652: 							       $ENV{'form.msgsub'},$message);
 1653: 	    }
 1654: 	    if ($ENV{'form.collaborator'.$ctr}) {
 1655: 		my (@collaborators) = split(/:/,$ENV{'form.collaborator'.$ctr});
 1656: 		foreach (@collaborators) {
 1657: 		    my ($errorflag,$pts,$wgt) = 
 1658: 			&saveHandGrade($request,$url,$symb,$_,$udom,$ctr,$ENV{'form.unamedom'.$ctr});
 1659: 		    if ($errorflag eq 'not_allowed') {
 1660: 			$request->print("<font color=\"red\">Not allowed to modify grades for $_:$udom</font>");
 1661: 			next;
 1662: 		    } else {
 1663: 			if ($message ne '') {
 1664: 			    $msgstatus = &Apache::lonmsg::user_normal_msg ($_,$udom,
 1665: 									   $ENV{'form.msgsub'},
 1666: 									   $message);
 1667: 			}
 1668: 		    }
 1669: 		}
 1670: 	    }
 1671: 	    $ctr++;
 1672: 	}
 1673:     }
 1674: 
 1675:     if ($ENV{'form.handgrade'} eq 'yes') {
 1676: 	# Keywords sorted in alphabatical order
 1677: 	my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'};
 1678: 	my %keyhash = ();
 1679: 	$ENV{'form.keywords'}           =~ s/,\s{0,}|\s+/ /g;
 1680: 	$ENV{'form.keywords'}           =~ s/^\s+|\s+$//;
 1681: 	my (@keywords) = sort(split(/\s+/,$ENV{'form.keywords'}));
 1682: 	$ENV{'form.keywords'} = join(' ',@keywords);
 1683: 	$keyhash{$symb.'_keywords'}     = $ENV{'form.keywords'};
 1684: 	$keyhash{$symb.'_subject'}      = $ENV{'form.msgsub'};
 1685: 	$keyhash{$loginuser.'_kwclr'}   = $ENV{'form.kwclr'};
 1686: 	$keyhash{$loginuser.'_kwsize'}  = $ENV{'form.kwsize'};
 1687: 	$keyhash{$loginuser.'_kwstyle'} = $ENV{'form.kwstyle'};
 1688: 
 1689: 	# message center - Order of message gets changed. Blank line is eliminated.
 1690: 	# New messages are saved in ENV for the next student.
 1691: 	# All messages are saved in nohist_handgrade.db
 1692: 	my ($ctr,$idx) = (1,1);
 1693: 	while ($ctr <= $ENV{'form.savemsgN'}) {
 1694: 	    if ($ENV{'form.savemsg'.$ctr} ne '') {
 1695: 		$keyhash{$symb.'_savemsg'.$idx} = $ENV{'form.savemsg'.$ctr};
 1696: 		$idx++;
 1697: 	    }
 1698: 	    $ctr++;
 1699: 	}
 1700: 	$ctr = 0;
 1701: 	while ($ctr < $ngrade) {
 1702: 	    if ($ENV{'form.newmsg'.$ctr} ne '') {
 1703: 		$keyhash{$symb.'_savemsg'.$idx} = $ENV{'form.newmsg'.$ctr};
 1704: 		$ENV{'form.savemsg'.$idx} = $ENV{'form.newmsg'.$ctr};
 1705: 		$idx++;
 1706: 	    }
 1707: 	    $ctr++;
 1708: 	}
 1709: 	$ENV{'form.savemsgN'} = --$idx;
 1710: 	$keyhash{$symb.'_savemsgN'} = $ENV{'form.savemsgN'};
 1711: 	my $putresult = &Apache::lonnet::put
 1712: 	    ('nohist_handgrade',\%keyhash,
 1713: 	     $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
 1714: 	     $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
 1715:     }
 1716:     # Called by Save & Refresh from Highlight Attribute Window
 1717:     my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'1');
 1718:     if ($ENV{'form.refresh'} eq 'on') {
 1719: 	my ($ctr,$total) = (0,0);
 1720: 	while ($ctr < $ngrade) {
 1721: 	    $total++ if  $ENV{'form.unamedom'.$ctr} ne '';
 1722: 	    $ctr++;
 1723: 	}
 1724: 	$ENV{'form.NTSTU'}=$ngrade;
 1725: 	$ctr = 0;
 1726: 	while ($ctr < $total) {
 1727: 	    my $processUser = $ENV{'form.unamedom'.$ctr};
 1728: 	    ($ENV{'form.student'},$ENV{'form.userdom'}) = split(/:/,$processUser);
 1729: 	    $ENV{'form.fullname'} = $$fullname{$processUser};
 1730: 	    &submission($request,$ctr,$total-1);
 1731: 	    $ctr++;
 1732: 	}
 1733: 	return '';
 1734:     }
 1735: 
 1736: # Go directly to grade student - from submission or link from chart page
 1737:     if ($button eq 'Grade Student') {
 1738: 	(undef,undef,$ENV{'form.handgrade'},undef,undef) = &showResourceInfo($url);
 1739: 	my $processUser = $ENV{'form.unamedom'.$ENV{'form.studentNo'}};
 1740: 	($ENV{'form.student'},$ENV{'form.userdom'}) = split(/:/,$processUser);
 1741: 	$ENV{'form.fullname'} = $$fullname{$processUser};
 1742: 	&submission($request,0,0);
 1743: 	return '';
 1744:     }
 1745: 
 1746:     # Get the next/previous one or group of students
 1747:     my $firststu = $ENV{'form.unamedom0'};
 1748:     my $laststu = $ENV{'form.unamedom'.($ngrade-1)};
 1749:     my $ctr = 2;
 1750:     while ($laststu eq '') {
 1751: 	$laststu  = $ENV{'form.unamedom'.($ngrade-$ctr)};
 1752: 	$ctr++;
 1753: 	$laststu = $firststu if ($ctr > $ngrade);
 1754:     }
 1755: 
 1756:     my (@parsedlist,@nextlist);
 1757:     my ($nextflg) = 0;
 1758:     foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
 1759: 	if ($nextflg == 1 && $button =~ /Next$/) {
 1760: 	    push @parsedlist,$_;
 1761: 	}
 1762: 	$nextflg = 1 if ($_ eq $laststu);
 1763: 	if ($button eq 'Previous') {
 1764: 	    last if ($_ eq $firststu);
 1765: 	    push @parsedlist,$_;
 1766: 	}
 1767:     }
 1768:     $ctr = 0;
 1769:     my ($partlist,$handgrade) = &response_type($ENV{'form.url'});
 1770:     @parsedlist = reverse @parsedlist if ($button eq 'Previous');
 1771:     foreach my $student (@parsedlist) {
 1772: 	my ($uname,$udom) = split(/:/,$student);
 1773: 	if ($ENV{'form.submitonly'} eq 'yes') {
 1774: 	    my (%status) = &student_gradeStatus($ENV{'form.url'},$symb,$udom,$uname,$partlist) ;
 1775: 	    my $statusflg = '';
 1776: 	    foreach (keys(%status)) {
 1777: 		$statusflg = 1 if ($status{$_} ne 'nothing');
 1778: 		my ($foo,$partid,$foo1) = split(/\./);
 1779: 		$statusflg = '' if ($status{'resource.'.$partid.'.submitted_by'} ne '');
 1780: 	    }
 1781: 	    next if ($statusflg eq '');
 1782: 	}
 1783: 	push @nextlist,$student if ($ctr < $ntstu);
 1784: 	$ctr++;
 1785:     }
 1786: 
 1787:     $ctr = 0;
 1788:     my $total = scalar(@nextlist)-1;
 1789: 
 1790:     foreach (sort @nextlist) {
 1791: 	my ($uname,$udom,$submitter) = split(/:/);
 1792: 	$ENV{'form.student'}  = $uname;
 1793: 	$ENV{'form.userdom'}  = $udom;
 1794: 	$ENV{'form.fullname'} = $$fullname{$_};
 1795: 	&submission($request,$ctr,$total);
 1796: 	$ctr++;
 1797:     }
 1798:     if ($total < 0) {
 1799: 	my $the_end = '<h3><font color="red">LON-CAPA User Message</font></h3><br />'."\n";
 1800: 	$the_end.='<b>Message: </b> No more students for this section or class.<br /><br />'."\n";
 1801: 	$the_end.='Click on the button below to return to the grading menu.<br /><br />'."\n";
 1802: 	$the_end.=&show_grading_menu_form ($symb,$url);
 1803: 	$request->print($the_end);
 1804:     }
 1805:     return '';
 1806: }
 1807: 
 1808: #---- Save the score and award for each student, if changed
 1809: sub saveHandGrade {
 1810:     my ($request,$url,$symb,$stuname,$domain,$newflg,$submitter) = @_;
 1811:     my $usec = &Apache::lonnet::getsection($domain,$stuname,
 1812: 					   $ENV{'request.course.id'});
 1813:     if (!&canmodify($usec)) { return('not_allowed'); }
 1814:     my %record     = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$domain,$stuname);
 1815:     my %newrecord  = ();
 1816:     my ($pts,$wgt) = ('','');
 1817:     foreach (split(/:/,$ENV{'form.partlist'.$newflg})) {
 1818: 	if ($ENV{'form.GD_SEL'.$newflg.'_'.$_} eq 'excused') {
 1819: 	    if ($record{'resource.'.$_.'.solved'} ne 'excused') {
 1820: 		$newrecord{'resource.'.$_.'.solved'} = 'excused';
 1821: 		if (exists($record{'resource.'.$_.'.awarded'})) {
 1822: 		    $newrecord{'resource.'.$_.'.awarded'} = '';
 1823: 		}
 1824: 	    }
 1825: 	} else {
 1826: 	    $pts = ($ENV{'form.GD_BOX'.$newflg.'_'.$_} ne '' ? 
 1827: 		    $ENV{'form.GD_BOX'.$newflg.'_'.$_} : 
 1828: 		    $ENV{'form.RADVAL'.$newflg.'_'.$_});
 1829: 	    return 'no_score' if ($pts eq '' && $ENV{'form.GD_SEL'.$newflg.'_'.$_} eq '');
 1830: 	    $wgt = $ENV{'form.WGT'.$newflg.'_'.$_} eq '' ? 1 : 
 1831: 		$ENV{'form.WGT'.$newflg.'_'.$_};
 1832: 	    my $partial= $pts/$wgt;
 1833: 	    next if ($partial eq $record{'resource.'.$_.'.awarded'}); #do not update score for part if not changed.
 1834: 	    $newrecord{'resource.'.$_.'.awarded'}  = $partial 
 1835: 		if ($record{'resource.'.$_.'.awarded'} ne $partial);
 1836: 	    my $reckey = 'resource.'.$_.'.solved';
 1837: 	    if ($partial == 0) {
 1838: 		$newrecord{$reckey} = 'incorrect_by_override' 
 1839: 		    if ($record{$reckey} ne 'incorrect_by_override');
 1840: 	    } else {
 1841: 		$newrecord{$reckey} = 'correct_by_override' 
 1842: 		    if ($record{$reckey} ne 'correct_by_override');
 1843: 	    }
 1844: 	    $newrecord{'resource.'.$_.'.submitted_by'} = $submitter 
 1845: 		if ($submitter && ($record{'resource.'.$_.'.submitted_by'} ne $submitter));
 1846: 	    $newrecord{'resource.'.$_.'.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
 1847: 	}
 1848:     }
 1849: 
 1850:     if (scalar(keys(%newrecord)) > 0) {
 1851: 	&Apache::lonnet::cstore(\%newrecord,$symb,
 1852: 				$ENV{'request.course.id'},$domain,$stuname);
 1853:     }
 1854:     return '',$pts,$wgt;
 1855: }
 1856: 
 1857: #--------------------------------------------------------------------------------------
 1858: #
 1859: #-------------------------- Next few routines handles grading by section or whole class
 1860: #
 1861: #--- Javascript to handle grading by section or whole class
 1862: sub viewgrades_js {
 1863:     my ($request) = shift;
 1864: 
 1865:     $request->print(<<VIEWJAVASCRIPT);
 1866: <script type="text/javascript" language="javascript">
 1867:    function writePoint(partid,weight,point) {
 1868: 	var radioButton = eval("document.classgrade.RADVAL_"+partid);
 1869: 	var textbox = eval("document.classgrade.TEXTVAL_"+partid);
 1870: 	if (point == "textval") {
 1871: 	    var point = eval("document.classgrade.TEXTVAL_"+partid+".value");
 1872: 	    if (isNaN(point) || parseFloat(point) < 0) {
 1873: 		alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point));
 1874: 		var resetbox = false;
 1875: 		for (var i=0; i<radioButton.length; i++) {
 1876: 		    if (radioButton[i].checked) {
 1877: 			textbox.value = i;
 1878: 			resetbox = true;
 1879: 		    }
 1880: 		}
 1881: 		if (!resetbox) {
 1882: 		    textbox.value = "";
 1883: 		}
 1884: 		return;
 1885: 	    }
 1886: 	    if (parseFloat(point) > parseFloat(weight)) {
 1887: 		var resp = confirm("You entered a value ("+parseFloat(point)+
 1888: 				   ") greater than the weight for the part. Accept?");
 1889: 		if (resp == false) {
 1890: 		    textbox.value = "";
 1891: 		    return;
 1892: 		}
 1893: 	    }
 1894: 	    for (var i=0; i<radioButton.length; i++) {
 1895: 		radioButton[i].checked=false;
 1896: 		if (parseFloat(point) == i) {
 1897: 		    radioButton[i].checked=true;
 1898: 		}
 1899: 	    }
 1900: 
 1901: 	} else {
 1902: 	    textbox.value = point;
 1903: 	}
 1904: 	for (i=0;i<document.classgrade.total.value;i++) {
 1905: 	    var user = eval("document.classgrade.ctr"+i+".value");
 1906: 	    var scorename = eval("document.classgrade.GD_"+user+
 1907: 				 "_"+partid+"_awarded");
 1908: 	    var saveval   = eval("document.classgrade.GD_"+user+
 1909: 				 "_"+partid+"_solved_s.value");
 1910: 	    var selname   = eval("document.classgrade.GD_"+user+"_"+partid+"_solved");
 1911: 	    if (saveval != "correct") {
 1912: 		scorename.value = point;
 1913: 		if (selname[0].selected != true) {
 1914: 		    selname[0].selected = true;
 1915: 		}
 1916: 	    }
 1917: 	}
 1918: 	var selval   = eval("document.classgrade.SELVAL_"+partid);
 1919: 	selval[0].selected = true;
 1920:     }
 1921: 
 1922:     function writeRadText(partid,weight) {
 1923: 	var selval   = eval("document.classgrade.SELVAL_"+partid);
 1924: 	var radioButton = eval("document.classgrade.RADVAL_"+partid);
 1925: 	var textbox = eval("document.classgrade.TEXTVAL_"+partid);
 1926: 	if (selval[1].selected) {
 1927: 	    for (var i=0; i<radioButton.length; i++) {
 1928: 		radioButton[i].checked=false;
 1929: 
 1930: 	    }
 1931: 	    textbox.value = "";
 1932: 
 1933: 	    for (i=0;i<document.classgrade.total.value;i++) {
 1934: 		var user = eval("document.classgrade.ctr"+i+".value");
 1935: 		var scorename = eval("document.classgrade.GD_"+user+
 1936: 				     "_"+partid+"_awarded");
 1937: 		var saveval   = eval("document.classgrade.GD_"+user+
 1938: 				     "_"+partid+"_solved_s.value");
 1939: 		var selname   = eval("document.classgrade.GD_"+user+
 1940: 				     "_"+partid+"_solved");
 1941: 		if (saveval != "correct") {
 1942: 		    scorename.value = "";
 1943: 		    selname[1].selected = true;
 1944: 		}
 1945: 	    }
 1946: 	} else {
 1947: 	    for (i=0;i<document.classgrade.total.value;i++) {
 1948: 		var user = eval("document.classgrade.ctr"+i+".value");
 1949: 		var scorename = eval("document.classgrade.GD_"+user+
 1950: 				     "_"+partid+"_awarded");
 1951: 		var saveval   = eval("document.classgrade.GD_"+user+
 1952: 				     "_"+partid+"_solved_s.value");
 1953: 		var selname   = eval("document.classgrade.GD_"+user+
 1954: 				     "_"+partid+"_solved");
 1955: 		if (saveval != "correct") {
 1956: 		    scorename.value = eval("document.classgrade.GD_"+user+
 1957: 				     "_"+partid+"_awarded_s.value");;
 1958: 		    selname[0].selected = true;
 1959: 		}
 1960: 	    }
 1961: 	}	    
 1962:     }
 1963: 
 1964:     function changeSelect(partid,user) {
 1965: 	var selval = eval("document.classgrade.GD_"+user+'_'+partid+"_solved");
 1966: 	var textbox = eval("document.classgrade.GD_"+user+'_'+partid+"_awarded");
 1967: 	var point  = textbox.value;
 1968: 	var weight = eval("document.classgrade.weight_"+partid+".value");
 1969: 
 1970: 	if (isNaN(point) || parseFloat(point) < 0) {
 1971: 	    alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point));
 1972: 	    textbox.value = "";
 1973: 	    return;
 1974: 	}
 1975: 	if (parseFloat(point) > parseFloat(weight)) {
 1976: 	    var resp = confirm("You entered a value ("+parseFloat(point)+
 1977: 			       ") greater than the weight of the part. Accept?");
 1978: 	    if (resp == false) {
 1979: 		textbox.value = "";
 1980: 		return;
 1981: 	    }
 1982: 	}
 1983: 	selval[0].selected = true;
 1984:     }
 1985: 
 1986:     function changeOneScore(partid,user) {
 1987: 	var selval = eval("document.classgrade.GD_"+user+'_'+partid+"_solved");
 1988: 	if (selval[1].selected) {
 1989: 	    var boxval = eval("document.classgrade.GD_"+user+'_'+partid+"_awarded");
 1990: 	    boxval.value = "";
 1991: 	}
 1992:     }
 1993: 
 1994:     function resetEntry(numpart) {
 1995: 	for (ctpart=0;ctpart<numpart;ctpart++) {
 1996: 	    var partid = eval("document.classgrade.partid_"+ctpart+".value");
 1997: 	    var radioButton = eval("document.classgrade.RADVAL_"+partid);
 1998: 	    var textbox = eval("document.classgrade.TEXTVAL_"+partid);
 1999: 	    var selval  = eval("document.classgrade.SELVAL_"+partid);
 2000: 	    for (var i=0; i<radioButton.length; i++) {
 2001: 		radioButton[i].checked=false;
 2002: 
 2003: 	    }
 2004: 	    textbox.value = "";
 2005: 	    selval[0].selected = true;
 2006: 
 2007: 	    for (i=0;i<document.classgrade.total.value;i++) {
 2008: 		var user = eval("document.classgrade.ctr"+i+".value");
 2009: 		var resetscore = eval("document.classgrade.GD_"+user+
 2010: 				      "_"+partid+"_awarded");
 2011: 		resetscore.value = eval("document.classgrade.GD_"+user+
 2012: 					"_"+partid+"_awarded_s.value");
 2013: 
 2014: 		var saveselval   = eval("document.classgrade.GD_"+user+
 2015: 				     "_"+partid+"_solved_s.value");
 2016: 
 2017: 		var selname   = eval("document.classgrade.GD_"+user+"_"+partid+"_solved");
 2018: 		if (saveselval == "excused") {
 2019: 		    if (selname[1].selected == false) { selname[1].selected = true;}
 2020: 		} else {
 2021: 		    if (selname[0].selected == false) {selname[0].selected = true};
 2022: 		}
 2023: 	    }
 2024: 	}
 2025:     }
 2026: 
 2027: </script>
 2028: VIEWJAVASCRIPT
 2029: }
 2030: 
 2031: #--- show scores for a section or whole class w/ option to change/update a score
 2032: sub viewgrades {
 2033:     my ($request) = shift;
 2034:     &viewgrades_js($request);
 2035: 
 2036:     my ($symb,$url) = ($ENV{'form.symb'},$ENV{'form.url'}); 
 2037:     my $result='<h3><font color="#339933">Manual Grading</font></h3>';
 2038: 
 2039:     $result.='<font size=+1><b>Current Resource: </b>'.$ENV{'form.probTitle'}.'</font>'."\n";
 2040: 
 2041:     #view individual student submission form - called using Javascript viewOneStudent
 2042:     $result.=&jscriptNform($url,$symb);
 2043: 
 2044:     #beginning of class grading form
 2045:     $result.= '<form action="/adm/grades" method="post" name="classgrade">'."\n".
 2046: 	'<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
 2047: 	'<input type="hidden" name="url"     value="'.$url.'" />'."\n".
 2048: 	'<input type="hidden" name="command" value="editgrades" />'."\n".
 2049: 	'<input type="hidden" name="section" value="'.$ENV{'form.section'}.'" />'."\n".
 2050: 	'<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."\n".
 2051: 	'<input type="hidden" name="probTitle" value="'.$ENV{'form.probTitle'}.'" />'."\n";
 2052: 
 2053:     $result.='<h3>Assign Common Grade To ';
 2054:     if ($ENV{'form.section'} eq 'all') {
 2055: 	$result.='Class </h3>';
 2056:     } elsif ($ENV{'form.section'} eq 'no') {
 2057: 	$result.='Students in no Section </h3>';
 2058:     } else {
 2059: 	$result.='Students in Section '.$ENV{'form.section'}.'</h3>';
 2060:     }
 2061:     $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".
 2062: 	'<table border=0><tr bgcolor="#ffffdd"><td>';
 2063:     #radio buttons/text box for assigning points for a section or class.
 2064:     #handles different parts of a problem
 2065:     my ($partlist,$handgrade) = &response_type($ENV{'form.url'});
 2066:     my %weight = ();
 2067:     my $ctsparts = 0;
 2068:     $result.='<table border="0">';
 2069:     my %seen = ();
 2070:     for (sort keys(%$handgrade)) {
 2071: 	my ($partid,$respid) = split (/_/,$_,2);
 2072: 	next if $seen{$partid};
 2073: 	$seen{$partid}++;
 2074: 	my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});
 2075: 	my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);
 2076: 	$weight{$partid} = $wgt eq '' ? '1' : $wgt;
 2077: 
 2078: 	$result.='<input type="hidden" name="partid_'.
 2079: 	    $ctsparts.'" value="'.$partid.'" />'."\n";
 2080: 	$result.='<input type="hidden" name="weight_'.
 2081: 	    $partid.'" value="'.$weight{$partid}.'" />'."\n";
 2082: 	$result.='<tr><td><b>Part  '.$partid.'&nbsp; &nbsp;Point:</b> </td><td>';
 2083: 	$result.='<table border="0"><tr>';  
 2084: 	my $ctr = 0;
 2085: 	while ($ctr<=$weight{$partid}) { # display radio buttons in a nice table 10 across
 2086: 	    $result.= '<td><input type="radio" name="RADVAL_'.$partid.'" '.
 2087: 		'onclick="javascript:writePoint(\''.$partid.'\','.$weight{$partid}.
 2088: 		','.$ctr.')" />'.$ctr."</td>\n";
 2089: 	    $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
 2090: 	    $ctr++;
 2091: 	}
 2092: 	$result.='</tr></table>';
 2093: 	$result.= '</td><td><b> or </b><input type="text" name="TEXTVAL_'.
 2094: 	    $partid.'" size="4" '.'onChange="javascript:writePoint(\''.
 2095: 		$partid.'\','.$weight{$partid}.',\'textval\')" /> /'.
 2096: 	    $weight{$partid}.' (problem weight)</td>'."\n";
 2097: 	$result.= '</td><td><select name="SELVAL_'.$partid.'"'.
 2098: 	    'onChange="javascript:writeRadText(\''.$partid.'\','.
 2099: 		$weight{$partid}.')"> '.
 2100: 	    '<option selected="on"> </option>'.
 2101: 	    '<option>excused</option></select></td></tr>'."\n";
 2102: 	$ctsparts++;
 2103:     }
 2104:     $result.='</table>'.'</td></tr></table>'.'</td></tr></table>'."\n".
 2105: 	'<input type="hidden" name="totalparts" value="'.$ctsparts.'" />';
 2106:     $result.='<input type="button" value="Reset" '.
 2107: 	'onClick="javascript:resetEntry('.$ctsparts.');" TARGET=_self>';
 2108: 
 2109:     #table listing all the students in a section/class
 2110:     #header of table
 2111:     $result.= '<h3>Assign Grade to Specific Students in ';
 2112:     if ($ENV{'form.section'} eq 'all') {
 2113: 	$result.='the Class </h3>';
 2114:     } elsif ($ENV{'form.section'} eq 'no') {
 2115: 	$result.='no Section </h3>';
 2116:     } else {
 2117: 	$result.='Section '.$ENV{'form.section'}.'</h3>';
 2118:     }
 2119:     $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".
 2120: 	'<table border=0><tr bgcolor="#deffff">'.
 2121: 	'<td><b>Fullname</b> <font color="#999999">(Username)</font></td>'."\n";
 2122:     my (@parts) = sort(&getpartlist($url));
 2123:     foreach my $part (@parts) {
 2124: 	my $display=&Apache::lonnet::metadata($url,$part.'.display');
 2125: 	$display =~ s/^Number of Attempts/Tries/; # makes the column narrower
 2126: 	if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
 2127: 	if ($display =~ /^Partial Credit Factor/) {
 2128: 	    my ($partid) = &split_part_type($part);
 2129: 	    $result.='<td><b>Score Part '.$partid.'<br />(weight = '.
 2130: 		$weight{$partid}.')</b></td>'."\n";
 2131: 	    next;
 2132: 	}
 2133: 	$display =~ s|Problem Status|Grade Status<br />|;
 2134: 	$result.='<td><b>'.$display.'</b></td>'."\n";
 2135:     }
 2136:     $result.='</tr>';
 2137: 
 2138:     #get info for each student
 2139:     #list all the students - with points and grade status
 2140:     my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'1');
 2141:     my $ctr = 0;
 2142:     foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
 2143: 	my $uname = $_;
 2144: 	$uname=~s/:/_/;
 2145: 	$result.='<input type="hidden" name="ctr'.$ctr.'" value="'.$uname.'" />'."\n";
 2146: 	$result.=&viewstudentgrade($url,$symb,$ENV{'request.course.id'},
 2147: 				   $_,$$fullname{$_},\@parts,\%weight);
 2148: 	$ctr++;
 2149:     }
 2150:     $result.='</table></td></tr></table>';
 2151:     $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n";
 2152:     $result.='<input type="button" value="Submit Changes" '.
 2153: 	'onClick="javascript:submit();" TARGET=_self /></form>'."\n";
 2154:     if (scalar(%$fullname) eq 0) {
 2155: 	my $colspan=3+scalar(@parts);
 2156: 	$result='<font color="red">There are no students in section "'.$ENV{'form.section'}.
 2157: 	    '" with enrollment status "'.$ENV{'form.Status'}.'" to modify or grade.</font>';
 2158:     }
 2159:     $result.=&show_grading_menu_form($symb,$url);
 2160:     return $result;
 2161: }
 2162: 
 2163: #--- call by previous routine to display each student
 2164: sub viewstudentgrade {
 2165:     my ($url,$symb,$courseid,$student,$fullname,$parts,$weight) = @_;
 2166:     my ($uname,$udom) = split(/:/,$student);
 2167:     $student=~s/:/_/;
 2168:     my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);
 2169:     my $result='<tr bgcolor="#ffffdd"><td>'.
 2170: 	'<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.
 2171: 	'\')"; TARGET=_self>'.$fullname.'</a> '.
 2172: 	'<font color="#999999">('.$uname.($ENV{'user.domain'} eq $udom ? '' : ':'.$udom).')</font></td>'."\n";
 2173:     foreach my $apart (@$parts) {
 2174: 	my ($part,$type) = &split_part_type($apart);
 2175: 	my $score=$record{"resource.$part.$type"};
 2176: 	if ($type eq 'awarded') {
 2177: 	    my $pts = $score eq '' ? '' : $score*$$weight{$part};
 2178: 	    $result.='<input type="hidden" name="'.
 2179: 		'GD_'.$student.'_'.$part.'_awarded_s" value="'.$pts.'" />'."\n";
 2180: 	    $result.='<td align="middle"><input type="text" name="'.
 2181: 		'GD_'.$student.'_'.$part.'_awarded" '.
 2182: 		'onChange="javascript:changeSelect(\''.$part.'\',\''.$student.
 2183: 		'\')" value="'.$pts.'" size="4" /></td>'."\n";
 2184: 	} elsif ($type eq 'solved') {
 2185: 	    my ($status,$foo)=split(/_/,$score,2);
 2186: 	    $status = 'nothing' if ($status eq '');
 2187: 	    $result.='<input type="hidden" name="'.'GD_'.$student.'_'.
 2188: 		$part.'_solved_s" value="'.$status.'" />'."\n";
 2189: 	    $result.='<td align="middle"><select name="'.
 2190: 		'GD_'.$student.'_'.$part.'_solved" '.
 2191: 		'onChange="javascript:changeOneScore(\''.$part.'\',\''.$student.'\')" >'."\n";
 2192: 	    my $optsel = '<option selected="on"> </option><option>excused</option>'."\n";
 2193: 	    $optsel = '<option> </option><option selected="on">excused</option>'."\n"
 2194: 		if ($status eq 'excused');
 2195: 	    $result.=$optsel;
 2196: 	    $result.="</select></td>\n";
 2197: 	} else {
 2198: 	    $result.='<input type="hidden" name="'.
 2199: 		'GD_'.$student.'_'.$part.'_'.$type.'_s" value="'.$score.'" />'.
 2200: 		    "\n";
 2201: 	    $result.='<td align="middle"><input type="text" name="'.
 2202: 		'GD_'.$student.'_'.$part.'_'.$type.'" '.
 2203: 		'value="'.$score.'" size="4" /></td>'."\n";
 2204: 	}
 2205:     }
 2206:     $result.='</tr>';
 2207:     return $result;
 2208: }
 2209: 
 2210: #--- change scores for all the students in a section/class
 2211: #    record does not get update if unchanged
 2212: sub editgrades {
 2213:     my ($request) = @_;
 2214: 
 2215:     my $symb=$ENV{'form.symb'};
 2216:     my $url =$ENV{'form.url'};
 2217:     my $title='<h3><font color="#339933">Current Grade Status</font></h3>';
 2218:     $title.='<font size=+1><b>Current Resource: </b>'.$ENV{'form.probTitle'}.'</font><br />'."\n";
 2219:     $title.='<font size=+1><b>Section: </b>'.$ENV{'form.section'}.'</font>'."\n";
 2220:     my $result= '<table border="0"><tr><td bgcolor="#777777">'."\n";
 2221:     $result.= '<table border="0"><tr bgcolor="#deffff">'.
 2222: 	'<td rowspan=2><b>Username</b></td><td rowspan=2><b>Domain</b></td><td rowspan=2><b>Fullname</b></td>'."\n";
 2223: 
 2224:     my %scoreptr = (
 2225: 		    'correct'  =>'correct_by_override',
 2226: 		    'incorrect'=>'incorrect_by_override',
 2227: 		    'excused'  =>'excused',
 2228: 		    'ungraded' =>'ungraded_attempted',
 2229: 		    'nothing'  => '',
 2230: 		    );
 2231:     my ($classlist,undef,$fullname) = &getclasslist($ENV{'form.section'},'0');
 2232: 
 2233:     my (@partid);
 2234:     my %weight = ();
 2235:     my %columns = ();
 2236:     my ($i,$ctr,$count,$rec_update) = (0,0,0,0);
 2237: 
 2238:     my (@parts) = sort(&getpartlist($url));
 2239:     my $header;
 2240:     while ($ctr < $ENV{'form.totalparts'}) {
 2241: 	my $partid = $ENV{'form.partid_'.$ctr};
 2242: 	push @partid,$partid;
 2243: 	$weight{$partid} = $ENV{'form.weight_'.$partid};
 2244: 	$ctr++;
 2245:     }
 2246:     foreach my $partid (@partid) {
 2247: 	$header .= '<td align="center">&nbsp;<b>Old Score</b>&nbsp;</td>'.
 2248: 	    '<td align="center">&nbsp;<b>New Score</b>&nbsp;</td>';
 2249: 	$columns{$partid}=2;
 2250: 	foreach my $stores (@parts) {
 2251: 	    my ($part,$type) = &split_part_type($stores);
 2252: 	    if ($part !~ m/^\Q$partid\E/) { next;}
 2253: 	    if ($type eq 'awarded' || $type eq 'solved') { next; }
 2254: 	    my $display=&Apache::lonnet::metadata($url,$stores.'.display');
 2255: 	    $display =~ s/\[Part: (\w)+\]//;
 2256: 	    $header .= '<td align="center">&nbsp;<b>Old</b> '.$display.'&nbsp;</td>'.
 2257: 		'<td align="center">&nbsp;<b>New</b> '.$display.'&nbsp;</td>';
 2258: 	    $columns{$partid}+=2;
 2259: 	}
 2260:     }
 2261:     foreach my $partid (@partid) {
 2262: 	$result .= '<td colspan="'.$columns{$partid}.
 2263: 	    '" align="center"><b>Part '.$partid.
 2264: 	    '</b> (Weight = '.$weight{$partid}.')</td>';
 2265: 
 2266:     }
 2267:     $result .= '</tr><tr bgcolor="#deffff">';
 2268:     $result .= $header;
 2269:     $result .= '</tr>'."\n";
 2270:     my $noupdate;
 2271:     for ($i=0; $i<$ENV{'form.total'}; $i++) {
 2272: 	my $line;
 2273: 	my $user = $ENV{'form.ctr'.$i};
 2274: 	my $usercolon = $user;
 2275: 	$usercolon =~s/_/:/;
 2276: 	my ($uname,$udom)=split(/_/,$user);
 2277: 	my %newrecord;
 2278: 	my $updateflag = 0;
 2279: 	$line .= '<tr bgcolor="#ffffde"><td>'.$uname.'&nbsp;</td><td>'.
 2280: 	    $udom.'&nbsp;</td><td>'.
 2281: 		$$fullname{$usercolon}.'&nbsp;</td>';
 2282: 	my $usec=$classlist->{"$uname:$udom"}[5];
 2283: 	if (!&canmodify($usec)) {
 2284: 	    my $numcols=scalar(@partid)*(scalar(@parts)-1)*2;
 2285: 	    $noupdate.=$line."<td colspan=\"$numcols\"><font color=\"red\">Not allowed to modify student</font></td></tr>";
 2286: 	    next;
 2287: 	}
 2288: 	foreach (@partid) {
 2289: 	    my $old_aw    = $ENV{'form.GD_'.$user.'_'.$_.'_awarded_s'};
 2290: 	    my $old_part_pcr = $old_aw/($weight{$_} ne '0' ? $weight{$_}:1);
 2291: 	    my $old_part  = $old_aw eq '' ? '' : $old_part_pcr;
 2292: 	    my $old_score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_solved_s'}};
 2293: 
 2294: 	    my $awarded   = $ENV{'form.GD_'.$user.'_'.$_.'_awarded'};
 2295: 	    my $pcr       = $awarded/($weight{$_} ne '0' ? $weight{$_} : 1);
 2296: 	    my $partial   = $awarded eq '' ? '' : $pcr;
 2297: 	    my $score;
 2298: 	    if ($partial eq '') {
 2299: 		$score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_solved_s'}};
 2300: 	    } elsif ($partial > 0) {
 2301: 		$score = 'correct_by_override';
 2302: 	    } elsif ($partial == 0) {
 2303: 		$score = 'incorrect_by_override';
 2304: 	    }
 2305: 	    $score = 'excused' if (($ENV{'form.GD_'.$user.'_'.$_.'_solved'} eq 'excused') &&
 2306: 				   ($score ne 'excused'));
 2307: 	    $line .= '<td align="center">'.$old_aw.'&nbsp;</td>'.
 2308: 		'<td align="center">'.$awarded.
 2309: 		($score eq 'excused' ? $score : '').'&nbsp;</td>';
 2310: 
 2311: 	    if (!($old_part eq $partial && $old_score eq $score)) {
 2312: 		$updateflag = 1;
 2313: 		$newrecord{'resource.'.$_.'.awarded'}  = $partial if $partial ne '';
 2314: 		$newrecord{'resource.'.$_.'.solved'}   = $score;
 2315: 		$rec_update++;
 2316: 	    }
 2317: 
 2318: 	    my $partid=$_;
 2319: 	    foreach my $stores (@parts) {
 2320: 		my ($part,$type) = &split_part_type($stores);
 2321: 		if ($part !~ m/^\Q$partid\E/) { next;}
 2322: 		if ($type eq 'awarded' || $type eq 'solved') { next; }
 2323: 		my $old_aw    = $ENV{'form.GD_'.$user.'_'.$part.'_'.$type.'_s'};
 2324: 		my $awarded   = $ENV{'form.GD_'.$user.'_'.$part.'_'.$type};
 2325: 		if ($awarded ne '' && $awarded ne $old_aw) {
 2326: 		    $newrecord{'resource.'.$part.'.'.$type}= $awarded;
 2327: 		    $newrecord{'resource.'.$part.'.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
 2328: 		    $updateflag=1;
 2329: 		}
 2330: 		$line .= '<td align="center">'.$old_aw.'&nbsp;</td>'.
 2331: 		    '<td align="center">'.$awarded.'&nbsp;</td>';
 2332: 	    }
 2333: 	}
 2334: 	$line.='</tr>'."\n";
 2335: 	if ($updateflag) {
 2336: 	    $count++;
 2337: 	    &Apache::lonnet::cstore(\%newrecord,$symb,$ENV{'request.course.id'},
 2338: 				    $udom,$uname);
 2339: 	    $result.=$line;
 2340: 	} else {
 2341: 	    $noupdate.=$line;
 2342: 	}
 2343:     }
 2344:     if ($noupdate) {
 2345: 	my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3;
 2346: 	$result .= '<tr bgcolor="#ffffff"><td align="center" colspan="'.$numcols.'">No Changes Occured For the Students Below</td></tr>'.$noupdate;
 2347:     }
 2348:     $result .= '</table></td></tr></table>'."\n".
 2349: 	&show_grading_menu_form ($symb,$url);
 2350:     my $msg = '<b>Number of records updated = '.$rec_update.
 2351: 	' for '.$count.' student'.($count <= 1 ? '' : 's').'.</b><br />'.
 2352: 	'<b>Total number of students = '.$ENV{'form.total'}.'</b><br />';
 2353:     return $title.$msg.$result;
 2354: }
 2355: 
 2356: sub split_part_type {
 2357:     my ($partstr) = @_;
 2358:     my ($temp,@allparts)=split(/_/,$partstr);
 2359:     my $type=pop(@allparts);
 2360:     my $part=join('.',@allparts);
 2361:     return ($part,$type);
 2362: }
 2363: 
 2364: #------------- end of section for handling grading by section/class ---------
 2365: #
 2366: #----------------------------------------------------------------------------
 2367: 
 2368: 
 2369: #----------------------------------------------------------------------------
 2370: #
 2371: #-------------------------- Next few routines handles grading by csv upload
 2372: #
 2373: #--- Javascript to handle csv upload
 2374: sub csvupload_javascript_reverse_associate {
 2375:   return(<<ENDPICK);
 2376:   function verify(vf) {
 2377:     var foundsomething=0;
 2378:     var founduname=0;
 2379:     var founddomain=0;
 2380:     for (i=0;i<=vf.nfields.value;i++) {
 2381:       tw=eval('vf.f'+i+'.selectedIndex');
 2382:       if (i==0 && tw!=0) { founduname=1; }
 2383:       if (i==1 && tw!=0) { founddomain=1; }
 2384:       if (i!=0 && i!=1 && tw!=0) { foundsomething=1; }
 2385:     }
 2386:     if (founduname==0 || founddomain==0) {
 2387:       alert('You need to specify at both the username and domain');
 2388:       return;
 2389:     }
 2390:     if (foundsomething==0) {
 2391:       alert('You need to specify at least one grading field');
 2392:       return;
 2393:     }
 2394:     vf.submit();
 2395:   }
 2396:   function flip(vf,tf) {
 2397:     var nw=eval('vf.f'+tf+'.selectedIndex');
 2398:     var i;
 2399:     for (i=0;i<=vf.nfields.value;i++) {
 2400:       //can not pick the same destination field for both name and domain
 2401:       if (((i ==0)||(i ==1)) && 
 2402:           ((tf==0)||(tf==1)) && 
 2403:           (i!=tf) &&
 2404:           (eval('vf.f'+i+'.selectedIndex')==nw)) {
 2405:         eval('vf.f'+i+'.selectedIndex=0;')
 2406:       }
 2407:     }
 2408:   }
 2409: ENDPICK
 2410: }
 2411: 
 2412: sub csvupload_javascript_forward_associate {
 2413:   return(<<ENDPICK);
 2414:   function verify(vf) {
 2415:     var foundsomething=0;
 2416:     var founduname=0;
 2417:     var founddomain=0;
 2418:     for (i=0;i<=vf.nfields.value;i++) {
 2419:       tw=eval('vf.f'+i+'.selectedIndex');
 2420:       if (tw==1) { founduname=1; }
 2421:       if (tw==2) { founddomain=1; }
 2422:       if (tw>2) { foundsomething=1; }
 2423:     }
 2424:     if (founduname==0 || founddomain==0) {
 2425:       alert('You need to specify at both the username and domain');
 2426:       return;
 2427:     }
 2428:     if (foundsomething==0) {
 2429:       alert('You need to specify at least one grading field');
 2430:       return;
 2431:     }
 2432:     vf.submit();
 2433:   }
 2434:   function flip(vf,tf) {
 2435:     var nw=eval('vf.f'+tf+'.selectedIndex');
 2436:     var i;
 2437:     //can not pick the same destination field twice
 2438:     for (i=0;i<=vf.nfields.value;i++) {
 2439:       if ((i!=tf) && (eval('vf.f'+i+'.selectedIndex')==nw)) {
 2440:         eval('vf.f'+i+'.selectedIndex=0;')
 2441:       }
 2442:     }
 2443:   }
 2444: ENDPICK
 2445: }
 2446: 
 2447: sub csvuploadmap_header {
 2448:     my ($request,$symb,$url,$datatoken,$distotal)= @_;
 2449:     my $javascript;
 2450:     if ($ENV{'form.upfile_associate'} eq 'reverse') {
 2451: 	$javascript=&csvupload_javascript_reverse_associate();
 2452:     } else {
 2453: 	$javascript=&csvupload_javascript_forward_associate();
 2454:     }
 2455: 
 2456:     my ($result) = &showResourceInfo($url,$ENV{'form.probTitle'});
 2457: 
 2458:     $request->print(<<ENDPICK);
 2459: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
 2460: <h3><font color="#339933">Uploading Class Grades</font></h3>
 2461: $result
 2462: <hr>
 2463: <h3>Identify fields</h3>
 2464: Total number of records found in file: $distotal <hr />
 2465: Enter as many fields as you can. The system will inform you and bring you back
 2466: to this page if the data selected is insufficient to run your class.<hr />
 2467: <input type="button" value="Reverse Association" onClick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" />
 2468: <input type="hidden" name="associate"  value="" />
 2469: <input type="hidden" name="phase"      value="three" />
 2470: <input type="hidden" name="datatoken"  value="$datatoken" />
 2471: <input type="hidden" name="fileupload" value="$ENV{'form.fileupload'}" />
 2472: <input type="hidden" name="upfiletype" value="$ENV{'form.upfiletype'}" />
 2473: <input type="hidden" name="upfile_associate" 
 2474:                                        value="$ENV{'form.upfile_associate'}" />
 2475: <input type="hidden" name="symb"       value="$symb" />
 2476: <input type="hidden" name="url"        value="$url" />
 2477: <input type="hidden" name="saveState"  value="$ENV{'form.saveState'}" />
 2478: <input type="hidden" name="probTitle"  value="$ENV{'form.probTitle'}" />
 2479: <input type="hidden" name="command"    value="csvuploadassign" />
 2480: <hr />
 2481: <script type="text/javascript" language="Javascript">
 2482: $javascript
 2483: </script>
 2484: ENDPICK
 2485:     $request->print(&show_grading_menu_form($symb,$url));
 2486:     return '';
 2487: 
 2488: }
 2489: 
 2490: sub csvupload_fields {
 2491:     my ($url) = @_;
 2492:     my (@parts) = &getpartlist($url);
 2493:     my @fields=(['username','Student Username'],['domain','Student Domain']);
 2494:     foreach my $part (sort(@parts)) {
 2495: 	my @datum;
 2496: 	my $display=&Apache::lonnet::metadata($url,$part.'.display');
 2497: 	my $name=$part;
 2498: 	if  (!$display) { $display = $name; }
 2499: 	@datum=($name,$display);
 2500: 	push(@fields,\@datum);
 2501:     }
 2502:     return (@fields);
 2503: }
 2504: 
 2505: sub csvuploadmap_footer {
 2506:     my ($request,$i,$keyfields) =@_;
 2507:     $request->print(<<ENDPICK);
 2508: </table>
 2509: <input type="hidden" name="nfields" value="$i" />
 2510: <input type="hidden" name="keyfields" value="$keyfields" />
 2511: <input type="button" onClick="javascript:verify(this.form)" value="Assign Grades" /><br />
 2512: </form>
 2513: ENDPICK
 2514: }
 2515: 
 2516: sub upcsvScores_form {
 2517:     my ($request) = shift;
 2518:     my ($symb,$url)=&get_symb_and_url($request);
 2519:     if (!$symb) {return '';}
 2520:     my $result =<<CSVFORMJS;
 2521: <script type="text/javascript" language="javascript">
 2522:     function checkUpload(formname) {
 2523: 	if (formname.upfile.value == "") {
 2524: 	    alert("Please use the browse button to select a file from your local directory.");
 2525: 	    return false;
 2526: 	}
 2527: 	formname.submit();
 2528:     }
 2529:     </script>
 2530: CSVFORMJS
 2531:     $ENV{'form.probTitle'} = &Apache::lonnet::gettitle($symb);
 2532:     my ($table) = &showResourceInfo($url,$ENV{'form.probTitle'});
 2533:     $result.=$table;
 2534:     $result.='<br /><table width=100% border=0><tr><td bgcolor="#777777">'."\n";
 2535:     $result.='<table width=100% border=0><tr bgcolor="#e6ffff"><td>'."\n";
 2536:     $result.='&nbsp;<b>Specify a file containing the class scores for current resource'.
 2537: 	'.</b></td></tr>'."\n";
 2538:     $result.='<tr bgcolor=#ffffe6><td>'."\n";
 2539:     my $upfile_select=&Apache::loncommon::upfile_select_html();
 2540:     $result.=<<ENDUPFORM;
 2541: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
 2542: <input type="hidden" name="symb" value="$symb" />
 2543: <input type="hidden" name="url" value="$url" />
 2544: <input type="hidden" name="command" value="csvuploadmap" />
 2545: <input type="hidden" name="probTitle" value="$ENV{'form.probTitle'}" />
 2546: <input type="hidden" name="saveState"  value="$ENV{'form.saveState'}" />
 2547: $upfile_select
 2548: <br /><input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scores" />
 2549: 
 2550: </form>
 2551: ENDUPFORM
 2552:     $result.='</td></tr></table>'."\n";
 2553:     $result.='</td></tr></table><br /><br />'."\n";
 2554:     $result.=&show_grading_menu_form($symb,$url);
 2555:     return $result;
 2556: }
 2557: 
 2558: 
 2559: sub csvuploadmap {
 2560:     my ($request)= @_;
 2561:     my ($symb,$url)=&get_symb_and_url($request);
 2562:     if (!$symb) {return '';}
 2563: 
 2564:     my $datatoken;
 2565:     if (!$ENV{'form.datatoken'}) {
 2566: 	$datatoken=&Apache::loncommon::upfile_store($request);
 2567:     } else {
 2568: 	$datatoken=$ENV{'form.datatoken'};
 2569: 	&Apache::loncommon::load_tmp_file($request);
 2570:     }
 2571:     my @records=&Apache::loncommon::upfile_record_sep();
 2572:     &csvuploadmap_header($request,$symb,$url,$datatoken,$#records+1);
 2573:     my ($i,$keyfields);
 2574:     if (@records) {
 2575: 	my @fields=&csvupload_fields($url);
 2576: 
 2577: 	if ($ENV{'form.upfile_associate'} eq 'reverse') {	
 2578: 	    &Apache::loncommon::csv_print_samples($request,\@records);
 2579: 	    $i=&Apache::loncommon::csv_print_select_table($request,\@records,
 2580: 							  \@fields);
 2581: 	    foreach (@fields) { $keyfields.=$_->[0].','; }
 2582: 	    chop($keyfields);
 2583: 	} else {
 2584: 	    unshift(@fields,['none','']);
 2585: 	    $i=&Apache::loncommon::csv_samples_select_table($request,\@records,
 2586: 							    \@fields);
 2587: 	    my %sone=&Apache::loncommon::record_sep($records[0]);
 2588: 	    $keyfields=join(',',sort(keys(%sone)));
 2589: 	}
 2590:     }
 2591:     &csvuploadmap_footer($request,$i,$keyfields);
 2592:     $request->print(&show_grading_menu_form($symb,$url));
 2593: 
 2594:     return '';
 2595: }
 2596: 
 2597: sub csvuploadassign {
 2598:     my ($request)= @_;
 2599:     my ($symb,$url)=&get_symb_and_url($request);
 2600:     if (!$symb) {return '';}
 2601:     &Apache::loncommon::load_tmp_file($request);
 2602:     my @gradedata = &Apache::loncommon::upfile_record_sep();
 2603:     my @keyfields = split(/\,/,$ENV{'form.keyfields'});
 2604:     my %fields=();
 2605:     for (my $i=0; $i<=$ENV{'form.nfields'}; $i++) {
 2606: 	if ($ENV{'form.upfile_associate'} eq 'reverse') {
 2607: 	    if ($ENV{'form.f'.$i} ne 'none') {
 2608: 		$fields{$keyfields[$i]}=$ENV{'form.f'.$i};
 2609: 	    }
 2610: 	} else {
 2611: 	    if ($ENV{'form.f'.$i} ne 'none') {
 2612: 		$fields{$ENV{'form.f'.$i}}=$keyfields[$i];
 2613: 	    }
 2614: 	}
 2615:     }
 2616:     $request->print('<h3>Assigning Grades</h3>');
 2617:     my $courseid=$ENV{'request.course.id'};
 2618:     my ($classlist) = &getclasslist('all',0);
 2619:     my @notallowed;
 2620:     my @skipped;
 2621:     my $countdone=0;
 2622:     foreach my $grade (@gradedata) {
 2623: 	my %entries=&Apache::loncommon::record_sep($grade);
 2624: 	my $username=$entries{$fields{'username'}};
 2625: 	my $domain=$entries{$fields{'domain'}};
 2626: 	if (!exists($$classlist{"$username:$domain"})) {
 2627: 	    push(@skipped,"$username:$domain");
 2628: 	    next;
 2629: 	}
 2630: 	my $usec=$classlist->{"$username:$domain"}[5];
 2631: 	if (!&canmodify($usec)) {
 2632: 	    push(@notallowed,"$username:$domain");
 2633: 	    next;
 2634: 	}
 2635: 	my %grades;
 2636: 	foreach my $dest (keys(%fields)) {
 2637: 	    if ($dest eq 'username' || $dest eq 'domain') { next; }
 2638: 	    if ($entries{$fields{$dest}} eq '') { next; }
 2639: 	    my $store_key=$dest;
 2640: 	    $store_key=~s/^stores/resource/;
 2641: 	    $store_key=~s/_/\./g;
 2642: 	    $grades{$store_key}=$entries{$fields{$dest}};
 2643: 	}
 2644: 	$grades{"resource.regrader"}="$ENV{'user.name'}:$ENV{'user.domain'}";
 2645: 	&Apache::lonnet::cstore(\%grades,$symb,$ENV{'request.course.id'},
 2646: 				$domain,$username);
 2647: 	$request->print('.');
 2648: 	$request->rflush();
 2649: 	$countdone++;
 2650:     }
 2651:     $request->print("<br />Stored $countdone students\n");
 2652:     if (@skipped) {
 2653: 	$request->print('<p<font size="+1"><b>Skipped Students</b></font></p>');
 2654: 	foreach my $student (@skipped) { $request->print("$student<br />\n"); }
 2655:     }
 2656:     if (@notallowed) {
 2657: 	$request->print('<p><font size="+1" color="red"><b>Students Not Allowed to Modify</b></font></p>');
 2658: 	foreach my $student (@notallowed) { $request->print("$student<br />\n"); }
 2659:     }
 2660:     $request->print("<br />\n");
 2661:     $request->print(&show_grading_menu_form($symb,$url));
 2662:     return '';
 2663: }
 2664: #------------- end of section for handling csv file upload ---------
 2665: #
 2666: #-------------------------------------------------------------------
 2667: #
 2668: #-------------- Next few routines handle grading by page/sequence
 2669: #
 2670: #--- Select a page/sequence and a student to grade
 2671: sub pickStudentPage {
 2672:     my ($request) = shift;
 2673: 
 2674:     $request->print(<<LISTJAVASCRIPT);
 2675: <script type="text/javascript" language="javascript">
 2676: 
 2677: function checkPickOne(formname) {
 2678:     if (radioSelection(formname.student) == null) {
 2679: 	alert("Please select the student you wish to grade.");
 2680: 	return;
 2681:     }
 2682:     var ptr = pullDownSelection(formname.selectpage);
 2683:     formname.page.value = eval("formname.page"+ptr+".value");
 2684:     formname.title.value = eval("formname.title"+ptr+".value");
 2685:     formname.submit();
 2686: }
 2687: 
 2688: </script>
 2689: LISTJAVASCRIPT
 2690:     &commonJSfunctions($request);
 2691:     my ($symb,$url) = &get_symb_and_url($request);
 2692:     my $cdom      = $ENV{"course.$ENV{'request.course.id'}.domain"};
 2693:     my $cnum      = $ENV{"course.$ENV{'request.course.id'}.num"};
 2694:     my $getsec    = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};
 2695: 
 2696:     my $result='<h3><font color="#339933">&nbsp;'.
 2697: 	'Manual Grading by Page or Sequence</font></h3>';
 2698: 
 2699:     $result.='<form action="/adm/grades" method="post" name="displayPage">'."\n";
 2700:     $result.='&nbsp;<b>Problems from:</b> <select name="selectpage">'."\n";
 2701:     my ($titles,$symbx) = &getSymbMap($request);
 2702:     my ($curpage,$type,$mapId) = ($symb =~ /(.*?\.(page|sequence))___(\d+)___/); 
 2703:     my $ctr=0;
 2704:     foreach (@$titles) {
 2705: 	my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
 2706: 	$result.='<option value="'.$ctr.'" '.
 2707: 	    ($$symbx{$_} =~ /$curpage$/ ? 'selected="on"' : '').
 2708: 	    '>'.$showtitle.'</option>'."\n";
 2709: 	$ctr++;
 2710:     }
 2711:     $result.= '</select>'."<br>\n";
 2712:     $ctr=0;
 2713:     foreach (@$titles) {
 2714: 	my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
 2715: 	$result.='<input type="hidden" name="page'.$ctr.'" value="'.$$symbx{$_}.'" />'."\n";
 2716: 	$result.='<input type="hidden" name="title'.$ctr.'" value="'.$showtitle.'" />'."\n";
 2717: 	$ctr++;
 2718:     }
 2719:     $result.='<input type="hidden" name="page" />'."\n".
 2720: 	'<input type="hidden" name="title" />'."\n";
 2721: 
 2722:     $result.='&nbsp;<b>View Problems Text: </b><input type="radio" name="vProb" value="no" checked /> no '."\n".
 2723: 	'<input type="radio" name="vProb" value="yes" /> yes '."<br>\n";
 2724: 
 2725:     $result.='&nbsp;<b>Submission Details: </b>'.
 2726: 	'<input type="radio" name="lastSub" value="none" /> none'."\n".
 2727: 	'<input type="radio" name="lastSub" value="datesub" checked /> by dates and submissions'."\n".
 2728: 	'<input type="radio" name="lastSub" value="all" /> all details'."\n";
 2729: 
 2730:     $result.='<input type="hidden" name="section"     value="'.$getsec.'" />'."\n".
 2731: 	'<input type="hidden" name="Status"  value="'.$ENV{'form.Status'}.'" />'."\n".
 2732: 	'<input type="hidden" name="command" value="displayPage" />'."\n".
 2733: 	'<input type="hidden" name="url"     value="'.$url.'" />'."\n".
 2734: 	'<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
 2735: 	'<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."<br />\n";
 2736: 
 2737:     $result.='&nbsp;<input type="button" '.
 2738: 	'onClick="javascript:checkPickOne(this.form);"value="Submit" /><br />'."\n";
 2739: 
 2740:     $request->print($result);
 2741: 
 2742:     my $studentTable.='&nbsp;<b>Select a student you wish to grade</b><br>'.
 2743: 	'<table border="0"><tr><td bgcolor="#777777">'.
 2744: 	'<table border="0"><tr bgcolor="#e6ffff">'.
 2745: 	'<td><b>&nbsp;Fullname <font color="#999999">(username)</font></b></td>'.
 2746: 	'<td><b>&nbsp;Fullname <font color="#999999">(username)</font></b></td>'.
 2747: 	'<td><b>&nbsp;Fullname <font color="#999999">(username)</font></b></td>'.
 2748: 	'<td><b>&nbsp;Fullname <font color="#999999">(username)</font></b></td></tr>';
 2749:  
 2750:     my (undef,undef,$fullname) = &getclasslist($getsec,'1');
 2751:     my $ptr = 1;
 2752:     foreach my $student (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
 2753: 	my ($uname,$udom) = split(/:/,$student);
 2754: 	$studentTable.=($ptr%4 == 1 ? '<tr bgcolor="#ffffe6"><td>' : '</td><td>');
 2755: 	$studentTable.='<input type="radio" name="student" value="'.$student.'" /> '.$$fullname{$student}.
 2756: 	    '<font color="#999999"> ('.$uname.($udom eq $cdom ? '':':'.$udom).')</font>'."\n";
 2757: 	$studentTable.=($ptr%4 == 0 ? '</td></tr>' : '');
 2758: 	$ptr++;
 2759:     }
 2760:     $studentTable.='</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;' if ($ptr%4 == 2);
 2761:     $studentTable.='</td><td>&nbsp;</td><td>&nbsp;' if ($ptr%4 == 3);
 2762:     $studentTable.='</td><td>&nbsp;' if ($ptr%4 == 0);
 2763:     $studentTable.='</td></tr></table></td></tr></table>'."\n";
 2764:     $studentTable.='<br />&nbsp;<input type="button" '.
 2765: 	'onClick="javascript:checkPickOne(this.form);"value="Submit" /></form>'."\n";
 2766: 
 2767:     $studentTable.=&show_grading_menu_form($symb,$url);
 2768:     $request->print($studentTable);
 2769: 
 2770:     return '';
 2771: }
 2772: 
 2773: sub getSymbMap {
 2774:     my ($request) = @_;
 2775:     my $navmap = Apache::lonnavmaps::navmap-> new($ENV{'request.course.fn'}.'.db',
 2776: 						  $ENV{'request.course.fn'}.'_parms.db');
 2777:     $navmap->init();
 2778: 
 2779:     my %symbx = ();
 2780:     my @titles = ();
 2781:     my $minder = 0;
 2782: 
 2783:     # Gather every sequence that has problems.
 2784:     my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); }, 1);
 2785:     for my $sequence ($navmap->getById('0.0'), @sequences) {
 2786: 	if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 0) ) {
 2787: 	    my $title = $minder.'.'.$sequence->compTitle();
 2788: 	    push @titles, $title; # minder in case two titles are identical
 2789: 	    $symbx{$title} = $sequence->symb();
 2790: 	    $minder++;
 2791: 	}
 2792:     }
 2793: 
 2794:     $navmap->untieHashes();
 2795:     return \@titles,\%symbx;
 2796: }
 2797: 
 2798: #
 2799: #--- Displays a page/sequence w/wo problems, w/wo submissions
 2800: sub displayPage {
 2801:     my ($request) = shift;
 2802: 
 2803:     my ($symb,$url) = &get_symb_and_url($request);
 2804:     my $cdom      = $ENV{"course.$ENV{'request.course.id'}.domain"};
 2805:     my $cnum      = $ENV{"course.$ENV{'request.course.id'}.num"};
 2806:     my $getsec    = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};
 2807:     my $pageTitle = $ENV{'form.page'};
 2808:     my ($classlist,undef,$fullname) = &getclasslist($getsec,'1');
 2809:     my ($uname,$udom) = split(/:/,$ENV{'form.student'});
 2810:     my $usec=$classlist->{$ENV{'form.student'}}[5];
 2811:     if (!&canview($usec)) {
 2812: 	$request->print('<font color="red">Unable to view requested student.('.$ENV{'form.student'}.')</font>');
 2813: 	$request->print(&show_grading_menu_form($symb,$url));
 2814: 	return;
 2815:     }
 2816:     my $result='<h3><font color="#339933">&nbsp;'.$ENV{'form.title'}.'</font></h3>';
 2817:     $result.='<h3>&nbsp;Student: '.$$fullname{$ENV{'form.student'}}.
 2818: 	'<font color="#999999"> ('.$uname.($udom eq $cdom ? '':':'.$udom).')</font></h3>'."\n";
 2819: 
 2820:     &sub_page_js($request);
 2821:     $request->print($result);
 2822: 
 2823:     my $navmap = Apache::lonnavmaps::navmap-> new($ENV{'request.course.fn'}.'.db',
 2824: 						  $ENV{'request.course.fn'}.'_parms.db',1, 1);
 2825:     my ($mapUrl, $id, $resUrl) = split(/___/, $ENV{'form.page'});
 2826:     my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
 2827: 
 2828:     my $iterator = $navmap->getIterator($map->map_start(),
 2829: 					$map->map_finish());
 2830: 
 2831:     my $studentTable='<form action="/adm/grades" method="post" name="gradePage">'."\n".
 2832: 	'<input type="hidden" name="command" value="gradeByPage" />'."\n".
 2833: 	'<input type="hidden" name="student" value="'.$ENV{'form.student'}.'" />'."\n".
 2834: 	'<input type="hidden" name="page"    value="'.$pageTitle.'" />'."\n".
 2835: 	'<input type="hidden" name="title"   value="'.$ENV{'form.title'}.'" />'."\n".
 2836: 	'<input type="hidden" name="url"     value="'.$url.'" />'."\n".
 2837: 	'<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
 2838: 	'<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."\n";
 2839: 
 2840:     my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').
 2841: 	'/check.gif" height="16" border="0" />';
 2842: 
 2843:     $studentTable.='&nbsp;<b>Note:</b> Problems graded correct by the computer are marked with a '.$checkIcon.
 2844: 	' symbol.'."\n".
 2845: 	'<table border="0"><tr><td bgcolor="#777777">'.
 2846: 	'<table border="0"><tr bgcolor="#e6ffff">'.
 2847: 	'<td align="center"><b>&nbsp;Prob.&nbsp;</b></td>'.
 2848: 	'<td><b>&nbsp;'.($ENV{'form.vProb'} eq 'no' ? 'Title' : 'Problem Text').'/Grade</b></td></tr>';
 2849: 
 2850:     my ($depth,$question) = (1,1);
 2851:     $iterator->next(); # skip the first BEGIN_MAP
 2852:     my $curRes = $iterator->next(); # for "current resource"
 2853:     while ($depth > 0) {
 2854:         if($curRes == $iterator->BEGIN_MAP) { $depth++; }
 2855:         if($curRes == $iterator->END_MAP) { $depth--; }
 2856: 
 2857: #        if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) {
 2858:         if (ref($curRes) && $curRes->is_problem()) {
 2859: 	    my $parts = $curRes->parts();
 2860:             my $title = $curRes->compTitle();
 2861: 	    my $symbx = $curRes->symb();
 2862: 	    $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$question.
 2863: 		(scalar(@{$parts}) == 1 ? '' : '<br>('.scalar(@{$parts}).'&nbsp;parts)').'</td>';
 2864: 	    $studentTable.='<td valign="top">';
 2865: 	    if ($ENV{'form.vProb'} eq 'yes') {
 2866: 		$studentTable.=&show_problem($request,$symbx,$uname,$udom,1);
 2867: 	    } else {
 2868: 		my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$ENV{'request.course.id'});
 2869: 		$companswer =~ s|<form(.*?)>||g;
 2870: 		$companswer =~ s|</form>||g;
 2871: #		while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>
 2872: #		    $companswer =~ s/$1/ /ms;
 2873: #		    $request->print('match='.$1."<br>\n");
 2874: #		}
 2875: #		$companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;
 2876: 		$studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br>&nbsp;<b>Correct answer:</b><br>'.$companswer;
 2877: 	    }
 2878: 
 2879: 	    my %record = &Apache::lonnet::restore($symbx,$ENV{'request.course.id'},$udom,$uname);
 2880: 	    if ($ENV{'form.lastSub'} eq 'datesub') {
 2881: 		if ($record{'version'} eq '') {
 2882: 		    $studentTable.='<br />&nbsp;<font color="red">No recorded submission for this problem</font><br />';
 2883: 		} else {
 2884: 		    my %responseType = ();
 2885: 		    foreach my $partid (@{$parts}) {
 2886: 			$responseType{$partid} = $curRes->responseType($partid);
 2887: 		    }
 2888: 		    $studentTable.= &displaySubByDates(\$symbx,\%record,$parts,\%responseType,$checkIcon);
 2889: 		}
 2890: 	    } elsif ($ENV{'form.lastSub'} eq 'all') {
 2891: 		my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : '');
 2892: 		$studentTable.=&Apache::loncommon::get_previous_attempt($symbx,$uname,$udom,
 2893: 									$ENV{'request.course.id'},
 2894: 									'','.submission');
 2895:  
 2896: 	    }
 2897: 	    if (&canmodify($usec)) {
 2898: 		foreach my $partid (@{$parts}) {
 2899: 		    $studentTable.=&gradeBox($request,$symbx,$uname,$udom,$question,$partid,\%record);
 2900: 		    $studentTable.='<input type="hidden" name="q_'.$question.'" value="'.$partid.'" />'."\n";
 2901: 		    $question++;
 2902: 		}
 2903: 	    }
 2904: 	    $studentTable.='</td></tr>';
 2905: 
 2906: 	}
 2907:         $curRes = $iterator->next();
 2908:     }
 2909: 
 2910:     $navmap->untieHashes();
 2911: 
 2912:     $studentTable.='</td></tr></table></td></tr></table>'."\n".
 2913: 	'&nbsp;&nbsp;<input type="button" value="Save" '.
 2914: 	'onClick="javascript:checkSubmitPage(this.form,'.$question.');" TARGET=_self />'.
 2915: 	'</form>'."\n";
 2916:     $studentTable.=&show_grading_menu_form($symb,$url);
 2917:     $request->print($studentTable);
 2918: 
 2919:     return '';
 2920: }
 2921: 
 2922: sub displaySubByDates {
 2923:     my ($symbx,$record,$parts,$responseType,$checkIcon) = @_;
 2924:     my $studentTable='<table border="0" width="100%"><tr><td bgcolor="#777777">'.
 2925: 	'<table border="0" width="100%"><tr bgcolor="#e6ffff">'.
 2926: 	'<td><b>Date/Time</b></td>'.
 2927: 	'<td><b>Submission</b></td>'.
 2928: 	'<td><b>Status&nbsp;</b></td></tr>';
 2929:     my ($version);
 2930:     my %mark;
 2931:     $mark{'correct_by_student'} = $checkIcon;
 2932:     return '<br />&nbsp;<font color="red">Nothing submitted - no attempts</font><br />' 
 2933: 	if (!exists($$record{'1:timestamp'}));
 2934:     for ($version=1;$version<=$$record{'version'};$version++) {
 2935: 	my $timestamp = scalar(localtime($$record{$version.':timestamp'}));
 2936: 	$studentTable.='<tr bgcolor="#ffffff" valign="top"><td>'.$timestamp.'</td>';
 2937: 	my @versionKeys = split(/\:/,$$record{$version.':keys'});
 2938: 	my @displaySub = ();
 2939: 	foreach my $partid (@{$parts}) {
 2940: 	    my @matchKey = grep /^resource\.$partid\..*?\.submission$/,@versionKeys;
 2941: #	    next if ($$record{"$version:resource.$partid.solved"} eq '');
 2942: 	    $displaySub[0].=(exists $$record{$version.':'.$matchKey[0]}) ? 
 2943: 		'<b>Part&nbsp;'.$partid.'&nbsp;'.
 2944: 		($$record{"$version:resource.$partid.tries"} eq '' ? 'Trial&nbsp;not&nbsp;counted' :
 2945: 		 'Trial&nbsp;'.$$record{"$version:resource.$partid.tries"}).'</b>&nbsp; '.
 2946: 		 &cleanRecord($$record{$version.':'.$matchKey[0]},$$responseType{$partid},$$symbx).'<br />' : '';
 2947: 	    $displaySub[1].=(exists $$record{"$version:resource.$partid.award"}) ?
 2948: 		'<b>Part&nbsp;'.$partid.'</b> &nbsp;'.
 2949: 		lc($$record{"$version:resource.$partid.award"}).' '.
 2950: 		$mark{$$record{"$version:resource.$partid.solved"}}.'<br />' : '';
 2951: 	    $displaySub[2].=(exists $$record{"$version:resource.$partid.regrader"}) ?
 2952: 		$$record{"$version:resource.$partid.regrader"}.' (<b>Part:</b> '.$partid.')' : '';
 2953: 	}
 2954: 	$displaySub[2].=(exists $$record{"$version:resource.regrader"}) ?
 2955: 	    $$record{"$version:resource.regrader"} : '';
 2956: 	$studentTable.='<td>'.$displaySub[0].'&nbsp;</td><td>'.$displaySub[1].
 2957: 	    ($displaySub[2] eq '' ? '' : 'Manually graded by '.$displaySub[2]).'&nbsp;</td></tr>';
 2958:     }
 2959:     $studentTable.='</table></td></tr></table>';
 2960:     return $studentTable;
 2961: }
 2962: 
 2963: sub updateGradeByPage {
 2964:     my ($request) = shift;
 2965: 
 2966:     my $cdom      = $ENV{"course.$ENV{'request.course.id'}.domain"};
 2967:     my $cnum      = $ENV{"course.$ENV{'request.course.id'}.num"};
 2968:     my $getsec    = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};
 2969:     my $pageTitle = $ENV{'form.page'};
 2970:     my ($classlist,undef,$fullname) = &getclasslist($getsec,'1');
 2971:     my ($uname,$udom) = split(/:/,$ENV{'form.student'});
 2972:     my $usec=$classlist->{$ENV{'form.student'}}[5];
 2973:     if (!&canmodify($usec)) {
 2974: 	$request->print('<font color="red">Unable to modify requested student.('.$ENV{'form.student'}.'</font>');
 2975: 	$request->print(&show_grading_menu_form($ENV{'form.symb'},$ENV{'form.url'}));
 2976: 	return;
 2977:     }
 2978:     my $result='<h3><font color="#339933">&nbsp;'.$ENV{'form.title'}.'</font></h3>';
 2979:     $result.='<h3>&nbsp;Student: '.$$fullname{$ENV{'form.student'}}.
 2980: 	'<font color="#999999"> ('.$uname.($udom eq $cdom ? '':':'.$udom).')</font></h3>'."\n";
 2981: 
 2982:     $request->print($result);
 2983: 
 2984:     my $navmap = Apache::lonnavmaps::navmap-> new($ENV{'request.course.fn'}.'.db',
 2985: 						  $ENV{'request.course.fn'}.'_parms.db',1, 1);
 2986:     my ($mapUrl, $id, $resUrl) = split(/___/, $ENV{'form.page'});
 2987:     my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
 2988: 
 2989:     my $iterator = $navmap->getIterator($map->map_start(),
 2990: 					$map->map_finish());
 2991: 
 2992:     my $studentTable='<table border="0"><tr><td bgcolor="#777777">'.
 2993: 	'<table border="0"><tr bgcolor="#e6ffff">'.
 2994: 	'<td align="center"><b>&nbsp;No&nbsp;</b></td>'.
 2995: 	'<td><b>&nbsp;Title&nbsp;</b></td>'.
 2996: 	'<td><b>&nbsp;Previous Score&nbsp;</b></td>'.
 2997: 	'<td><b>&nbsp;New Score&nbsp;</b></td></tr>';
 2998: 
 2999:     $iterator->next(); # skip the first BEGIN_MAP
 3000:     my $curRes = $iterator->next(); # for "current resource"
 3001:     my ($depth,$question,$changeflag)= (1,1,0);
 3002:     while ($depth > 0) {
 3003:         if($curRes == $iterator->BEGIN_MAP) { $depth++; }
 3004:         if($curRes == $iterator->END_MAP) { $depth--; }
 3005: 
 3006:         if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) {
 3007: 	    my $parts = $curRes->parts();
 3008:             my $title = $curRes->compTitle();
 3009: 	    my $symbx = $curRes->symb();
 3010: 	    $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$question.
 3011: 		(scalar(@{$parts}) == 1 ? '' : '<br>('.scalar(@{$parts}).'&nbsp;parts)').'</td>';
 3012: 	    $studentTable.='<td valign="top">&nbsp;<b>'.$title.'</b>&nbsp;</td>';
 3013: 
 3014: 	    my %newrecord=();
 3015: 	    my @displayPts=();
 3016: 	    foreach my $partid (@{$parts}) {
 3017: 		my $newpts = $ENV{'form.GD_BOX'.$question.'_'.$partid};
 3018: 		my $oldpts = $ENV{'form.oldpts'.$question.'_'.$partid};
 3019: 
 3020: 		my $wgt = $ENV{'form.WGT'.$question.'_'.$partid} != 0 ? 
 3021: 		    $ENV{'form.WGT'.$question.'_'.$partid} : 1;
 3022: 		my $partial = $newpts/$wgt;
 3023: 		my $score;
 3024: 		if ($partial > 0) {
 3025: 		    $score = 'correct_by_override';
 3026: 		} elsif ($partial == 0) {
 3027: 		    $score = 'incorrect_by_override';
 3028: 		}
 3029: 		if ($ENV{'form.GD_SEL'.$question.'_'.$partid} eq 'excused') {
 3030: 		    $partial = '';
 3031: 		    $score = 'excused';
 3032: 		}
 3033: 		my $oldstatus = $ENV{'form.solved'.$question.'_'.$partid};
 3034: 		$displayPts[0].='&nbsp;<b>Part</b> '.$partid.' = '.
 3035: 		    (($oldstatus eq 'excused') ? 'excused' : $oldpts).
 3036: 		    '&nbsp;<br>';
 3037: 		$displayPts[1].='&nbsp;<b>Part</b> '.$partid.' = '.
 3038: 		    ($oldstatus eq 'correct_by_student' ? $oldpts :
 3039: 		     (($score eq 'excused') ? 'excused' : $newpts)).
 3040: 		    '&nbsp;<br>';
 3041: 
 3042: 		$question++;
 3043: 		if (($oldstatus eq 'correct_by_student') ||
 3044: 		    ($newpts eq $oldpts && $score eq $oldstatus))
 3045: 		{
 3046: 		    next;
 3047: 		}
 3048: 		$newrecord{'resource.'.$partid.'.awarded'}  = $partial if $partial ne '';
 3049: 		$newrecord{'resource.'.$partid.'.solved'}   = $score;
 3050: 		$newrecord{'resource.'.$partid.'.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
 3051: 
 3052: 		$changeflag++;
 3053: 	    }
 3054: 	    if (scalar(keys(%newrecord)) > 0) {
 3055: 		&Apache::lonnet::cstore(\%newrecord,$symbx,$ENV{'request.course.id'},
 3056: 					$udom,$uname);
 3057: 	    }
 3058: 	    $studentTable.='<td valign="top">'.$displayPts[0].'</td>'.
 3059: 		'<td valign="top">'.$displayPts[1].'</td>'.
 3060: 		'</tr>';
 3061: 
 3062: 	}
 3063:         $curRes = $iterator->next();
 3064:     }
 3065: 
 3066:     $navmap->untieHashes();
 3067: 
 3068:     $studentTable.='</td></tr></table></td></tr></table>';
 3069:     $studentTable.=&show_grading_menu_form($ENV{'form.symb'},$ENV{'form.url'});
 3070:     my $grademsg=($changeflag == 0 ? 'No score was changed or updated.' :
 3071: 		  'The scores were changed for '.
 3072: 		  $changeflag.' problem'.($changeflag == 1 ? '.' : 's.'));
 3073:     $request->print($grademsg.$studentTable);
 3074: 
 3075:     return '';
 3076: }
 3077: 
 3078: #-------- end of section for handling grading by page/sequence ---------
 3079: #
 3080: #-------------------------------------------------------------------
 3081: 
 3082: #--------------------Scantron Grading-----------------------------------
 3083: #
 3084: #------ start of section for handling grading by page/sequence ---------
 3085: 
 3086: sub defaultFormData {
 3087:     my ($symb,$url)=@_;
 3088:     return '
 3089:       <input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
 3090:      '<input type="hidden" name="url"     value="'.$url.'" />'."\n".
 3091:      '<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."\n".
 3092:      '<input type="hidden" name="probTitle" value="'.$ENV{'form.probTitle'}.'" />'."\n";
 3093: }
 3094: 
 3095: sub getSequenceDropDown {
 3096:     my ($request,$symb)=@_;
 3097:     my $result='<select name="selectpage">'."\n";
 3098:     my ($titles,$symbx) = &getSymbMap($request);
 3099:     my ($curpage,$type,$mapId) = ($symb =~ /(.*?\.(page|sequence))___(\d+)___/); 
 3100:     my $ctr=0;
 3101:     foreach (@$titles) {
 3102: 	my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
 3103: 	$result.='<option value="'.$$symbx{$_}.'" '.
 3104: 	    ($$symbx{$_} =~ /$curpage$/ ? 'selected="on"' : '').
 3105: 	    '>'.$showtitle.'</option>'."\n";
 3106: 	$ctr++;
 3107:     }
 3108:     $result.= '</select>';
 3109:     return $result;
 3110: }
 3111: 
 3112: sub scantron_uploads {
 3113:     if (!-e $Apache::lonnet::perlvar{'lonScansDir'}) { return ''};
 3114:     my $result=	'<select name="scantron_selectfile">';
 3115:     opendir(DIR,$Apache::lonnet::perlvar{'lonScansDir'});
 3116:     my @files=sort(readdir(DIR));
 3117:     foreach my $filename (@files) {
 3118: 	if ($filename eq '.' or $filename eq '..') { next; }
 3119: 	$result.="<option>$filename</option>\n";
 3120:     }
 3121:     closedir(DIR);
 3122:     $result.="</select>";
 3123:     return $result;
 3124: }
 3125: 
 3126: sub scantron_scantab {
 3127:     my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
 3128:     my $result='<select name="scantron_format">'."\n";
 3129:     foreach my $line (<$fh>) {
 3130: 	my ($name,$descrip)=split(/:/,$line);
 3131: 	if ($name =~ /^\#/) { next; }
 3132: 	$result.='<option value="'.$name.'">'.$descrip.'</option>'."\n";
 3133:     }
 3134:     $result.='</select>'."\n";
 3135: 
 3136:     return $result;
 3137: }
 3138: 
 3139: sub scantron_selectphase {
 3140:     my ($r) = @_;
 3141:     my ($symb,$url)=&get_symb_and_url($r);
 3142:     if (!$symb) {return '';}
 3143:     my $sequence_selector=&getSequenceDropDown($r,$symb);
 3144:     my $default_form_data=&defaultFormData($symb,$url);
 3145:     my $grading_menu_button=&show_grading_menu_form($symb,$url);
 3146:     my $file_selector=&scantron_uploads();
 3147:     my $format_selector=&scantron_scantab();
 3148:     my $result;
 3149:     $result.= <<SCANTRONFORM;
 3150: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantro_process">
 3151:   <input type="hidden" name="command" value="scantron_process" />
 3152:   $default_form_data
 3153:   <table width="100%" border="0">
 3154:     <tr>
 3155:       <td bgcolor="#777777">
 3156:         <table width="100%" border="0">
 3157:           <tr bgcolor="#e6ffff">
 3158:             <td>
 3159:               &nbsp;<b>Specify file location and which Folder/Sequence to grade</b>
 3160:             </td>
 3161:           </tr>
 3162:           <tr bgcolor="#ffffe6">
 3163:             <td>
 3164:                Sequence to grade: $sequence_selector
 3165: 	    </td>
 3166:           </tr>
 3167:           <tr bgcolor="#ffffe6">
 3168:             <td>
 3169: 		Filename of scoring office file: $file_selector
 3170: 	    </td>
 3171:           </tr>
 3172:           <tr bgcolor="#ffffe6">
 3173:             <td>
 3174:               Format of data file: $format_selector
 3175: 	    </td>
 3176:           </tr>
 3177:         </table>
 3178:       </td>
 3179:     </tr>
 3180:   </table>
 3181:   <input type="submit" value="Submit" />
 3182: </form>
 3183: $grading_menu_button
 3184: SCANTRONFORM
 3185: 
 3186:     return $result;
 3187: }
 3188: 
 3189: sub get_scantron_config {
 3190:     my ($which) = @_;
 3191:     my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
 3192:     my %config;
 3193:     foreach my $line (<$fh>) {
 3194: 	my ($name,$descrip)=split(/:/,$line);
 3195: 	if ($name ne $which ) { next; }
 3196: 	chomp($line);
 3197: 	my @config=split(/:/,$line);
 3198: 	$config{'name'}=$config[0];
 3199: 	$config{'description'}=$config[1];
 3200: 	$config{'CODElocation'}=$config[2];
 3201: 	$config{'CODEstart'}=$config[3];
 3202: 	$config{'CODElength'}=$config[4];
 3203: 	$config{'IDstart'}=$config[5];
 3204: 	$config{'IDlength'}=$config[6];
 3205: 	$config{'Qstart'}=$config[7];
 3206: 	$config{'Qlength'}=$config[8];
 3207: 	$config{'Qoff'}=$config[9];
 3208: 	$config{'Qon'}=$config[10];
 3209: 	last;
 3210:     }
 3211:     return %config;
 3212: }
 3213: 
 3214: sub username_to_idmap {
 3215:     my ($classlist)= @_;
 3216:     my %idmap;
 3217:     foreach my $student (keys(%$classlist)) {
 3218: 	$idmap{$classlist->{$student}->[&Apache::loncoursedata::CL_ID]}=
 3219: 	    $student;
 3220:     }
 3221:     return %idmap;
 3222: }
 3223: 
 3224: sub scantron_parse_scanline {
 3225:     my ($line,$scantron_config)=@_;
 3226:     my %record;
 3227:     my $questions=substr($line,$$scantron_config{'Qstart'}-1);
 3228:     my $data=substr($line,0,$$scantron_config{'Qstart'}-1);
 3229:     if ($$scantron_config{'CODElocation'} ne 0) {
 3230: 	if ($$scantron_config{'CODElocation'} < 0) {
 3231: 	    $record{'scantron.CODE'}=substr($data,$$scantron_config{'CODEstart'}-1,
 3232: 					    $$scantron_config{'CODElength'});
 3233: 	} else {
 3234: 	    #FIXME interpret first N questions
 3235: 	}
 3236:     }
 3237:     $record{'scantron.ID'}=substr($data,$$scantron_config{'IDstart'}-1,
 3238: 				  $$scantron_config{'IDlength'});
 3239:     my @alphabet=('A'..'Z');
 3240:     my $questnum=0;
 3241:     while ($questions) {
 3242: 	$questnum++;
 3243: 	my $currentquest=substr($questions,0,$$scantron_config{'Qlength'});
 3244: 	substr($questions,0,$$scantron_config{'Qlength'})='';
 3245: 	if (length($currentquest) < $$scantron_config{'Qlength'}) { next; }
 3246: 	my (@array)=split(/$$scantron_config{'Qon'}/,$currentquest);
 3247: 	if (scalar(@array) gt 2) {
 3248: 	    #FIXME do something intelligent with double bubbles
 3249: 	    Apache->request->print("<br ><b>Wha!!!</b> <pre>".scalar(@array).
 3250: 				   '-'.$currentquest.'-'.$questnum.'</pre><br />');
 3251: 	}
 3252: 	if (length($array[0]) eq $$scantron_config{'Qlength'}) {
 3253: 	    $record{"scantron.$questnum.answer"}='';
 3254: 	} else {
 3255: 	    $record{"scantron.$questnum.answer"}=$alphabet[length($array[0])];
 3256: 	}
 3257:     }
 3258:     $record{'scantron.maxquest'}=$questnum;
 3259:     return \%record;
 3260: }
 3261: 
 3262: sub scantron_add_delay {
 3263: }
 3264: 
 3265: sub scantron_find_student {
 3266:     my ($scantron_record,$idmap)=@_;
 3267:     my $scanID=$$scantron_record{'scantron.ID'};
 3268:     foreach my $id (keys(%$idmap)) {
 3269: 	Apache->request->print('<pre>checking studnet -'.$id.'- againt -'.$scanID.'- </pre>');
 3270: 	if (lc($id) eq lc($scanID)) { Apache->request->print('success');return $$idmap{$id}; }
 3271:     }
 3272:     return undef;
 3273: }
 3274: 
 3275: sub scantron_filter {
 3276:     my ($curres)=@_;
 3277:     if (ref($curres) && $curres->is_problem() && !$curres->randomout) {
 3278: 	return 1;
 3279:     }
 3280:     return 0;
 3281: }
 3282: 
 3283: sub scantron_process_students {
 3284:     my ($r) = @_;
 3285:     my (undef,undef,$sequence)=split(/___/,$ENV{'form.selectpage'});
 3286:     my ($symb,$url)=&get_symb_and_url($r);
 3287:     if (!$symb) {return '';}
 3288:     my $default_form_data=&defaultFormData($symb,$url);
 3289: 
 3290:     my %scantron_config=&get_scantron_config($ENV{'form.scantron_format'});
 3291:     my $scanlines=Apache::File->new($Apache::lonnet::perlvar{'lonScansDir'}."/$ENV{'form.scantron_selectfile'}");
 3292:     my @scanlines=<$scanlines>;
 3293:     my $classlist=&Apache::loncoursedata::get_classlist();
 3294:     my %idmap=&username_to_idmap($classlist);
 3295:     my $navmap=Apache::lonnavmaps::navmap->new($ENV{'request.course.fn'}.'.db',$ENV{'request.course.fn'}.'_parms.db',1, 1);
 3296:     my $map=$navmap->getResourceByUrl($sequence);
 3297:     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
 3298:     $r->print("geto ".scalar(@resources)."<br />");
 3299:     my $result= <<SCANTRONFORM;
 3300: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload">
 3301:   <input type="hidden" name="command" value="scantron_configphase" />
 3302:   $default_form_data
 3303: SCANTRONFORM
 3304:     $r->print($result);
 3305: 
 3306:     my @delayqueue;
 3307:     my $totalcorrect;
 3308:     my $totalincorrect;
 3309: 
 3310:     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,
 3311: 	           'Scantron Status','Scantron Progress',scalar(@scanlines));
 3312:     foreach my $line (@scanlines) {
 3313: 	my $studentcorrect;
 3314: 	my $studentincorrect;
 3315: 
 3316: 	chomp($line);
 3317: 	my $scan_record=&scantron_parse_scanline($line,\%scantron_config);
 3318: 	my ($uname,$udom);
 3319: 	if ($uname=&scantron_find_student($scan_record,\%idmap)) {
 3320: 	    &scantron_add_delay(\@delayqueue,$line,
 3321: 				'Unable to find a student that matches');
 3322: 	}
 3323: 	$r->print('<pre>doing studnet'.$uname.'</pre>');
 3324: 	($uname,$udom)=split(/:/,$uname);
 3325: 	&Apache::lonnet::delenv('form.counter');
 3326: 	&Apache::lonnet::appenv(%$scan_record);
 3327: #    &Apache::lonhomework::showhash(%ENV);
 3328:     $Apache::lonxml::debug=1;
 3329: 	&Apache::lonxml::debug("line is $line");
 3330: 	
 3331: 	    my $i=0;
 3332: 	foreach my $resource (@resources) {
 3333: 	    $i++;
 3334: 	    my $result=&Apache::lonnet::ssi($resource->src(),
 3335: 				 ('submitted'     =>'scantron',
 3336: 				  'grade_target'  =>'grade',
 3337: 				  'grade_username'=>$uname,
 3338: 				  'grade_domain'  =>$udom,
 3339: 				  'grade_courseid'=>$ENV{'request.course.id'},
 3340: 				  'grade_symb'    =>$resource->symb()));
 3341: 	    my %score=&Apache::lonnet::restore($resource->symb(),
 3342: 					       $ENV{'request.course.id'},
 3343: 					       $udom,$uname);
 3344: 	    foreach my $part ($resource->{PARTS}) {
 3345: 		if ($score{'resource.'.$part.'.solved'} =~ /^correct/) {
 3346: 		    $studentcorrect++;
 3347: 		    $totalcorrect++;
 3348: 		} else {
 3349: 		    $studentincorrect++;
 3350: 		    $totalincorrect++;
 3351: 		}
 3352: 	    }
 3353: 	    $r->print('<pre>'.
 3354: 		      $resource->symb().'-'.
 3355: 		      $resource->src().'-'.'</pre>result is'.$result);
 3356: 	    &Apache::lonhomework::showhash(%score);
 3357: 	#    if ($i eq 3) {last;}
 3358: 	}
 3359: 	&Apache::lonnet::delenv('form.counter');
 3360: 	&Apache::lonnet::delenv('scantron\.');
 3361: 	&Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
 3362:              'last student Who got a '.$studentcorrect.' correct and '.
 3363: 	     $studentincorrect.' incorrect. The class has gotten '.
 3364:              $totalcorrect.' correct and '.$totalincorrect.' incorrect');
 3365: 	last;
 3366: 	#FIXME
 3367: 	#get iterator for $sequence
 3368: 	#foreach question 'submit' the students answer to the server
 3369: 	#   through grade target {
 3370: 	#   generate data to pass back that includes grade recevied
 3371: 	#}
 3372:     }
 3373:     $Apache::lonxml::debug=0;
 3374:     foreach my $delay (@delayqueue) {
 3375: 	#FIXME
 3376: 	#print out each delayed student with interface to select how
 3377: 	#  to repair student provided info
 3378: 	#Expected errors include
 3379: 	#  1 bad/no stuid/username
 3380: 	#  2 invalid bubblings
 3381: 	
 3382:     }
 3383:     #FIXME
 3384:     # if delay queue exists 2 submits one to process delayed students one
 3385:     #     to ignore delayed students, possibly saving the delay queue for later
 3386:     
 3387:     $navmap->untieHashes();
 3388: }
 3389: #-------- end of section for handling grading scantron forms -------
 3390: #
 3391: #-------------------------------------------------------------------
 3392: 
 3393: 
 3394: #-------------------------- Menu interface -------------------------
 3395: #
 3396: #--- Show a Grading Menu button - Calls the next routine ---
 3397: sub show_grading_menu_form {
 3398:     my ($symb,$url)=@_;
 3399:     my $result.='<form action="/adm/grades" method="post">'."\n".
 3400: 	'<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
 3401: 	'<input type="hidden" name="url" value="'.$url.'" />'."\n".
 3402: 	'<input type="hidden" name="saveState"  value="'.$ENV{'form.saveState'}.'" />'."\n".
 3403: 	'<input type="hidden" name="command" value="gradingmenu" />'."\n".
 3404: 	'<input type="submit" name="submit" value="Grading Menu" />'."\n".
 3405: 	'</form>'."\n";
 3406:     return $result;
 3407: }
 3408: 
 3409: # -- Retrieve choices for grading form
 3410: sub savedState {
 3411:     my %savedState = ();
 3412:     if ($ENV{'form.saveState'}) {
 3413: 	foreach (split(/:/,$ENV{'form.saveState'})) {
 3414: 	    my ($key,$value) = split(/=/,$_,2);
 3415: 	    $savedState{$key} = $value;
 3416: 	}
 3417:     }
 3418:     return \%savedState;
 3419: }
 3420: 
 3421: #--- Displays the main menu page -------
 3422: sub gradingmenu {
 3423:     my ($request) = @_;
 3424:     my ($symb,$url)=&get_symb_and_url($request);
 3425:     if (!$symb) {return '';}
 3426:     my $probTitle = &Apache::lonnet::gettitle($symb);
 3427: 
 3428:     $request->print(<<GRADINGMENUJS);
 3429: <script type="text/javascript" language="javascript">
 3430:     function checkChoice(formname,val,cmdx) {
 3431: 	if (val <= 2) {
 3432: 	    var cmd = radioSelection(formname.radioChoice);
 3433: 	    var cmdsave = cmd;
 3434: 	} else {
 3435: 	    cmd = cmdx;
 3436: 	    cmdsave = 'submission';
 3437: 	}
 3438: 	formname.command.value = cmd;
 3439: 	formname.saveState.value = "saveCmd="+cmdsave+":saveSec="+pullDownSelection(formname.section)+
 3440: 	    ":saveSub="+radioSelection(formname.submitonly)+":saveStatus="+pullDownSelection(formname.Status);
 3441: 	if (val < 5) formname.submit();
 3442: 	if (val == 5) {
 3443: 	    if (!checkReceiptNo(formname,'notOK')) { return false;}
 3444: 	    formname.submit();
 3445: 	}
 3446:     }
 3447: 
 3448:     function checkReceiptNo(formname,nospace) {
 3449: 	var receiptNo = formname.receipt.value;
 3450: 	var checkOpt = false;
 3451: 	if (nospace == "OK" && isNaN(receiptNo)) {checkOpt = true;}
 3452: 	if (nospace == "notOK" && (isNaN(receiptNo) || receiptNo == "")) {checkOpt = true;}
 3453: 	if (checkOpt) {
 3454: 	    alert("Please enter a receipt number given by a student in the receipt box.");
 3455: 	    formname.receipt.value = "";
 3456: 	    formname.receipt.focus();
 3457: 	    return false;
 3458: 	}
 3459: 	return true;
 3460:     }
 3461: </script>
 3462: GRADINGMENUJS
 3463:     &commonJSfunctions($request);
 3464:     my $result='<h3>&nbsp;<font color="#339933">Manual Grading/View Submission</font></h3>';
 3465:     my ($table,undef,$hdgrade) = &showResourceInfo($url,$probTitle);
 3466:     $result.=$table;
 3467:     my (undef,$sections) = &getclasslist('all','0');
 3468:     my $savedState = &savedState();
 3469:     my $saveCmd = ($$savedState{'saveCmd'} eq '' ? 'submission' : $$savedState{'saveCmd'});
 3470:     my $saveSec = ($$savedState{'saveSec'} eq '' ? 'all' : $$savedState{'saveSec'});
 3471:     my $saveSub = ($$savedState{'saveSub'} eq '' ? 'all' : $$savedState{'saveSub'});
 3472:     my $saveStatus = ($$savedState{'saveStatus'} eq '' ? 'Active' : $$savedState{'saveStatus'});
 3473: 
 3474:     $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
 3475: 	'<input type="hidden" name="symb"        value="'.$symb.'" />'."\n".
 3476: 	'<input type="hidden" name="url"         value="'.$url.'" />'."\n".
 3477: 	'<input type="hidden" name="handgrade"   value="'.$hdgrade.'" />'."\n".
 3478: 	'<input type="hidden" name="probTitle"   value="'.$probTitle.'" />'."\n".
 3479: 	'<input type="hidden" name="command"     value="" />'."\n".
 3480: 	'<input type="hidden" name="saveState"   value="" />'."\n".
 3481: 	'<input type="hidden" name="gradingMenu" value="1" />'."\n".
 3482: 	'<input type="hidden" name="showgrading" value="yes" />'."\n";
 3483: 
 3484:     $result.='<table width="100%" border=0><tr><td bgcolor=#777777>'."\n".
 3485: 	'<table width=100% border=0><tr bgcolor="#e6ffff"><td colspan="2">'."\n".
 3486: 	'&nbsp;<b>Select a Grading/Viewing Option</b></td></tr>'."\n".
 3487: 	'<tr bgcolor="#ffffe6" valign="top"><td>'."\n";
 3488: 
 3489:     $result.='<table width="100%" border=0>';
 3490:     $result.='<tr bgcolor="#ffffe6" valign="top"><td>'."\n".
 3491: 	'&nbsp;Select Section: <select name="section">'."\n";
 3492:     if (ref($sections)) {
 3493: 	foreach (sort (@$sections)) {$result.='<option value="'.$_.'" '.
 3494: 					 ($saveSec eq $_ ? 'selected="on"' : '').'>'.$_.'</option>'."\n";}
 3495:     }
 3496:     $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="on"' : ''). '>all</select> &nbsp; ';
 3497: 
 3498:     $result.='Student Status:</b>'.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,undef);
 3499: 
 3500:     if (ref($sections)) {
 3501: 	$result.='&nbsp;(Section "no" implies the students were not assigned a section.)<br />' 
 3502: 	    if (grep /no/,@$sections);
 3503:     }
 3504:     $result.='</td></tr>';
 3505: 
 3506:     $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.
 3507: 	'<input type="radio" name="radioChoice" value="submission" '.
 3508: 	($saveCmd eq 'submission' ? 'checked' : '').'> '.'<b>Current Resource:</b> For one or more students'.
 3509: 	'<br />&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;-->For students with '.
 3510: 	'<input type="radio" name="submitonly" value="yes" '.
 3511: 	($saveSub eq 'yes' ? 'checked' : '').' /> submissions or '.
 3512: 	'<input type="radio" name="submitonly" value="all" '.
 3513: 	($saveSub eq 'all' ? 'checked' : '').' /> for all</td></tr>'."\n";
 3514: 
 3515:     $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.
 3516: 	'<input type="radio" name="radioChoice" value="viewgrades" '.
 3517: 	($saveCmd eq 'viewgrades' ? 'checked' : '').'> '.
 3518: 	'<b>Current Resource:</b> For all students in selected section or course</td></tr>'."\n";
 3519: 
 3520:     $result.='<tr bgcolor="#ffffe6" valign="top"><td>'.
 3521: 	'<input type="radio" name="radioChoice" value="pickStudentPage" '.
 3522: 	($saveCmd eq 'pickStudentPage' ? 'checked' : '').'> '.
 3523: 	'The <b>complete</b> set/page/sequence: For one student</td></tr>'."\n";
 3524: 
 3525:     $result.='<tr bgcolor="#ffffe6"><td><br />'.
 3526: 	'<input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="View/Grade/Regrade" />'.
 3527: 	'</td></tr></table>'."\n";
 3528: 
 3529:     $result.='</td><td valign="top">';
 3530: 
 3531:     $result.='<table width="100%" border=0>';
 3532:     $result.='<tr bgcolor="#ffffe6"><td>'.
 3533: 	'<input type="button" onClick="javascript:checkChoice(this.form,\'3\',\'csvform\');" value="Upload" />'.
 3534: 	' scores from file </td></tr>'."\n";
 3535: 
 3536:     $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.
 3537: 	'<input type="button" onClick="javascript:checkChoice(this.form,\'4\',\'scantron_selectphase\');'.
 3538: 	'" value="Grade" /> scantron forms</td></tr>'."\n";
 3539: 
 3540:     if ((&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'})) && ($symb)) {
 3541: 	$result.='<tr bgcolor="#ffffe6"valign="top"><td>'.
 3542: 	    '<input type="button" onClick="javascript:checkChoice(this.form,\'5\',\'verify\');" value="Verify" />'.
 3543: 	    ' submission Receipt no: '.unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}).
 3544: 	    '-<input type="text" name="receipt" size="4" onChange="javascript:checkReceiptNo(this.form,\'OK\')">'.
 3545: 	    '</td></tr>'."\n";
 3546:     } 
 3547: 
 3548:     $result.='</form></td></tr></table>'."\n".
 3549: 	'</td></tr></table>'."\n".
 3550: 	'</td></tr></table>'."\n";
 3551:     return $result;
 3552: }
 3553: 
 3554: sub handler {
 3555:     my $request=$_[0];
 3556: 
 3557:     undef(%perm);
 3558:     if ($ENV{'browser.mathml'}) {
 3559: 	$request->content_type('text/xml');
 3560:     } else {
 3561: 	$request->content_type('text/html');
 3562:     }
 3563:     $request->send_http_header;
 3564:     return '' if $request->header_only;
 3565:     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});
 3566:     my $url=$ENV{'form.url'};
 3567:     my $symb=$ENV{'form.symb'};
 3568:     my $command=$ENV{'form.command'};
 3569:     if (!$url) {
 3570: 	my ($temp1,$temp2);
 3571: 	($temp1,$temp2,$ENV{'form.url'})=split(/___/,$symb);
 3572: 	$url = $ENV{'form.url'};
 3573:     }
 3574:     &send_header($request);
 3575:     if ($url eq '' && $symb eq '') {
 3576: 	if ($ENV{'user.adv'}) {
 3577: 	    if (($ENV{'form.codeone'}) && ($ENV{'form.codetwo'}) &&
 3578: 		($ENV{'form.codethree'})) {
 3579: 		my $token=$ENV{'form.codeone'}.'*'.$ENV{'form.codetwo'}.'*'.
 3580: 		    $ENV{'form.codethree'};
 3581: 		my ($tsymb,$tuname,$tudom,$tcrsid)=
 3582: 		    &Apache::lonnet::checkin($token);
 3583: 		if ($tsymb) {
 3584: 		    my ($map,$id,$url)=split(/\_\_\_/,$tsymb);
 3585: 		    if (&Apache::lonnet::allowed('mgr',$tcrsid)) {
 3586: 			$request->print(&Apache::lonnet::ssi_body('/res/'.$url,
 3587: 					  ('grade_username' => $tuname,
 3588: 					   'grade_domain' => $tudom,
 3589: 					   'grade_courseid' => $tcrsid,
 3590: 					   'grade_symb' => $tsymb)));
 3591: 		    } else {
 3592: 			$request->print('<h3>Not authorized: '.$token.'</h3>');
 3593: 		    }
 3594: 		} else {
 3595: 		    $request->print('<h3>Not a valid DocID: '.$token.'</h3>');
 3596: 		}
 3597: 	    } else {
 3598: 		$request->print(&Apache::lonxml::tokeninputfield());
 3599: 	    }
 3600: 	}
 3601:     } else {
 3602: 	if (!($perm{'vgr'}=&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'}))) {
 3603: 	    if ($perm{'vgr'}=&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'}.'/'.$ENV{'request.course.sec'})) {
 3604: 		$perm{'vgr_section'}=$ENV{'request.course.sec'};
 3605: 	    } else {
 3606: 		delete($perm{'vgr'});
 3607: 	    }
 3608: 	}
 3609: 	if (!($perm{'mgr'}=&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'}))) {
 3610: 	    if ($perm{'mgr'}=&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'}.'/'.$ENV{'request.course.sec'})) {
 3611: 		$perm{'mgr_section'}=$ENV{'request.course.sec'};
 3612: 	    } else {
 3613: 		delete($perm{'mgr'});
 3614: 	    }
 3615: 	}
 3616: 
 3617: 	if ($command eq 'submission' && $perm{'vgr'}) {
 3618: 	    ($ENV{'form.student'} eq '' ? &listStudents($request) : &submission($request,0,0));
 3619: 	} elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) {
 3620: 	    &pickStudentPage($request);
 3621: 	} elsif ($command eq 'displayPage' && $perm{'vgr'}) {
 3622: 	    &displayPage($request);
 3623: 	} elsif ($command eq 'gradeByPage' && $perm{'mgr'}) {
 3624: 	    &updateGradeByPage($request);
 3625: 	} elsif ($command eq 'processGroup' && $perm{'vgr'}) {
 3626: 	    &processGroup($request);
 3627: 	} elsif ($command eq 'gradingmenu' && $perm{'vgr'}) {
 3628: 	    $request->print(&gradingmenu($request));
 3629: 	} elsif ($command eq 'viewgrades' && $perm{'vgr'}) {
 3630: 	    $request->print(&viewgrades($request));
 3631: 	} elsif ($command eq 'handgrade' && $perm{'mgr'}) {
 3632: 	    $request->print(&processHandGrade($request));
 3633: 	} elsif ($command eq 'editgrades' && $perm{'mgr'}) {
 3634: 	    $request->print(&editgrades($request));
 3635: 	} elsif ($command eq 'verify' && $perm{'vgr'}) {
 3636: 	    $request->print(&verifyreceipt($request));
 3637: 	} elsif ($command eq 'csvform' && $perm{'mgr'}) {
 3638: 	    $request->print(&upcsvScores_form($request));
 3639: 	} elsif ($command eq 'csvupload' && $perm{'mgr'}) {
 3640: 	    $request->print(&csvupload($request));
 3641: 	} elsif ($command eq 'csvuploadmap' && $perm{'mgr'} ) {
 3642: 	    $request->print(&csvuploadmap($request));
 3643: 	} elsif ($command eq 'csvuploadassign' && $perm{'mgr'}) {
 3644: 	    if ($ENV{'form.associate'} ne 'Reverse Association') {
 3645: 		$request->print(&csvuploadassign($request));
 3646: 	    } else {
 3647: 		if ( $ENV{'form.upfile_associate'} ne 'reverse' ) {
 3648: 		    $ENV{'form.upfile_associate'} = 'reverse';
 3649: 		} else {
 3650: 		    $ENV{'form.upfile_associate'} = 'forward';
 3651: 		}
 3652: 		$request->print(&csvuploadmap($request));
 3653: 	    }
 3654: 	} elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {
 3655: 	    $request->print(&scantron_selectphase($request));
 3656: 	} elsif ($command eq 'scantron_process' && $perm{'mgr'}) {
 3657: 	    $request->print(&scantron_process_students($request));
 3658: 	} elsif ($command) {
 3659: 	    $request->print("Access Denied");
 3660: 	}
 3661:     }
 3662:     &send_footer($request);
 3663:     return '';
 3664: }
 3665: 
 3666: sub send_header {
 3667:     my ($request)= @_;
 3668:     $request->print(&Apache::lontexconvert::header());
 3669: #  $request->print("
 3670: #<script>
 3671: #remotewindow=open('','homeworkremote');
 3672: #remotewindow.close();
 3673: #</script>"); 
 3674:     $request->print(&Apache::loncommon::bodytag('Grading'));
 3675: }
 3676: 
 3677: sub send_footer {
 3678:     my ($request)= @_;
 3679:     $request->print('</body>');
 3680:     $request->print(&Apache::lontexconvert::footer());
 3681: }
 3682: 
 3683: 1;
 3684: 
 3685: __END__;

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