Annotation of loncom/homework/grades.pm, revision 1.118

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

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

Internal Server Error

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

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

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