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

1.17      albertel    1: # The LearningOnline Network with CAPA
1.13      albertel    2: # The LON-CAPA Grading handler
1.17      albertel    3: #
1.272   ! albertel    4: # $Id: grades.pm,v 1.271 2005/06/04 03:36:36 albertel 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.1       albertel   28: 
                     29: package Apache::grades;
                     30: use strict;
                     31: use Apache::style;
                     32: use Apache::lonxml;
                     33: use Apache::lonnet;
1.3       albertel   34: use Apache::loncommon;
1.112     ng         35: use Apache::lonhtmlcommon;
1.68      ng         36: use Apache::lonnavmaps;
1.1       albertel   37: use Apache::lonhomework;
1.55      matthew    38: use Apache::loncoursedata;
1.38      ng         39: use Apache::lonmsg qw(:user_normal_msg);
1.1       albertel   40: use Apache::Constants qw(:common);
1.167     sakharuk   41: use Apache::lonlocal;
1.170     albertel   42: use String::Similarity;
1.87      www        43: 
                     44: my %oldessays=();
1.103     albertel   45: my %perm=();
1.1       albertel   46: 
1.68      ng         47: # ----- These first few routines are general use routines.----
1.44      ng         48: #
1.146     albertel   49: # --- Retrieve the parts from the metadata file.---
1.44      ng         50: sub getpartlist {
1.146     albertel   51:     my ($url,$symb) = @_;
                     52:     my $partorder = &Apache::lonnet::metadata($url, 'partorder');
                     53:     my @parts;
                     54:     if ($partorder) {
                     55: 	for my $part (split (/,/,$partorder)) {
                     56: 	    if (!&Apache::loncommon::check_if_partid_hidden($part,$symb)) {
                     57: 		push(@parts, $part);
                     58: 	    }
                     59: 	}	    
                     60:     } else {
                     61: 	my $metadata = &Apache::lonnet::metadata($url, 'packages');
                     62: 	foreach (split(/\,/,$metadata)) {
                     63: 	    if ($_ =~ /^part_(.*)$/) {
                     64: 		if (!&Apache::loncommon::check_if_partid_hidden($1,$symb)) {
                     65: 		    push(@parts, $1);
                     66: 		}
                     67: 	    }
1.41      ng         68: 	}
1.16      albertel   69:     }
1.146     albertel   70:     my @stores;
                     71:     foreach my $part (@parts) {
                     72: 	my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));
                     73: 	foreach my $key (@metakeys) {
                     74: 	    if ($key =~ m/^stores_\Q$part\E_/) { push(@stores,$key); }
                     75: 	}
                     76:     }
                     77:     return @stores;
1.2       albertel   78: }
                     79: 
1.44      ng         80: # --- Get the symbolic name of a problem and the url
                     81: sub get_symb_and_url {
1.173     albertel   82:     my ($request,$silent) = @_;
1.257     albertel   83:     (my $url=$env{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
                     84:     my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
1.173     albertel   85:     if ($symb eq '') { 
                     86: 	if (!$silent) {
                     87: 	    $request->print("Unable to handle ambiguous references:$url:.");
                     88: 	    return ();
                     89: 	}
                     90:     }
1.44      ng         91:     return ($symb,$url);
1.32      ng         92: }
                     93: 
1.129     ng         94: #--- Format fullname, username:domain if different for display
                     95: #--- Use anywhere where the student names are listed
                     96: sub nameUserString {
                     97:     my ($type,$fullname,$uname,$udom) = @_;
                     98:     if ($type eq 'header') {
1.250     albertel   99: 	return '<b>&nbsp;Fullname&nbsp;</b><font color="#999999">(Username)</font>';
1.129     ng        100:     } else {
                    101: 	return '&nbsp;'.$fullname.'<font color="#999999">&nbsp;('.$uname.
1.257     albertel  102: 	    ($env{'user.domain'} eq $udom ? '' : ' ('.$udom.')').')</font>';
1.129     ng        103:     }
                    104: }
                    105: 
1.44      ng        106: #--- Get the partlist and the response type for a given problem. ---
                    107: #--- Indicate if a response type is coded handgraded or not. ---
1.39      ng        108: sub response_type {
1.125     ng        109:     my ($url,$symb) = shift;
1.257     albertel  110:     $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url))) if ($symb eq '');
1.41      ng        111:     my $allkeys = &Apache::lonnet::metadata($url,'keys');
1.154     albertel  112:     my %vPart;
                    113:     foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) {
                    114: 	$vPart{$partid}=1;
                    115:     }
1.41      ng        116:     my %seen = ();
1.147     albertel  117:     my (@partlist,%handgrade,%responseType);
1.41      ng        118:     foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) {
1.147     albertel  119: 	if (/^\w+response_.*/) {
1.41      ng        120: 	    my ($responsetype,$part) = split(/_/,$_,2);
                    121: 	    my ($partid,$respid) = split(/_/,$part);
1.146     albertel  122: 	    if (&Apache::loncommon::check_if_partid_hidden($partid,$symb)) {
                    123: 		next;
                    124: 	    }
1.154     albertel  125: 	    if (%vPart && !exists($vPart{$partid})) {
                    126: 		next;
                    127: 	    }
1.118     ng        128: 	    $responsetype =~ s/response$//; # make it compatible w/ navmaps - should move to that!!
1.127     ng        129: 	    my ($value) = &Apache::lonnet::EXT('resource.'.$part.'.handgrade',$symb);
1.147     albertel  130: 	    $handgrade{$part} = ($value eq 'yes' ? 'yes' : 'no'); 
                    131: 	    if (!exists($responseType{$partid})) { $responseType{$partid}={}; }
                    132: 	    $responseType{$partid}->{$respid}=$responsetype;
1.41      ng        133: 	    next if ($seen{$partid} > 0);
                    134: 	    $seen{$partid}++;
                    135: 	    push @partlist,$partid;
                    136: 	}
                    137:     }
1.147     albertel  138:     return \@partlist,\%handgrade,\%responseType;
1.39      ng        139: }
                    140: 
1.207     albertel  141: sub get_display_part {
                    142:     my ($partID,$url,$symb)=@_;
                    143:     if (!defined($symb) || $symb eq '') {
1.257     albertel  144: 	$symb=$env{'form.symb'};
1.207     albertel  145: 	if ($symb eq '') { $symb=&Apache::lonnet::symbread($url) }
                    146:     }
                    147:     my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',$symb);
                    148:     if (defined($display) and $display ne '') {
                    149: 	$display.= " (<font color=\"#999900\">id $partID</font>)";
                    150:     } else {
                    151: 	$display=$partID;
                    152:     }
                    153:     return $display;
                    154: }
1.269     raeburn   155: 
1.118     ng        156: #--- Show resource title
                    157: #--- and parts and response type
                    158: sub showResourceInfo {
1.154     albertel  159:     my ($url,$probTitle,$checkboxes) = @_;
                    160:     my $col=3;
                    161:     if ($checkboxes) { $col=4; }
1.118     ng        162:     my $result ='<table border="0">'.
1.167     sakharuk  163: 	'<tr><td colspan="'.$col.'"><font size="+1"><b>'.&mt('Current Resource').': </b>'.
1.154     albertel  164: 	$probTitle.'</font></td></tr>'."\n";
1.147     albertel  165:     my ($partlist,$handgrade,$responseType) = &response_type($url);
1.126     ng        166:     my %resptype = ();
1.122     ng        167:     my $hdgrade='no';
1.154     albertel  168:     my %partsseen;
1.147     albertel  169:     for my $part_resID (sort keys(%$handgrade)) {
                    170: 	my $handgrade=$$handgrade{$part_resID};
                    171: 	my ($partID,$resID) = split(/_/,$part_resID);
                    172: 	my $responsetype = $responseType->{$partID}->{$resID};
1.118     ng        173: 	$hdgrade = $handgrade if ($handgrade eq 'yes');
1.154     albertel  174: 	$result.='<tr>';
                    175: 	if ($checkboxes) {
                    176: 	    if (exists($partsseen{$partID})) {
                    177: 		$result.="<td>&nbsp;</td>";
                    178: 	    } else {
                    179: 		$result.="<td><input type='checkbox' name='vPart' value='$partID' checked='on' /></td>";
                    180: 	    }
                    181: 	    $partsseen{$partID}=1;
                    182: 	}
1.207     albertel  183: 	my $display_part=&get_display_part($partID,$url);
                    184: 	$result.='<td><b>Part: </b>'.$display_part.' <font color="#999999">'.
1.147     albertel  185: 	    $resID.'</font></td>'.
1.118     ng        186: 	    '<td><b>Type: </b>'.$responsetype.'</td></tr>';
                    187: #	    '<td><b>Handgrade: </b>'.$handgrade.'</td></tr>';
                    188:     }
                    189:     $result.='</table>'."\n";
1.147     albertel  190:     return $result,$responseType,$hdgrade,$partlist,$handgrade;
1.118     ng        191: }
                    192: 
1.148     albertel  193: 
                    194: sub get_order {
                    195:     my ($partid,$respid,$symb,$uname,$udom)=@_;
                    196:     my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);
                    197:     $url=&Apache::lonnet::clutter($url);
                    198:     my $subresult=&Apache::lonnet::ssi($url,
                    199: 				       ('grade_target' => 'analyze'),
                    200: 				       ('grade_domain' => $udom),
                    201: 				       ('grade_symb' => $symb),
                    202: 				       ('grade_courseid' => 
1.257     albertel  203: 					        $env{'request.course.id'}),
1.148     albertel  204: 				       ('grade_username' => $uname));
1.149     albertel  205:     (undef,$subresult)=split(/_HASH_REF__/,$subresult,2);
1.148     albertel  206:     my %analyze=&Apache::lonnet::str2hash($subresult);
                    207:     return ($analyze{"$partid.$respid.shown"});
                    208: }
1.118     ng        209: #--- Clean response type for display
1.148     albertel  210: #--- Currently filters option/rank/radiobutton/match/essay response types only.
1.118     ng        211: sub cleanRecord {
1.148     albertel  212:     my ($answer,$response,$symb,$partid,$respid,$record,$order,$version) = @_;
                    213:     my $grayFont = '<font color="#999999">';
                    214:     if ($response =~ /^(option|rank)$/) {
                    215: 	my %answer=&Apache::lonnet::str2hash($answer);
                    216: 	my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
                    217: 	my ($toprow,$bottomrow);
                    218: 	foreach my $foil (@$order) {
                    219: 	    if ($grading{$foil} == 1) {
                    220: 		$toprow.='<td><b>'.$answer{$foil}.'&nbsp;</b></td>';
                    221: 	    } else {
                    222: 		$toprow.='<td><i>'.$answer{$foil}.'&nbsp;</i></td>';
                    223: 	    }
                    224: 	    $bottomrow.='<td>'.$grayFont.$foil.'</font>&nbsp;</td>';
                    225: 	}
                    226: 	return '<blockquote><table border="1">'.
                    227: 	    '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.
                    228: 	    '<tr valign="top"><td>'.$grayFont.'Option ID</font></td>'.
                    229: 	    $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';
                    230:     } elsif ($response eq 'match') {
                    231: 	my %answer=&Apache::lonnet::str2hash($answer);
                    232: 	my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
                    233: 	my @items=&Apache::lonnet::str2array($record->{$version."resource.$partid.$respid.submissionitems"});
                    234: 	my ($toprow,$middlerow,$bottomrow);
                    235: 	foreach my $foil (@$order) {
                    236: 	    my $item=shift(@items);
                    237: 	    if ($grading{$foil} == 1) {
                    238: 		$toprow.='<td><b>'.$item.'&nbsp;</b></td>';
                    239: 		$middlerow.='<td><b>'.$grayFont.$answer{$foil}.'&nbsp;</font></b></td>';
                    240: 	    } else {
                    241: 		$toprow.='<td><i>'.$item.'&nbsp;</i></td>';
                    242: 		$middlerow.='<td><i>'.$grayFont.$answer{$foil}.'&nbsp;</font></i></td>';
                    243: 	    }
                    244: 	    $bottomrow.='<td>'.$grayFont.$foil.'</font>&nbsp;</td>';
1.118     ng        245: 	}
1.126     ng        246: 	return '<blockquote><table border="1">'.
1.148     albertel  247: 	    '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.
                    248: 	    '<tr valign="top"><td>'.$grayFont.'Item ID</font></td>'.
                    249: 	    $middlerow.'</tr>'.
                    250: 	    '<tr valign="top"><td>'.$grayFont.'Option ID</font></td>'.
                    251: 	    $bottomrow.'</tr>'.'</table></blockquote>';
                    252:     } elsif ($response eq 'radiobutton') {
                    253: 	my %answer=&Apache::lonnet::str2hash($answer);
                    254: 	my ($toprow,$bottomrow);
                    255: 	my $correct=($order->[0])+1;
                    256: 	for (my $i=1;$i<=$#$order;$i++) {
                    257: 	    my $foil=$order->[$i];
                    258: 	    if (exists($answer{$foil})) {
                    259: 		if ($i == $correct) {
                    260: 		    $toprow.='<td><b>true</b></td>';
                    261: 		} else {
                    262: 		    $toprow.='<td><i>true</i></td>';
                    263: 		}
                    264: 	    } else {
                    265: 		$toprow.='<td>false</td>';
                    266: 	    }
                    267: 	    $bottomrow.='<td>'.$grayFont.$foil.'</font>&nbsp;</td>';
                    268: 	}
                    269: 	return '<blockquote><table border="1">'.
                    270: 	    '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'.
                    271: 	    '<tr valign="top"><td>'.$grayFont.'Option ID</font></td>'.
                    272: 	    $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';
                    273:     } elsif ($response eq 'essay') {
1.257     albertel  274: 	if (! exists ($env{'form.'.$symb})) {
1.122     ng        275: 	    my (%keyhash) = &Apache::lonnet::dump('nohist_handgrade',
1.257     albertel  276: 						  $env{'course.'.$env{'request.course.id'}.'.domain'},
                    277: 						  $env{'course.'.$env{'request.course.id'}.'.num'});
1.122     ng        278: 
1.257     albertel  279: 	    my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};
                    280: 	    $env{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : '';
                    281: 	    $env{'form.kwclr'}    = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red';
                    282: 	    $env{'form.kwsize'}   = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';
                    283: 	    $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
                    284: 	    $env{'form.'.$symb} = 1; # so that we don't have to read it from disk for multiple sub of the same prob.
1.122     ng        285: 	}
1.166     albertel  286: 	$answer =~ s-\n-<br />-g;
                    287: 	return '<br /><br /><blockquote><tt>'.&keywords_highlight($answer).'</tt></blockquote>';
1.268     albertel  288:     } elsif ( $response eq 'organic') {
                    289: 	my $result='Smile representation: "<tt>'.$answer.'</tt>"';
                    290: 	my $jme=$record->{$version."resource.$partid.$respid.molecule"};
                    291: 	$result.=&Apache::chemresponse::jme_img($jme,$answer,400);
                    292: 	return $result;
1.122     ng        293:     }
1.118     ng        294:     return $answer;
                    295: }
                    296: 
                    297: #-- A couple of common js functions
                    298: sub commonJSfunctions {
                    299:     my $request = shift;
                    300:     $request->print(<<COMMONJSFUNCTIONS);
                    301: <script type="text/javascript" language="javascript">
                    302:     function radioSelection(radioButton) {
                    303: 	var selection=null;
                    304: 	if (radioButton.length > 1) {
                    305: 	    for (var i=0; i<radioButton.length; i++) {
                    306: 		if (radioButton[i].checked) {
                    307: 		    return radioButton[i].value;
                    308: 		}
                    309: 	    }
                    310: 	} else {
                    311: 	    if (radioButton.checked) return radioButton.value;
                    312: 	}
                    313: 	return selection;
                    314:     }
                    315: 
                    316:     function pullDownSelection(selectOne) {
                    317: 	var selection="";
                    318: 	if (selectOne.length > 1) {
                    319: 	    for (var i=0; i<selectOne.length; i++) {
                    320: 		if (selectOne[i].selected) {
                    321: 		    return selectOne[i].value;
                    322: 		}
                    323: 	    }
                    324: 	} else {
1.138     albertel  325:             // only one value it must be the selected one
                    326: 	    return selectOne.value;
1.118     ng        327: 	}
                    328:     }
                    329: </script>
                    330: COMMONJSFUNCTIONS
                    331: }
                    332: 
1.44      ng        333: #--- Dumps the class list with usernames,list of sections,
                    334: #--- section, ids and fullnames for each user.
                    335: sub getclasslist {
1.76      ng        336:     my ($getsec,$filterlist) = @_;
1.121     ng        337:     $getsec = $getsec eq '' ? 'all' : $getsec;
1.56      matthew   338:     my $classlist=&Apache::loncoursedata::get_classlist();
1.49      albertel  339:     # Bail out if we were unable to get the classlist
1.56      matthew   340:     return if (! defined($classlist));
                    341:     #
                    342:     my %sections;
                    343:     my %fullnames;
1.205     matthew   344:     foreach my $student (keys(%$classlist)) {
                    345:         my $end      = 
                    346:             $classlist->{$student}->[&Apache::loncoursedata::CL_END()];
                    347:         my $start    = 
                    348:             $classlist->{$student}->[&Apache::loncoursedata::CL_START()];
                    349:         my $id       = 
                    350:             $classlist->{$student}->[&Apache::loncoursedata::CL_ID()];
                    351:         my $section  = 
                    352:             $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()];
                    353:         my $fullname = 
                    354:             $classlist->{$student}->[&Apache::loncoursedata::CL_FULLNAME()];
                    355:         my $status   = 
                    356:             $classlist->{$student}->[&Apache::loncoursedata::CL_STATUS()];
1.76      ng        357: 	# filter students according to status selected
1.257     albertel  358: 	if ($filterlist && $env{'form.Status'} ne 'Any') {
                    359: 	    if ($env{'form.Status'} ne $status) {
1.205     matthew   360: 		delete ($classlist->{$student});
1.76      ng        361: 		next;
                    362: 	    }
                    363: 	}
1.205     matthew   364: 	$section = ($section ne '' ? $section : 'none');
1.106     albertel  365: 	if (&canview($section)) {
1.103     albertel  366: 	    if ($getsec eq 'all' || $getsec eq $section) {
                    367: 		$sections{$section}++;
1.205     matthew   368: 		$fullnames{$student}=$fullname;
1.103     albertel  369: 	    } else {
1.205     matthew   370: 		delete($classlist->{$student});
1.103     albertel  371: 	    }
                    372: 	} else {
1.205     matthew   373: 	    delete($classlist->{$student});
1.103     albertel  374: 	}
1.44      ng        375:     }
                    376:     my %seen = ();
1.56      matthew   377:     my @sections = sort(keys(%sections));
                    378:     return ($classlist,\@sections,\%fullnames);
1.44      ng        379: }
                    380: 
1.103     albertel  381: sub canmodify {
                    382:     my ($sec)=@_;
                    383:     if ($perm{'mgr'}) {
                    384: 	if (!defined($perm{'mgr_section'})) {
                    385: 	    # can modify whole class
                    386: 	    return 1;
                    387: 	} else {
                    388: 	    if ($sec eq $perm{'mgr_section'}) {
                    389: 		#can modify the requested section
                    390: 		return 1;
                    391: 	    } else {
                    392: 		# can't modify the request section
                    393: 		return 0;
                    394: 	    }
                    395: 	}
                    396:     }
                    397:     #can't modify
                    398:     return 0;
                    399: }
                    400: 
                    401: sub canview {
                    402:     my ($sec)=@_;
                    403:     if ($perm{'vgr'}) {
                    404: 	if (!defined($perm{'vgr_section'})) {
                    405: 	    # can modify whole class
                    406: 	    return 1;
                    407: 	} else {
                    408: 	    if ($sec eq $perm{'vgr_section'}) {
                    409: 		#can modify the requested section
                    410: 		return 1;
                    411: 	    } else {
                    412: 		# can't modify the request section
                    413: 		return 0;
                    414: 	    }
                    415: 	}
                    416:     }
                    417:     #can't modify
                    418:     return 0;
                    419: }
                    420: 
1.44      ng        421: #--- Retrieve the grade status of a student for all the parts
                    422: sub student_gradeStatus {
                    423:     my ($url,$symb,$udom,$uname,$partlist) = @_;
1.257     albertel  424:     my %record     = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);
1.44      ng        425:     my %partstatus = ();
                    426:     foreach (@$partlist) {
1.128     ng        427: 	my ($status,undef)   = split(/_/,$record{"resource.$_.solved"},2);
1.44      ng        428: 	$status              = 'nothing' if ($status eq '');
                    429: 	$partstatus{$_}      = $status;
                    430: 	my $subkey           = "resource.$_.submitted_by";
                    431: 	$partstatus{$subkey} = $record{$subkey} if ($record{$subkey} ne '');
                    432:     }
                    433:     return %partstatus;
                    434: }
                    435: 
1.45      ng        436: # hidden form and javascript that calls the form
                    437: # Use by verifyscript and viewgrades
                    438: # Shows a student's view of problem and submission
                    439: sub jscriptNform {
                    440:     my ($url,$symb) = @_;
                    441:     my $jscript='<script type="text/javascript" language="javascript">'."\n".
                    442: 	'    function viewOneStudent(user,domain) {'."\n".
                    443: 	'	document.onestudent.student.value = user;'."\n".
                    444: 	'	document.onestudent.userdom.value = domain;'."\n".
                    445: 	'	document.onestudent.submit();'."\n".
                    446: 	'    }'."\n".
                    447: 	'</script>'."\n";
                    448:     $jscript.= '<form action="/adm/grades" method="post" name="onestudent">'."\n".
                    449: 	'<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
                    450: 	'<input type="hidden" name="url"     value="'.$url.'" />'."\n".
1.257     albertel  451: 	'<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".
                    452: 	'<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n".
                    453: 	'<input type="hidden" name="Status"  value="'.$env{'form.Status'}.'" />'."\n".
1.45      ng        454: 	'<input type="hidden" name="command" value="submission" />'."\n".
                    455: 	'<input type="hidden" name="student" value="" />'."\n".
                    456: 	'<input type="hidden" name="userdom" value="" />'."\n".
                    457: 	'</form>'."\n";
                    458:     return $jscript;
                    459: }
1.39      ng        460: 
1.44      ng        461: #------------------ End of general use routines --------------------
1.87      www       462: 
                    463: #
                    464: # Find most similar essay
                    465: #
                    466: 
                    467: sub most_similar {
                    468:     my ($uname,$udom,$uessay)=@_;
                    469: 
                    470: # ignore spaces and punctuation
                    471: 
                    472:     $uessay=~s/\W+/ /gs;
                    473: 
                    474: # these will be returned. Do not care if not at least 50 percent similar
1.88      www       475:     my $limit=0.6;
1.87      www       476:     my $sname='';
                    477:     my $sdom='';
                    478:     my $scrsid='';
                    479:     my $sessay='';
                    480: # go through all essays ...
                    481:     foreach my $tkey (keys %oldessays) {
                    482: 	my ($tname,$tdom,$tcrsid)=split(/\./,$tkey);
                    483: # ... except the same student
1.88      www       484:         if (($tname ne $uname) || ($tdom ne $udom)) {
1.87      www       485: 	    my $tessay=$oldessays{$tkey};
                    486:             $tessay=~s/\W+/ /gs;
                    487: # String similarity gives up if not even limit
1.88      www       488:             my $tsimilar=&String::Similarity::similarity($uessay,$tessay,$limit);
1.87      www       489: # Found one
                    490:             if ($tsimilar>$limit) {
                    491: 		$limit=$tsimilar;
                    492:                 $sname=$tname;
1.88      www       493:                 $sdom=$tdom;
1.87      www       494:                 $scrsid=$tcrsid;
                    495:                 $sessay=$oldessays{$tkey};
                    496:             }
                    497:         } 
                    498:     }
1.88      www       499:     if ($limit>0.6) {
1.87      www       500:        return ($sname,$sdom,$scrsid,$sessay,$limit);
                    501:     } else {
                    502:        return ('','','','',0);
                    503:     }
                    504: }
                    505: 
1.44      ng        506: #-------------------------------------------------------------------
                    507: 
                    508: #------------------------------------ Receipt Verification Routines
1.45      ng        509: #
1.44      ng        510: #--- Check whether a receipt number is valid.---
                    511: sub verifyreceipt {
                    512:     my $request  = shift;
                    513: 
1.257     albertel  514:     my $courseid = $env{'request.course.id'};
1.184     www       515:     my $receipt  = &Apache::lonnet::recprefix($courseid).'-'.
1.257     albertel  516: 	$env{'form.receipt'};
1.44      ng        517:     $receipt     =~ s/[^\-\d]//g;
1.257     albertel  518:     my $url      = $env{'form.url'};
                    519:     my $symb     = $env{'form.symb'};
1.44      ng        520:     unless ($symb) {
                    521: 	$symb    = &Apache::lonnet::symbread($url);
                    522:     }
                    523: 
1.45      ng        524:     my $title.='<h3><font color="#339933">Verifying Submission Receipt '.
                    525: 	$receipt.'</h3></font>'."\n".
1.257     albertel  526: 	'<font size=+1><b>Resource: </b>'.$env{'form.probTitle'}.'</font><br><br>'."\n";
1.44      ng        527: 
                    528:     my ($string,$contents,$matches) = ('','',0);
1.56      matthew   529:     my (undef,undef,$fullname) = &getclasslist('all','0');
1.177     albertel  530:     
                    531:     my $receiptparts=0;
1.257     albertel  532:     if ($env{"course.$courseid.receiptalg"} eq 'receipt2') { $receiptparts=1; }
1.177     albertel  533:     my $parts=['0'];
                    534:     if ($receiptparts) { ($parts)=&response_type($url,$symb); }
1.53      albertel  535:     foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
1.44      ng        536: 	my ($uname,$udom)=split(/\:/);
1.177     albertel  537: 	foreach my $part (@$parts) {
                    538: 	    if ($receipt eq &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb,$part)) {
                    539: 		$contents.='<tr bgcolor="#ffffe6"><td>&nbsp;'."\n".
                    540: 		    '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.
                    541: 		    '\')"; TARGET=_self>'.$$fullname{$_}.'</a>&nbsp;</td>'."\n".
                    542: 		    '<td>&nbsp;'.$uname.'&nbsp;</td>'.
                    543: 		    '<td>&nbsp;'.$udom.'&nbsp;</td>';
                    544: 		if ($receiptparts) {
                    545: 		    $contents.='<td>&nbsp;'.$part.'&nbsp;</td>';
                    546: 		}
                    547: 		$contents.='</tr>'."\n";
                    548: 		
                    549: 		$matches++;
                    550: 	    }
1.44      ng        551: 	}
                    552:     }
                    553:     if ($matches == 0) {
                    554: 	$string = $title.'No match found for the above receipt.';
                    555:     } else {
1.45      ng        556: 	$string = &jscriptNform($url,$symb).$title.
1.44      ng        557: 	    'The above receipt matches the following student'.
                    558: 	    ($matches <= 1 ? '.' : 's.')."\n".
                    559: 	    '<table border="0"><tr><td bgcolor="#777777">'."\n".
                    560: 	    '<table border="0"><tr bgcolor="#e6ffff">'."\n".
                    561: 	    '<td><b>&nbsp;Fullname&nbsp;</b></td>'."\n".
                    562: 	    '<td><b>&nbsp;Username&nbsp;</b></td>'."\n".
1.177     albertel  563: 	    '<td><b>&nbsp;Domain&nbsp;</b></td>';
                    564: 	if ($receiptparts) {
                    565: 	    $string.='<td>&nbsp;Problem Part&nbsp;</td>';
                    566: 	}
                    567: 	$string.='</tr>'."\n".$contents.
1.44      ng        568: 	    '</table></td></tr></table>'."\n";
                    569:     }
1.50      albertel  570:     return $string.&show_grading_menu_form($symb,$url);
1.44      ng        571: }
                    572: 
                    573: #--- This is called by a number of programs.
                    574: #--- Called from the Grading Menu - View/Grade an individual student
                    575: #--- Also called directly when one clicks on the subm button 
                    576: #    on the problem page.
1.30      ng        577: sub listStudents {
1.41      ng        578:     my ($request) = shift;
1.49      albertel  579: 
1.72      ng        580:     my ($symb,$url) = &get_symb_and_url($request);
1.257     albertel  581:     my $cdom      = $env{"course.$env{'request.course.id'}.domain"};
                    582:     my $cnum      = $env{"course.$env{'request.course.id'}.num"};
                    583:     my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
                    584:     my $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};
                    585: 
                    586:     my $viewgrade = $env{'form.showgrading'} eq 'yes' ? 'View/Grade/Regrade' : 'View';
                    587:     $env{'form.probTitle'} = $env{'form.probTitle'} eq '' ? 
                    588: 	&Apache::lonnet::gettitle($symb) : $env{'form.probTitle'};
1.49      albertel  589: 
1.118     ng        590:     my $result='<h3><font color="#339933">&nbsp;'.$viewgrade.
                    591: 	' Submissions for a Student or a Group of Students</font></h3>';
                    592: 
1.257     albertel  593:     my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($url,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes'));
1.49      albertel  594: 
1.45      ng        595:     $request->print(<<LISTJAVASCRIPT);
                    596: <script type="text/javascript" language="javascript">
1.110     ng        597:     function checkSelect(checkBox) {
                    598: 	var ctr=0;
                    599: 	var sense="";
                    600: 	if (checkBox.length > 1) {
                    601: 	    for (var i=0; i<checkBox.length; i++) {
                    602: 		if (checkBox[i].checked) {
                    603: 		    ctr++;
                    604: 		}
                    605: 	    }
                    606: 	    sense = "a student or group of students";
                    607: 	} else {
                    608: 	    if (checkBox.checked) {
                    609: 		ctr = 1;
                    610: 	    }
                    611: 	    sense = "the student";
                    612: 	}
                    613: 	if (ctr == 0) {
1.126     ng        614: 	    alert("Please select "+sense+" before clicking on the Next button.");
1.110     ng        615: 	    return false;
                    616: 	}
                    617: 	document.gradesub.submit();
                    618:     }
                    619: 
                    620:     function reLoadList(formname) {
1.112     ng        621: 	if (formname.saveStatusOld.value == pullDownSelection(formname.Status)) {return;}
1.110     ng        622: 	formname.command.value = 'submission';
                    623: 	formname.submit();
                    624:     }
1.45      ng        625: </script>
                    626: LISTJAVASCRIPT
                    627: 
1.118     ng        628:     &commonJSfunctions($request);
1.41      ng        629:     $request->print($result);
1.39      ng        630: 
1.257     albertel  631:     my $checkhdgrade = ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked' : '';
1.119     ng        632:     my $checklastsub = $checkhdgrade eq '' ? 'checked' : '';
1.154     albertel  633:     my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'.
                    634: 	"\n".$table.
1.267     albertel  635: 	'&nbsp;<b>View Problem Text: </b><label><input type="radio" name="vProb" value="no" checked="on" /> no </label>'."\n".
                    636: 	'<label><input type="radio" name="vProb" value="yes" /> one student </label>'."\n".
                    637: 	'<label><input type="radio" name="vProb" value="all" /> all students </label><br />'."\n".
                    638: 	'&nbsp;<b>View Answer: </b><label><input type="radio" name="vAns" value="no"  /> no </label>'."\n".
                    639: 	'<label><input type="radio" name="vAns" value="yes" /> one student </label>'."\n".
                    640: 	'<label><input type="radio" name="vAns" value="all" checked="on" /> all students </label><br />'."\n".
1.49      albertel  641: 	'&nbsp;<b>Submissions: </b>'."\n";
1.257     albertel  642:     if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) {
1.267     albertel  643: 	$gradeTable.='<label><input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> essay part only </label>'."\n";
1.49      albertel  644:     }
1.110     ng        645: 
1.257     albertel  646:     my $saveStatus = $env{'form.Status'} eq '' ? 'Active' : $env{'form.Status'};
                    647:     $env{'form.Status'} = $saveStatus;
1.110     ng        648: 
1.267     albertel  649:     $gradeTable.='<label><input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> last submission only </label>'."\n".
                    650: 	'<label><input type="radio" name="lastSub" value="last" /> last submission & parts info </label>'."\n".
                    651: 	'<label><input type="radio" name="lastSub" value="datesub" /> by dates and submissions </label>'."\n".
                    652: 	'<label><input type="radio" name="lastSub" value="all" /> all details</label>'."\n".
1.45      ng        653: 	'<input type="hidden" name="section"     value="'.$getsec.'" />'."\n".
                    654: 	'<input type="hidden" name="submitonly"  value="'.$submitonly.'" />'."\n".
1.257     albertel  655: 	'<input type="hidden" name="handgrade"   value="'.$env{'form.handgrade'}.'" /><br />'."\n".
                    656: 	'<input type="hidden" name="showgrading" value="'.$env{'form.showgrading'}.'" /><br />'."\n".
                    657: 	'<input type="hidden" name="saveState"   value="'.$env{'form.saveState'}.'" />'."\n".
                    658: 	'<input type="hidden" name="probTitle"   value="'.$env{'form.probTitle'}.'" />'."\n".
1.48      albertel  659: 	'<input type="hidden" name="url"  value="'.$url.'" />'."\n".
                    660: 	'<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
1.110     ng        661: 	'<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n";
                    662: 
1.257     albertel  663:     if (exists($env{'form.gradingMenu'}) && exists($env{'form.Status'})) {
                    664: 	$gradeTable.='<input type="hidden" name="Status"   value="'.$env{'form.Status'}.'" />'."\n";
1.124     ng        665:     } else {
                    666: 	$gradeTable.='<b>Student Status:</b> '.
                    667: 	    &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);').'<br />';
                    668:     }
1.112     ng        669: 
1.126     ng        670:     $gradeTable.='To '.lc($viewgrade).' a submission or a group of submissions, click on the check box(es) '.
                    671: 	'next to the student\'s name(s). Then click on the Next button.<br />'."\n".
1.110     ng        672: 	'<input type="hidden" name="command" value="processGroup" />'."\n";
1.249     albertel  673: 
                    674: # checkall buttons
                    675:     $gradeTable.=&check_script('gradesub', 'stuinfo');
1.110     ng        676:     $gradeTable.='<input type="button" '."\n".
1.45      ng        677: 	'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".
1.249     albertel  678: 	'value="Next->" /> <br />'."\n";
                    679:     $gradeTable.=&check_buttons();
1.267     albertel  680:     $gradeTable.='<label><input type="checkbox" name="checkPlag" checked="on" />Check For Plagiarism</label>';
1.249     albertel  681:     my ($classlist, undef, $fullname) = &getclasslist($getsec,'1');
1.45      ng        682:     $gradeTable.='<table border="0"><tr><td bgcolor="#777777">'.
1.110     ng        683: 	'<table border="0"><tr bgcolor="#e6ffff">';
                    684:     my $loop = 0;
                    685:     while ($loop < 2) {
1.126     ng        686: 	$gradeTable.='<td><b>&nbsp;No.</b>&nbsp;</td><td><b>&nbsp;Select&nbsp;</b></td>'.
1.250     albertel  687: 	    '<td>'.&nameUserString('header').'&nbsp;Section/Group</td>';
1.257     albertel  688: 	if ($env{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {
1.110     ng        689: 	    foreach (sort(@$partlist)) {
1.207     albertel  690: 		my $display_part=&get_display_part((split(/_/))[0],$url,$symb);
                    691: 		$gradeTable.='<td><b>&nbsp;Part: '.$display_part.
                    692: 		    ' Status&nbsp;</b></td>';
1.110     ng        693: 	    }
                    694: 	}
                    695: 	$loop++;
1.126     ng        696: #	$gradeTable.='<td></td>' if ($loop%2 ==1);
1.41      ng        697:     }
1.45      ng        698:     $gradeTable.='</tr>'."\n";
1.41      ng        699: 
1.45      ng        700:     my $ctr = 0;
1.53      albertel  701:     foreach my $student (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
1.41      ng        702: 	my ($uname,$udom) = split(/:/,$student);
1.110     ng        703: 	my %status = ();
1.257     albertel  704: 	if ($env{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {
1.110     ng        705: 	    (%status) =&student_gradeStatus($url,$symb,$udom,$uname,$partlist);
1.145     albertel  706: 	    my $submitted = 0;
1.164     albertel  707: 	    my $graded = 0;
1.248     albertel  708: 	    my $incorrect = 0;
1.110     ng        709: 	    foreach (keys(%status)) {
1.145     albertel  710: 		$submitted = 1 if ($status{$_} ne 'nothing');
1.248     albertel  711: 		$graded = 1 if ($status{$_} =~ /^ungraded/);
                    712: 		$incorrect = 1 if ($status{$_} =~ /^incorrect/);
                    713: 		
1.110     ng        714: 		my ($foo,$partid,$foo1) = split(/\./,$_);
                    715: 		if ($status{'resource.'.$partid.'.submitted_by'} ne '') {
1.145     albertel  716: 		    $submitted = 0;
1.150     albertel  717: 		    my ($part)=split(/\./,$partid);
1.110     ng        718: 		    $gradeTable.='<input type="hidden" name="'.
1.150     albertel  719: 			$student.':'.$part.':submitted_by" value="'.
1.110     ng        720: 			$status{'resource.'.$partid.'.submitted_by'}.'" />';
                    721: 		}
1.41      ng        722: 	    }
1.248     albertel  723: 	    
1.156     albertel  724: 	    next if (!$submitted && ($submitonly eq 'yes' ||
                    725: 				     $submitonly eq 'incorrect' ||
                    726: 				     $submitonly eq 'graded'));
1.248     albertel  727: 	    next if (!$graded && ($submitonly eq 'graded'));
                    728: 	    next if (!$incorrect && $submitonly eq 'incorrect');
1.41      ng        729: 	}
1.34      ng        730: 
1.45      ng        731: 	$ctr++;
1.249     albertel  732: 	my $section = $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()];
                    733: 
1.104     albertel  734: 	if ( $perm{'vgr'} eq 'F' ) {
1.110     ng        735: 	    $gradeTable.='<tr bgcolor="#ffffe6">' if ($ctr%2 ==1);
1.126     ng        736: 	    $gradeTable.='<td align="right">'.$ctr.'&nbsp;</td>'.
1.249     albertel  737:                '<td align="center"><label><input type=checkbox name="stuinfo" value="'.
                    738:                $student.':'.$$fullname{$student}.':::SECTION'.$section.
                    739: 	       ')&nbsp;" />&nbsp;&nbsp;</label></td>'."\n".'<td>'.
                    740: 	       &nameUserString(undef,$$fullname{$student},$uname,$udom).
                    741: 	       '&nbsp;'.$section.'</td>'."\n";
1.110     ng        742: 
1.257     albertel  743: 	    if ($env{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {
1.110     ng        744: 		foreach (sort keys(%status)) {
                    745: 		    next if (/^resource.*?submitted_by$/);
                    746: 		    $gradeTable.='<td align="middle">&nbsp;'.$status{$_}.'&nbsp;</td>'."\n";
                    747: 		}
1.41      ng        748: 	    }
1.126     ng        749: #	    $gradeTable.='<td></td>' if ($ctr%2 ==1);
1.110     ng        750: 	    $gradeTable.='</tr>'."\n" if ($ctr%2 ==0);
1.41      ng        751: 	}
                    752:     }
1.110     ng        753:     if ($ctr%2 ==1) {
1.126     ng        754: 	$gradeTable.='<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>';
1.257     albertel  755: 	    if ($env{'form.showgrading'} eq 'yes' && $submitonly ne 'all') {
1.110     ng        756: 		foreach (@$partlist) {
                    757: 		    $gradeTable.='<td>&nbsp;</td>';
                    758: 		}
                    759: 	    }
                    760: 	$gradeTable.='</tr>';
                    761:     }
                    762: 
1.249     albertel  763:     $gradeTable.='</table></td></tr></table>'."\n".
1.45      ng        764: 	'<input type="button" '.
                    765: 	'onClick="javascript:checkSelect(this.form.stuinfo);" '.
1.126     ng        766: 	'value="Next->" /></form>'."\n";
1.45      ng        767:     if ($ctr == 0) {
1.96      albertel  768: 	my $num_students=(scalar(keys(%$fullname)));
                    769: 	if ($num_students eq 0) {
                    770: 	    $gradeTable='<br />&nbsp;<font color="red">There are no students currently enrolled.</font>';
                    771: 	} else {
1.171     albertel  772: 	    my $submissions='submissions';
                    773: 	    if ($submitonly eq 'incorrect') { $submissions = 'incorrect submissions'; }
                    774: 	    if ($submitonly eq 'graded'   ) { $submissions = 'ungraded submissions'; }
1.96      albertel  775: 	    $gradeTable='<br />&nbsp;<font color="red">'.
1.171     albertel  776: 		'No '.$submissions.' found for this resource for any students. ('.$num_students.
                    777: 		' students checked for '.$submissions.')</font><br />';
1.96      albertel  778: 	}
1.46      ng        779:     } elsif ($ctr == 1) {
                    780: 	$gradeTable =~ s/type=checkbox/type=checkbox checked/;
1.45      ng        781:     }
1.50      albertel  782:     $gradeTable.=&show_grading_menu_form($symb,$url);
1.45      ng        783:     $request->print($gradeTable);
1.44      ng        784:     return '';
1.10      ng        785: }
                    786: 
1.44      ng        787: #---- Called from the listStudents routine
1.249     albertel  788: 
                    789: sub check_script {
                    790:     my ($form, $type)=@_;
                    791:     my $chkallscript='<script type="text/javascript">
                    792:     function checkall() {
                    793:         for (i=0; i<document.forms.'.$form.'.elements.length; i++) {
                    794:             ele = document.forms.'.$form.'.elements[i];
                    795:             if (ele.name == "'.$type.'") {
                    796:             document.forms.'.$form.'.elements[i].checked=true;
                    797:                                        }
                    798:         }
                    799:     }
                    800: 
                    801:     function checksec() {
                    802:         for (i=0; i<document.forms.'.$form.'.elements.length; i++) {
                    803:             ele = document.forms.'.$form.'.elements[i];
                    804:            string = document.forms.'.$form.'.chksec.value;
                    805:            if
                    806:           (ele.value.indexOf(":::SECTION"+string)>0) {
                    807:               document.forms.'.$form.'.elements[i].checked=true;
                    808:             }
                    809:         }
                    810:     }
                    811: 
                    812: 
                    813:     function uncheckall() {
                    814:         for (i=0; i<document.forms.'.$form.'.elements.length; i++) {
                    815:             ele = document.forms.'.$form.'.elements[i];
                    816:             if (ele.name == "'.$type.'") {
                    817:             document.forms.'.$form.'.elements[i].checked=false;
                    818:                                        }
                    819:         }
                    820:     }
                    821: 
                    822: </script>'."\n";
                    823:     return $chkallscript;
                    824: }
                    825: 
                    826: sub check_buttons {
                    827:     my $buttons.='<input type="button" onclick="checkall()" value="Check All" />';
                    828:     $buttons.='<input type="button" onclick="uncheckall()" value="Uncheck All" />&nbsp;';
                    829:     $buttons.='<input type="button" onclick="checksec()" value="Check Section/Group" />';
                    830:     $buttons.='<input type="text" size="5" name="chksec" />&nbsp;';
                    831:     return $buttons;
                    832: }
                    833: 
1.44      ng        834: #     Displays the submissions for one student or a group of students
1.34      ng        835: sub processGroup {
1.41      ng        836:     my ($request)  = shift;
                    837:     my $ctr        = 0;
1.155     albertel  838:     my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo');
1.41      ng        839:     my $total      = scalar(@stuchecked)-1;
1.45      ng        840: 
1.41      ng        841:     foreach (@stuchecked) {
                    842: 	my ($uname,$udom,$fullname) = split(/:/);
1.257     albertel  843: 	$env{'form.student'}        = $uname;
                    844: 	$env{'form.userdom'}        = $udom;
                    845: 	$env{'form.fullname'}       = $fullname;
1.41      ng        846: 	&submission($request,$ctr,$total);
                    847: 	$ctr++;
                    848:     }
                    849:     return '';
1.35      ng        850: }
1.34      ng        851: 
1.44      ng        852: #------------------------------------------------------------------------------------
                    853: #
                    854: #-------------------------- Next few routines handles grading by student, essentially
                    855: #                           handles essay response type problem/part
                    856: #
                    857: #--- Javascript to handle the submission page functionality ---
                    858: sub sub_page_js {
                    859:     my $request = shift;
                    860:     $request->print(<<SUBJAVASCRIPT);
                    861: <script type="text/javascript" language="javascript">
1.71      ng        862:     function updateRadio(formname,id,weight) {
1.125     ng        863: 	var gradeBox = formname["GD_BOX"+id];
                    864: 	var radioButton = formname["RADVAL"+id];
                    865: 	var oldpts = formname["oldpts"+id].value;
1.72      ng        866: 	var pts = checkSolved(formname,id) == 'update' ? gradeBox.value : oldpts;
1.71      ng        867: 	gradeBox.value = pts;
                    868: 	var resetbox = false;
                    869: 	if (isNaN(pts) || pts < 0) {
                    870: 	    alert("A number equal or greater than 0 is expected. Entered value = "+pts);
                    871: 	    for (var i=0; i<radioButton.length; i++) {
                    872: 		if (radioButton[i].checked) {
                    873: 		    gradeBox.value = i;
                    874: 		    resetbox = true;
                    875: 		}
                    876: 	    }
                    877: 	    if (!resetbox) {
                    878: 		formtextbox.value = "";
                    879: 	    }
                    880: 	    return;
1.44      ng        881: 	}
1.71      ng        882: 
                    883: 	if (pts > weight) {
                    884: 	    var resp = confirm("You entered a value ("+pts+
                    885: 			       ") greater than the weight for the part. Accept?");
                    886: 	    if (resp == false) {
1.125     ng        887: 		gradeBox.value = oldpts;
1.71      ng        888: 		return;
                    889: 	    }
1.44      ng        890: 	}
1.13      albertel  891: 
1.71      ng        892: 	for (var i=0; i<radioButton.length; i++) {
                    893: 	    radioButton[i].checked=false;
                    894: 	    if (pts == i && pts != "") {
                    895: 		radioButton[i].checked=true;
                    896: 	    }
                    897: 	}
                    898: 	updateSelect(formname,id);
1.125     ng        899: 	formname["stores"+id].value = "0";
1.41      ng        900:     }
1.5       albertel  901: 
1.72      ng        902:     function writeBox(formname,id,pts) {
1.125     ng        903: 	var gradeBox = formname["GD_BOX"+id];
1.71      ng        904: 	if (checkSolved(formname,id) == 'update') {
                    905: 	    gradeBox.value = pts;
                    906: 	} else {
1.125     ng        907: 	    var oldpts = formname["oldpts"+id].value;
1.72      ng        908: 	    gradeBox.value = oldpts;
1.125     ng        909: 	    var radioButton = formname["RADVAL"+id];
1.71      ng        910: 	    for (var i=0; i<radioButton.length; i++) {
                    911: 		radioButton[i].checked=false;
1.72      ng        912: 		if (i == oldpts) {
1.71      ng        913: 		    radioButton[i].checked=true;
                    914: 		}
                    915: 	    }
1.41      ng        916: 	}
1.125     ng        917: 	formname["stores"+id].value = "0";
1.71      ng        918: 	updateSelect(formname,id);
                    919: 	return;
1.41      ng        920:     }
1.44      ng        921: 
1.71      ng        922:     function clearRadBox(formname,id) {
                    923: 	if (checkSolved(formname,id) == 'noupdate') {
                    924: 	    updateSelect(formname,id);
                    925: 	    return;
                    926: 	}
1.125     ng        927: 	gradeSelect = formname["GD_SEL"+id];
1.71      ng        928: 	for (var i=0; i<gradeSelect.length; i++) {
                    929: 	    if (gradeSelect[i].selected) {
                    930: 		var selectx=i;
                    931: 	    }
                    932: 	}
1.125     ng        933: 	var stores = formname["stores"+id];
1.71      ng        934: 	if (selectx == stores.value) { return };
1.125     ng        935: 	var gradeBox = formname["GD_BOX"+id];
1.71      ng        936: 	gradeBox.value = "";
1.125     ng        937: 	var radioButton = formname["RADVAL"+id];
1.71      ng        938: 	for (var i=0; i<radioButton.length; i++) {
                    939: 	    radioButton[i].checked=false;
                    940: 	}
                    941: 	stores.value = selectx;
                    942:     }
1.5       albertel  943: 
1.71      ng        944:     function checkSolved(formname,id) {
1.125     ng        945: 	if (formname["solved"+id].value == "correct_by_student" && formname.overRideScore.value == 'no') {
1.118     ng        946: 	    var reply = confirm("This problem has been graded correct by the computer. Do you want to change the score?");
                    947: 	    if (!reply) {return "noupdate";}
1.120     ng        948: 	    formname.overRideScore.value = 'yes';
1.41      ng        949: 	}
1.71      ng        950: 	return "update";
1.13      albertel  951:     }
1.71      ng        952: 
                    953:     function updateSelect(formname,id) {
1.125     ng        954: 	formname["GD_SEL"+id][0].selected = true;
1.71      ng        955: 	return;
1.41      ng        956:     }
1.33      ng        957: 
1.121     ng        958: //=========== Check that a point is assigned for all the parts  ============
1.71      ng        959:     function checksubmit(formname,val,total,parttot) {
1.121     ng        960: 	formname.gradeOpt.value = val;
1.71      ng        961: 	if (val == "Save & Next") {
                    962: 	    for (i=0;i<=total;i++) {
                    963: 		for (j=0;j<parttot;j++) {
1.125     ng        964: 		    var partid = formname["partid"+i+"_"+j].value;
1.127     ng        965: 		    if (formname["GD_SEL"+i+"_"+partid][0].selected) {
1.125     ng        966: 			var points = formname["GD_BOX"+i+"_"+partid].value;
1.71      ng        967: 			if (points == "") {
1.125     ng        968: 			    var name = formname["name"+i].value;
1.129     ng        969: 			    var studentID = (name != '' ? name : formname["unamedom"+i].value);
                    970: 			    var resp = confirm("You did not assign a score for "+studentID+
                    971: 					       ", part "+partid+". Continue?");
1.71      ng        972: 			    if (resp == false) {
1.125     ng        973: 				formname["GD_BOX"+i+"_"+partid].focus();
1.71      ng        974: 				return false;
                    975: 			    }
                    976: 			}
                    977: 		    }
                    978: 		    
                    979: 		}
                    980: 	    }
                    981: 	    
                    982: 	}
1.121     ng        983: 	if (val == "Grade Student") {
                    984: 	    formname.showgrading.value = "yes";
                    985: 	    if (formname.Status.value == "") {
                    986: 		formname.Status.value = "Active";
                    987: 	    }
                    988: 	    formname.studentNo.value = total;
                    989: 	}
1.120     ng        990: 	formname.submit();
                    991:     }
                    992: 
1.71      ng        993: //======= Check that a score is assigned for all the problems (page/sequence grading only) =========
                    994:     function checkSubmitPage(formname,total) {
                    995: 	noscore = new Array(100);
                    996: 	var ptr = 0;
                    997: 	for (i=1;i<total;i++) {
1.125     ng        998: 	    var partid = formname["q_"+i].value;
1.127     ng        999: 	    if (formname["GD_SEL"+i+"_"+partid][0].selected) {
1.125     ng       1000: 		var points = formname["GD_BOX"+i+"_"+partid].value;
                   1001: 		var status = formname["solved"+i+"_"+partid].value;
1.71      ng       1002: 		if (points == "" && status != "correct_by_student") {
                   1003: 		    noscore[ptr] = i;
                   1004: 		    ptr++;
                   1005: 		}
                   1006: 	    }
                   1007: 	}
                   1008: 	if (ptr != 0) {
                   1009: 	    var sense = ptr == 1 ? ": " : "s: ";
                   1010: 	    var prolist = "";
                   1011: 	    if (ptr == 1) {
                   1012: 		prolist = noscore[0];
                   1013: 	    } else {
                   1014: 		var i = 0;
                   1015: 		while (i < ptr-1) {
                   1016: 		    prolist += noscore[i]+", ";
                   1017: 		    i++;
                   1018: 		}
                   1019: 		prolist += "and "+noscore[i];
                   1020: 	    }
                   1021: 	    var resp = confirm("You did not assign any score for the following problem"+sense+prolist+". Continue?");
                   1022: 	    if (resp == false) {
                   1023: 		return false;
                   1024: 	    }
                   1025: 	}
1.45      ng       1026: 
1.71      ng       1027: 	formname.submit();
                   1028:     }
                   1029: </script>
                   1030: SUBJAVASCRIPT
                   1031: }
1.45      ng       1032: 
1.71      ng       1033: #--- javascript for essay type problem --
                   1034: sub sub_page_kw_js {
                   1035:     my $request = shift;
1.80      ng       1036:     my $iconpath = $request->dir_config('lonIconsURL');
1.118     ng       1037:     &commonJSfunctions($request);
1.219     www      1038:     my $docopen=&Apache::lonhtmlcommon::javascript_docopen();
1.236     albertel 1039:     $docopen=~s/^document\.//;
1.71      ng       1040:     $request->print(<<SUBJAVASCRIPT);
                   1041: <script type="text/javascript" language="javascript">
1.45      ng       1042: 
1.44      ng       1043: //===================== Show list of keywords ====================
1.122     ng       1044:   function keywords(formname) {
                   1045:     var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",formname.keywords.value);
1.44      ng       1046:     if (nret==null) return;
1.122     ng       1047:     formname.keywords.value = nret;
1.44      ng       1048: 
1.122     ng       1049:     if (formname.keywords.value != "") {
1.128     ng       1050: 	formname.refresh.value = "on";
1.122     ng       1051: 	formname.submit();
1.44      ng       1052:     }
                   1053:     return;
                   1054:   }
                   1055: 
                   1056: //===================== Script to view submitted by ==================
                   1057:   function viewSubmitter(submitter) {
                   1058:     document.SCORE.refresh.value = "on";
                   1059:     document.SCORE.NCT.value = "1";
                   1060:     document.SCORE.unamedom0.value = submitter;
                   1061:     document.SCORE.submit();
                   1062:     return;
                   1063:   }
                   1064: 
                   1065: //===================== Script to add keyword(s) ==================
                   1066:   function getSel() {
                   1067:     if (document.getSelection) txt = document.getSelection();
                   1068:     else if (document.selection) txt = document.selection.createRange().text;
                   1069:     else return;
                   1070:     var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," ");
                   1071:     if (cleantxt=="") {
1.46      ng       1072: 	alert("Please select a word or group of words from document and then click this link.");
1.44      ng       1073: 	return;
                   1074:     }
                   1075:     var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt);
                   1076:     if (nret==null) return;
1.127     ng       1077:     document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret;
1.44      ng       1078:     if (document.SCORE.keywords.value != "") {
1.127     ng       1079: 	document.SCORE.refresh.value = "on";
1.44      ng       1080: 	document.SCORE.submit();
                   1081:     }
                   1082:     return;
                   1083:   }
                   1084: 
                   1085: //====================== Script for composing message ==============
1.80      ng       1086:    // preload images
                   1087:    img1 = new Image();
                   1088:    img1.src = "$iconpath/mailbkgrd.gif";
                   1089:    img2 = new Image();
                   1090:    img2.src = "$iconpath/mailto.gif";
                   1091: 
1.44      ng       1092:   function msgCenter(msgform,usrctr,fullname) {
                   1093:     var Nmsg  = msgform.savemsgN.value;
                   1094:     savedMsgHeader(Nmsg,usrctr,fullname);
                   1095:     var subject = msgform.msgsub.value;
1.127     ng       1096:     var msgchk = document.SCORE["includemsg"+usrctr].value;
1.44      ng       1097:     re = /msgsub/;
                   1098:     var shwsel = "";
                   1099:     if (re.test(msgchk)) { shwsel = "checked" }
1.123     ng       1100:     subject = (document.SCORE.shownSub.value == 0 ? checkEntities(subject) : subject);
                   1101:     displaySubject(checkEntities(subject),shwsel);
1.44      ng       1102:     for (var i=1; i<=Nmsg; i++) {
1.123     ng       1103: 	var testmsg = "savemsg"+i+",";
                   1104: 	re = new RegExp(testmsg,"g");
1.44      ng       1105: 	shwsel = "";
                   1106: 	if (re.test(msgchk)) { shwsel = "checked" }
1.125     ng       1107: 	var message = document.SCORE["savemsg"+i].value;
1.126     ng       1108: 	message = (document.SCORE["shownOnce"+i].value == 0 ? checkEntities(message) : message);
1.123     ng       1109: 	displaySavedMsg(i,message,shwsel); //I do not get it. w/o checkEntities on saved messages,
                   1110: 	                                   //any &lt; is already converted to <, etc. However, only once!!
1.44      ng       1111:     }
1.125     ng       1112:     newmsg = document.SCORE["newmsg"+usrctr].value;
1.44      ng       1113:     shwsel = "";
                   1114:     re = /newmsg/;
                   1115:     if (re.test(msgchk)) { shwsel = "checked" }
                   1116:     newMsg(newmsg,shwsel);
                   1117:     msgTail(); 
                   1118:     return;
                   1119:   }
                   1120: 
1.123     ng       1121:   function checkEntities(strx) {
                   1122:     if (strx.length == 0) return strx;
                   1123:     var orgStr = ["&", "<", ">", '"']; 
                   1124:     var newStr = ["&amp;", "&lt;", "&gt;", "&quot;"];
                   1125:     var counter = 0;
                   1126:     while (counter < 4) {
                   1127: 	strx = strReplace(strx,orgStr[counter],newStr[counter]);
                   1128: 	counter++;
                   1129:     }
                   1130:     return strx;
                   1131:   }
                   1132: 
                   1133:   function strReplace(strx, orgStr, newStr) {
                   1134:     return strx.split(orgStr).join(newStr);
                   1135:   }
                   1136: 
1.44      ng       1137:   function savedMsgHeader(Nmsg,usrctr,fullname) {
1.76      ng       1138:     var height = 70*Nmsg+250;
1.44      ng       1139:     var scrollbar = "no";
                   1140:     if (height > 600) {
                   1141: 	height = 600;
                   1142: 	scrollbar = "yes";
                   1143:     }
1.118     ng       1144:     var xpos = (screen.width-600)/2;
                   1145:     xpos = (xpos < 0) ? '0' : xpos;
                   1146:     var ypos = (screen.height-height)/2-30;
                   1147:     ypos = (ypos < 0) ? '0' : ypos;
                   1148: 
1.206     albertel 1149:     pWin = window.open('', 'MessageCenter', 'resizable=yes,toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height);
1.76      ng       1150:     pWin.focus();
                   1151:     pDoc = pWin.document;
1.219     www      1152:     pDoc.$docopen;
1.76      ng       1153:     pDoc.write("<html><head>");
                   1154:     pDoc.write("<title>Message Central</title>");
                   1155: 
                   1156:     pDoc.write("<script language=javascript>");
                   1157:     pDoc.write("function checkInput() {");
1.123     ng       1158:     pDoc.write("  opener.document.SCORE.msgsub.value = opener.checkEntities(document.msgcenter.msgsub.value);");
1.76      ng       1159:     pDoc.write("  var nmsg   = opener.document.SCORE.savemsgN.value;");
                   1160:     pDoc.write("  var usrctr = document.msgcenter.usrctr.value;");
1.125     ng       1161:     pDoc.write("  var newval = opener.document.SCORE[\\"newmsg\\"+usrctr];");
1.123     ng       1162:     pDoc.write("  newval.value = opener.checkEntities(document.msgcenter.newmsg.value);");
1.76      ng       1163: 
                   1164:     pDoc.write("  var msgchk = \\"\\";");
                   1165:     pDoc.write("  if (document.msgcenter.subchk.checked) {");
                   1166:     pDoc.write("     msgchk = \\"msgsub,\\";");
                   1167:     pDoc.write("  }");
1.80      ng       1168:     pDoc.write("  var includemsg = 0;");
                   1169:     pDoc.write("  for (var i=1; i<=nmsg; i++) {");
1.125     ng       1170:     pDoc.write("      var opnmsg = opener.document.SCORE[\\"savemsg\\"+i];");
                   1171:     pDoc.write("      var frmmsg = document.msgcenter[\\"msg\\"+i];");
1.123     ng       1172:     pDoc.write("      opnmsg.value = opener.checkEntities(frmmsg.value);");
1.125     ng       1173:     pDoc.write("      var showflg = opener.document.SCORE[\\"shownOnce\\"+i];");
1.123     ng       1174:     pDoc.write("      showflg.value = \\"1\\";");
1.125     ng       1175:     pDoc.write("      var chkbox = document.msgcenter[\\"msgn\\"+i];");
1.76      ng       1176:     pDoc.write("      if (chkbox.checked) {");
                   1177:     pDoc.write("         msgchk += \\"savemsg\\"+i+\\",\\";");
1.80      ng       1178:     pDoc.write("         includemsg = 1;");
1.76      ng       1179:     pDoc.write("      }");
                   1180:     pDoc.write("  }");
                   1181:     pDoc.write("  if (document.msgcenter.newmsgchk.checked) {");
                   1182:     pDoc.write("     msgchk += \\"newmsg\\"+usrctr;");
1.80      ng       1183:     pDoc.write("     includemsg = 1;");
                   1184:     pDoc.write("  }");
1.125     ng       1185:     pDoc.write("  imgformname = opener.document.SCORE[\\"mailicon\\"+usrctr];");
1.84      ng       1186:     pDoc.write("  imgformname.src = \\"$iconpath/\\"+((includemsg) ? \\"mailto.gif\\" : \\"mailbkgrd.gif\\");");
1.125     ng       1187:     pDoc.write("  var includemsg = opener.document.SCORE[\\"includemsg\\"+usrctr];");
1.76      ng       1188:     pDoc.write("  includemsg.value = msgchk;");
                   1189: 
                   1190:     pDoc.write("  self.close()");
                   1191: 
                   1192:     pDoc.write("}");
                   1193: 
                   1194:     pDoc.write("<");
                   1195:     pDoc.write("/script>");
                   1196: 
                   1197:     pDoc.write("</head><body bgcolor=white>");
                   1198: 
                   1199:     pDoc.write("<form action=\\"inactive\\" name=\\"msgcenter\\">");
                   1200:     pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">");
                   1201:     pDoc.write("<font color=\\"green\\" size=+1>&nbsp;Compose Message for \"+fullname+\"</font><br><br>");
                   1202: 
                   1203:     pDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
                   1204:     pDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
                   1205:     pDoc.write("<td><b>Type</b></td><td><b>Include</b></td><td><b>Message</td></tr>");
1.44      ng       1206: }
                   1207:     function displaySubject(msg,shwsel) {
1.76      ng       1208:     pDoc = pWin.document;
                   1209:     pDoc.write("<tr bgcolor=\\"#ffffdd\\">");
                   1210:     pDoc.write("<td>Subject</td>");
                   1211:     pDoc.write("<td align=\\"center\\"><input name=\\"subchk\\" type=\\"checkbox\\"" +shwsel+"></td>");
                   1212:     pDoc.write("<td><input name=\\"msgsub\\" type=\\"text\\" value=\\""+msg+"\\"size=\\"60\\" maxlength=\\"80\\"></td></tr>");
1.44      ng       1213: }
                   1214: 
1.72      ng       1215:   function displaySavedMsg(ctr,msg,shwsel) {
1.76      ng       1216:     pDoc = pWin.document;
                   1217:     pDoc.write("<tr bgcolor=\\"#ffffdd\\">");
                   1218:     pDoc.write("<td align=\\"center\\">"+ctr+"</td>");
                   1219:     pDoc.write("<td align=\\"center\\"><input name=\\"msgn"+ctr+"\\" type=\\"checkbox\\"" +shwsel+"></td>");
                   1220:     pDoc.write("<td><textarea name=\\"msg"+ctr+"\\" cols=\\"60\\" rows=\\"3\\">"+msg+"</textarea></td></tr>");
1.44      ng       1221: }
                   1222: 
                   1223:   function newMsg(newmsg,shwsel) {
1.76      ng       1224:     pDoc = pWin.document;
                   1225:     pDoc.write("<tr bgcolor=\\"#ffffdd\\">");
                   1226:     pDoc.write("<td align=\\"center\\">New</td>");
                   1227:     pDoc.write("<td align=\\"center\\"><input name=\\"newmsgchk\\" type=\\"checkbox\\"" +shwsel+"></td>");
                   1228:     pDoc.write("<td><textarea name=\\"newmsg\\" cols=\\"60\\" rows=\\"3\\" onchange=\\"javascript:this.form.newmsgchk.checked=true\\" >"+newmsg+"</textarea></td></tr>");
1.44      ng       1229: }
                   1230: 
                   1231:   function msgTail() {
1.76      ng       1232:     pDoc = pWin.document;
                   1233:     pDoc.write("</table>");
                   1234:     pDoc.write("</td></tr></table>&nbsp;");
                   1235:     pDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");
                   1236:     pDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br><br>");
                   1237:     pDoc.write("</form>");
                   1238:     pDoc.write("</body></html>");
1.128     ng       1239:     pDoc.close();
1.44      ng       1240: }
                   1241: 
                   1242: //====================== Script for keyword highlight options ==============
                   1243:   function kwhighlight() {
                   1244:     var kwclr    = document.SCORE.kwclr.value;
                   1245:     var kwsize   = document.SCORE.kwsize.value;
                   1246:     var kwstyle  = document.SCORE.kwstyle.value;
                   1247:     var redsel = "";
                   1248:     var grnsel = "";
                   1249:     var blusel = "";
                   1250:     if (kwclr=="red")   {var redsel="checked"};
                   1251:     if (kwclr=="green") {var grnsel="checked"};
                   1252:     if (kwclr=="blue")  {var blusel="checked"};
                   1253:     var sznsel = "";
                   1254:     var sz1sel = "";
                   1255:     var sz2sel = "";
                   1256:     if (kwsize=="0")  {var sznsel="checked"};
                   1257:     if (kwsize=="+1") {var sz1sel="checked"};
                   1258:     if (kwsize=="+2") {var sz2sel="checked"};
                   1259:     var synsel = "";
                   1260:     var syisel = "";
                   1261:     var sybsel = "";
                   1262:     if (kwstyle=="")    {var synsel="checked"};
                   1263:     if (kwstyle=="<i>") {var syisel="checked"};
                   1264:     if (kwstyle=="<b>") {var sybsel="checked"};
                   1265:     highlightCentral();
                   1266:     highlightbody('red','red',redsel,'0','normal',sznsel,'','normal',synsel);
                   1267:     highlightbody('green','green',grnsel,'+1','+1',sz1sel,'<i>','italic',syisel);
                   1268:     highlightbody('blue','blue',blusel,'+2','+2',sz2sel,'<b>','bold',sybsel);
                   1269:     highlightend();
                   1270:     return;
                   1271:   }
                   1272: 
                   1273:   function highlightCentral() {
1.76      ng       1274: //    if (window.hwdWin) window.hwdWin.close();
1.118     ng       1275:     var xpos = (screen.width-400)/2;
                   1276:     xpos = (xpos < 0) ? '0' : xpos;
                   1277:     var ypos = (screen.height-330)/2-30;
                   1278:     ypos = (ypos < 0) ? '0' : ypos;
                   1279: 
1.206     albertel 1280:     hwdWin = window.open('', 'KeywordHighlightCentral', 'resizeable=yes,toolbar=no,location=no,scrollbars=no,width=400,height=300,screenx='+xpos+',screeny='+ypos);
1.76      ng       1281:     hwdWin.focus();
                   1282:     var hDoc = hwdWin.document;
1.219     www      1283:     hDoc.$docopen;
1.76      ng       1284:     hDoc.write("<html><head>");
                   1285:     hDoc.write("<title>Highlight Central</title>");
                   1286: 
                   1287:     hDoc.write("<script language=javascript>");
                   1288:     hDoc.write("function updateChoice(flag) {");
1.118     ng       1289:     hDoc.write("  opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);");
                   1290:     hDoc.write("  opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);");
                   1291:     hDoc.write("  opener.document.SCORE.kwstyle.value = opener.radioSelection(document.hlCenter.kwdstyle);");
1.76      ng       1292:     hDoc.write("  opener.document.SCORE.refresh.value = \\"on\\";");
                   1293:     hDoc.write("  if (opener.document.SCORE.keywords.value!=\\"\\"){");
                   1294:     hDoc.write("     opener.document.SCORE.submit();");
                   1295:     hDoc.write("  }");
                   1296:     hDoc.write("  self.close()");
                   1297:     hDoc.write("}");
                   1298: 
                   1299:     hDoc.write("<");
                   1300:     hDoc.write("/script>");
                   1301: 
                   1302:     hDoc.write("</head><body bgcolor=white>");
                   1303: 
                   1304:     hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">");
                   1305:     hDoc.write("<font color=\\"green\\" size=+1>&nbsp;Keyword Highlight Options</font><br><br>");
                   1306: 
                   1307:     hDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
                   1308:     hDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
                   1309:     hDoc.write("<td><b>Text Color</b></td><td><b>Font Size</b></td><td><b>Font Style</td></tr>");
1.44      ng       1310:   }
                   1311: 
                   1312:   function highlightbody(clrval,clrtxt,clrsel,szval,sztxt,szsel,syval,sytxt,sysel) { 
1.76      ng       1313:     var hDoc = hwdWin.document;
                   1314:     hDoc.write("<tr bgcolor=\\"#ffffdd\\">");
                   1315:     hDoc.write("<td align=\\"left\\">");
                   1316:     hDoc.write("<input name=\\"kwdclr\\" type=\\"radio\\" value=\\""+clrval+"\\" "+clrsel+">&nbsp;"+clrtxt+"</td>");
                   1317:     hDoc.write("<td align=\\"left\\">");
                   1318:     hDoc.write("<input name=\\"kwdsize\\" type=\\"radio\\" value=\\""+szval+"\\" "+szsel+">&nbsp;"+sztxt+"</td>");
                   1319:     hDoc.write("<td align=\\"left\\">");
                   1320:     hDoc.write("<input name=\\"kwdstyle\\" type=\\"radio\\" value=\\""+syval+"\\" "+sysel+">&nbsp;"+sytxt+"</td>");
                   1321:     hDoc.write("</tr>");
1.44      ng       1322:   }
                   1323: 
                   1324:   function highlightend() { 
1.76      ng       1325:     var hDoc = hwdWin.document;
                   1326:     hDoc.write("</table>");
                   1327:     hDoc.write("</td></tr></table>&nbsp;");
                   1328:     hDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:updateChoice(1)\\">&nbsp;&nbsp;");
                   1329:     hDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br><br>");
                   1330:     hDoc.write("</form>");
                   1331:     hDoc.write("</body></html>");
1.128     ng       1332:     hDoc.close();
1.44      ng       1333:   }
                   1334: 
                   1335: </script>
                   1336: SUBJAVASCRIPT
                   1337: }
                   1338: 
1.71      ng       1339: #--- displays the grading box, used in essay type problem and grading by page/sequence
                   1340: sub gradeBox {
                   1341:     my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_;
                   1342: 
                   1343:     my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').
                   1344: 	'/check.gif" height="16" border="0" />';
                   1345: 
                   1346:     my $wgt    = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb,$udom,$uname);
                   1347:     my $wgtmsg = ($wgt > 0 ? '(problem weight)' : 
                   1348: 		  '<font color="red">problem weight assigned by computer</font>');
                   1349:     $wgt       = ($wgt > 0 ? $wgt : '1');
                   1350:     my $score  = ($$record{'resource.'.$partid.'.awarded'} eq '' ?
                   1351: 		  '' : $$record{'resource.'.$partid.'.awarded'}*$wgt);
                   1352:     my $result='<input type="hidden" name="WGT'.$counter.'_'.$partid.'" value="'.$wgt.'" />'."\n";
                   1353: 
1.207     albertel 1354:     my $display_part=&get_display_part($partid,undef,$symb);
1.270     albertel 1355: 
                   1356:     my %last_resets = &get_last_resets($symb,$env{'request.course.id'},
                   1357: 				       [$partid]);
                   1358:     my $aggtries = $$record{'resource.'.$partid.'.tries'};
1.269     raeburn  1359:     if ($last_resets{$partid}) {
                   1360:         $aggtries = &get_num_tries($record,$last_resets{$partid},$partid);
                   1361:     }
1.270     albertel 1362: 
1.71      ng       1363:     $result.='<table border="0"><tr><td>'.
1.207     albertel 1364: 	'<b>Part: </b>'.$display_part.' <b>Points: </b></td><td>'."\n";
1.71      ng       1365: 
                   1366:     my $ctr = 0;
                   1367:     $result.='<table border="0"><tr>'."\n";  # display radio buttons in a nice table 10 across
                   1368:     while ($ctr<=$wgt) {
1.179     albertel 1369: 	$result.= '<td><nobr><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '.
1.71      ng       1370: 	    'onclick="javascript:writeBox(this.form,\''.$counter.'_'.$partid.'\','.
1.72      ng       1371: 	    $ctr.')" value="'.$ctr.'" '.
1.179     albertel 1372: 	    ($score eq $ctr ? 'checked':'').' /> '.$ctr."</nobr></td>\n";
1.71      ng       1373: 	$result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
                   1374: 	$ctr++;
                   1375:     }
                   1376:     $result.='</tr></table>';
                   1377: 
                   1378:     $result.='</td><td>&nbsp;<b>or</b>&nbsp;</td>'."\n";
                   1379:     $result.='<td><input type="text" name="GD_BOX'.$counter.'_'.$partid.'"'.
                   1380: 	($score ne ''? ' value = "'.$score.'"':'').' size="4" '.
                   1381: 	'onChange="javascript:updateRadio(this.form,\''.$counter.'_'.$partid.'\','.
                   1382: 	$wgt.')" /></td>'."\n";
                   1383:     $result.='<td>/'.$wgt.' '.$wgtmsg.
                   1384: 	($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? '&nbsp;'.$checkIcon : '').
                   1385: 	' </td><td>'."\n";
                   1386: 
                   1387:     $result.='<select name="GD_SEL'.$counter.'_'.$partid.'" '.
                   1388: 	'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n";
                   1389:     if ($$record{'resource.'.$partid.'.solved'} eq 'excused') {
                   1390: 	$result.='<option> </option>'.
1.125     ng       1391: 	    '<option selected="on">excused</option>';
1.71      ng       1392:     } else {
                   1393: 	$result.='<option selected="on"> </option>'.
1.125     ng       1394: 	    '<option>excused</option>';
1.71      ng       1395:     }
1.125     ng       1396:     $result.='<option>reset status</option></select>'."\n";
1.71      ng       1397:     $result.="&nbsp&nbsp\n";
                   1398:     $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n".
                   1399: 	'<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n".
                   1400: 	'<input type="hidden" name="solved'.$counter.'_'.$partid.'" value="'.
1.269     raeburn  1401: 	$$record{'resource.'.$partid.'.solved'}.'" />'."\n".
                   1402:         '<input type="hidden" name="totaltries'.$counter.'_'.$partid.'" value="'.
                   1403:         $$record{'resource.'.$partid.'.tries'}.'" />'."\n".
                   1404:         '<input type="hidden" name="aggtries'.$counter.'_'.$partid.'" value="'.
                   1405:         $aggtries.'" />'."\n";
1.71      ng       1406:     $result.='</td></tr></table>'."\n";
                   1407:     return $result;
                   1408: }
1.44      ng       1409: 
1.58      albertel 1410: sub show_problem {
1.144     albertel 1411:     my ($request,$symb,$uname,$udom,$removeform,$viewon,$mode) = @_;
                   1412:     my $rendered;
                   1413:     if ($mode eq 'both' or $mode eq 'text') {
                   1414: 	$rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom,
1.257     albertel 1415: 					     $env{'request.course.id'});
1.144     albertel 1416:     }
1.58      albertel 1417:     if ($removeform) {
                   1418: 	$rendered=~s|<form(.*?)>||g;
                   1419: 	$rendered=~s|</form>||g;
                   1420: 	$rendered=~s|name="submit"|name="would_have_been_submit"|g;
                   1421:     }
1.144     albertel 1422:     my $companswer;
                   1423:     if ($mode eq 'both' or $mode eq 'answer') {
                   1424: 	$companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom,
1.257     albertel 1425: 						    $env{'request.course.id'});
1.144     albertel 1426:     }
1.58      albertel 1427:     if ($removeform) {
                   1428: 	$companswer=~s|<form(.*?)>||g;
                   1429: 	$companswer=~s|</form>||g;
1.144     albertel 1430: 	$companswer=~s|name="submit"|name="would_have_been_submit"|g;
1.58      albertel 1431:     }
                   1432:     my $result.='<table border="0" width="100%"><tr><td bgcolor="#777777">';
1.71      ng       1433:     $result.='<table border="0" width="100%">';
1.144     albertel 1434:     if ($viewon) {
                   1435: 	$result.='<tr><td bgcolor="#e6ffff"><b> ';
                   1436: 	if ($mode eq 'both' or $mode eq 'text') {
                   1437: 	    $result.='View of the problem - ';
                   1438: 	} else {
                   1439: 	    $result.='Correct answer: ';
                   1440: 	}
1.257     albertel 1441: 	$result.=$env{'form.fullname'}.'</b></td></tr>';
1.144     albertel 1442:     }
                   1443:     if ($mode eq 'both') {
                   1444: 	$result.='<tr><td bgcolor="#ffffff">'.$rendered.'<br />';
                   1445: 	$result.='<b>Correct answer:</b><br />'.$companswer;
                   1446:     } elsif ($mode eq 'text') {
                   1447: 	$result.='<tr><td bgcolor="#ffffff">'.$rendered;
                   1448:     } elsif ($mode eq 'answer') {
                   1449: 	$result.='<tr><td bgcolor="#ffffff">'.$companswer;
                   1450:     }
1.58      albertel 1451:     $result.='</td></tr></table>';
                   1452:     $result.='</td></tr></table><br />';
1.71      ng       1453:     return $result;
1.58      albertel 1454: }
                   1455: 
1.44      ng       1456: # --------------------------- show submissions of a student, option to grade 
                   1457: sub submission {
                   1458:     my ($request,$counter,$total) = @_;
                   1459: 
1.257     albertel 1460:     (my $url=$env{'form.url'})=~s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
                   1461:     my ($uname,$udom)     = ($env{'form.student'},$env{'form.userdom'});
                   1462:     $udom = ($udom eq '' ? $env{'user.domain'} : $udom); #has form.userdom changed for a student?
                   1463:     my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'});
                   1464:     $env{'form.fullname'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $env{'form.fullname'} eq '';
1.41      ng       1465: 
1.257     albertel 1466:     my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
1.41      ng       1467:     if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
1.104     albertel 1468: 
                   1469:     if (!&canview($usec)) {
1.116     ng       1470: 	$request->print('<font color="red">Unable to view requested student.('.
1.222     albertel 1471: 			$uname.'@'.$udom.' in section '.$usec.' in course id '.
1.257     albertel 1472: 			$env{'request.course.id'}.')</font>');
1.104     albertel 1473: 	$request->print(&show_grading_menu_form($symb,$url));
                   1474: 	return;
                   1475:     }
                   1476: 
1.257     albertel 1477:     if (!$env{'form.lastSub'}) { $env{'form.lastSub'} = 'datesub'; }
                   1478:     if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; }
                   1479:     if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }
                   1480:     my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');
1.122     ng       1481:     my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').
                   1482: 	'/check.gif" height="16" border="0" />';
1.41      ng       1483: 
                   1484:     # header info
                   1485:     if ($counter == 0) {
                   1486: 	&sub_page_js($request);
1.257     albertel 1487: 	&sub_page_kw_js($request) if ($env{'form.handgrade'} eq 'yes');
                   1488: 	$env{'form.probTitle'} = $env{'form.probTitle'} eq '' ? 
                   1489: 	    &Apache::lonnet::gettitle($symb) : $env{'form.probTitle'};
1.76      ng       1490: 
1.45      ng       1491: 	$request->print('<h3>&nbsp;<font color="#339933">Submission Record</font></h3>'."\n".
1.257     albertel 1492: 			'<font size=+1>&nbsp;<b>Resource: </b>'.$env{'form.probTitle'}.'</font>'."\n");
1.118     ng       1493: 
1.257     albertel 1494: 	if ($env{'form.handgrade'} eq 'no') {
1.118     ng       1495: 	    my $checkMark='<br /><br />&nbsp;<b>Note:</b> Part(s) graded correct by the computer is marked with a '.
                   1496: 		$checkIcon.' symbol.'."\n";
                   1497: 	    $request->print($checkMark);
                   1498: 	}
1.41      ng       1499: 
1.44      ng       1500: 	# option to display problem, only once else it cause problems 
                   1501:         # with the form later since the problem has a form.
1.257     albertel 1502: 	if ($env{'form.vProb'} eq 'yes' or $env{'form.vAns'} eq 'yes') {
1.144     albertel 1503: 	    my $mode;
1.257     albertel 1504: 	    if ($env{'form.vProb'} eq 'yes' && $env{'form.vAns'} eq 'yes') {
1.144     albertel 1505: 		$mode='both';
1.257     albertel 1506: 	    } elsif ($env{'form.vProb'} eq 'yes') {
1.144     albertel 1507: 		$mode='text';
1.257     albertel 1508: 	    } elsif ($env{'form.vAns'} eq 'yes') {
1.144     albertel 1509: 		$mode='answer';
                   1510: 	    }
                   1511: 	    $request->print(&show_problem($request,$symb,$uname,$udom,0,1,$mode));
1.41      ng       1512: 	}
                   1513: 	
1.44      ng       1514: 	# kwclr is the only variable that is guaranteed to be non blank 
                   1515:         # if this subroutine has been called once.
1.41      ng       1516: 	my %keyhash = ();
1.257     albertel 1517: 	if ($env{'form.kwclr'} eq '' && $env{'form.handgrade'} eq 'yes') {
1.41      ng       1518: 	    %keyhash = &Apache::lonnet::dump('nohist_handgrade',
1.257     albertel 1519: 					     $env{'course.'.$env{'request.course.id'}.'.domain'},
                   1520: 					     $env{'course.'.$env{'request.course.id'}.'.num'});
1.41      ng       1521: 
1.257     albertel 1522: 	    my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};
                   1523: 	    $env{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : '';
                   1524: 	    $env{'form.kwclr'}    = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red';
                   1525: 	    $env{'form.kwsize'}   = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';
                   1526: 	    $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
                   1527: 	    $env{'form.msgsub'}   = $keyhash{$symb.'_subject'} ne '' ? 
                   1528: 		$keyhash{$symb.'_subject'} : $env{'form.probTitle'};
                   1529: 	    $env{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0';
1.41      ng       1530: 	}
1.257     albertel 1531: 	my $overRideScore = $env{'form.overRideScore'} eq '' ? 'no' : $env{'form.overRideScore'};
1.44      ng       1532: 
1.41      ng       1533: 	$request->print('<form action="/adm/grades" method="post" name="SCORE">'."\n".
                   1534: 			'<input type="hidden" name="command"    value="handgrade" />'."\n".
1.257     albertel 1535: 			'<input type="hidden" name="saveState"  value="'.$env{'form.saveState'}.'" />'."\n".
                   1536: 			'<input type="hidden" name="Status"     value="'.$env{'form.Status'}.'" />'."\n".
1.120     ng       1537: 			'<input type="hidden" name="overRideScore" value="'.$overRideScore.'" />'."\n".
1.257     albertel 1538: 			'<input type="hidden" name="probTitle"  value="'.$env{'form.probTitle'}.'" />'."\n".
1.41      ng       1539: 			'<input type="hidden" name="refresh"    value="off" />'."\n".
1.120     ng       1540: 			'<input type="hidden" name="studentNo"  value="" />'."\n".
                   1541: 			'<input type="hidden" name="gradeOpt"   value="" />'."\n".
1.41      ng       1542: 			'<input type="hidden" name="symb"       value="'.$symb.'" />'."\n".
                   1543: 			'<input type="hidden" name="url"        value="'.$url.'" />'."\n".
1.257     albertel 1544: 			'<input type="hidden" name="showgrading" value="'.$env{'form.showgrading'}.'" />'."\n".
                   1545: 			'<input type="hidden" name="vProb"      value="'.$env{'form.vProb'}.'" />'."\n".
                   1546: 			'<input type="hidden" name="vAns"       value="'.$env{'form.vAns'}.'" />'."\n".
                   1547: 			'<input type="hidden" name="lastSub"    value="'.$env{'form.lastSub'}.'" />'."\n".
                   1548: 			'<input type="hidden" name="section"    value="'.$env{'form.section'}.'">'."\n".
                   1549: 			'<input type="hidden" name="submitonly" value="'.$env{'form.submitonly'}.'">'."\n".
                   1550: 			'<input type="hidden" name="handgrade"  value="'.$env{'form.handgrade'}.'">'."\n".
1.41      ng       1551: 			'<input type="hidden" name="NCT"'.
1.257     albertel 1552: 			' value="'.($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : $total+1).'" />'."\n");
                   1553: 	if ($env{'form.handgrade'} eq 'yes') {
                   1554: 	    $request->print('<input type="hidden" name="keywords" value="'.$env{'form.keywords'}.'" />'."\n".
                   1555: 			    '<input type="hidden" name="kwclr"    value="'.$env{'form.kwclr'}.'" />'."\n".
                   1556: 			    '<input type="hidden" name="kwsize"   value="'.$env{'form.kwsize'}.'" />'."\n".
                   1557: 			    '<input type="hidden" name="kwstyle"  value="'.$env{'form.kwstyle'}.'" />'."\n".
                   1558: 			    '<input type="hidden" name="msgsub"   value="'.$env{'form.msgsub'}.'" />'."\n".
1.123     ng       1559: 			    '<input type="hidden" name="shownSub" value="0" />'."\n".
1.257     albertel 1560: 			    '<input type="hidden" name="savemsgN" value="'.$env{'form.savemsgN'}.'" />'."\n");
1.154     albertel 1561: 	    foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) {
                   1562: 		$request->print('<input type="hidden" name="vPart" value="'.$partid.'" />'."\n");
                   1563: 	    }
1.123     ng       1564: 	}
1.41      ng       1565: 	
                   1566: 	my ($cts,$prnmsg) = (1,'');
1.257     albertel 1567: 	while ($cts <= $env{'form.savemsgN'}) {
1.41      ng       1568: 	    $prnmsg.='<input type="hidden" name="savemsg'.$cts.'" value="'.
1.123     ng       1569: 		(!exists($keyhash{$symb.'_savemsg'.$cts}) ? 
1.257     albertel 1570: 		 &Apache::lonfeedback::clear_out_html($env{'form.savemsg'.$cts}) :
1.80      ng       1571: 		 &Apache::lonfeedback::clear_out_html($keyhash{$symb.'_savemsg'.$cts})).
1.123     ng       1572: 		'" />'."\n".
                   1573: 		'<input type="hidden" name="shownOnce'.$cts.'" value="0" />'."\n";
1.41      ng       1574: 	    $cts++;
                   1575: 	}
                   1576: 	$request->print($prnmsg);
1.32      ng       1577: 
1.257     albertel 1578: 	if ($env{'form.handgrade'} eq 'yes' && $env{'form.showgrading'} eq 'yes') {
1.88      www      1579: #
                   1580: # Print out the keyword options line
                   1581: #
1.41      ng       1582: 	    $request->print(<<KEYWORDS);
1.38      ng       1583: &nbsp;<b>Keyword Options:</b>&nbsp;
1.122     ng       1584: <a href="javascript:keywords(document.SCORE)"; TARGET=_self>List</a>&nbsp; &nbsp;
1.38      ng       1585: <a href="#" onMouseDown="javascript:getSel(); return false"
                   1586:  CLASS="page">Paste Selection to List</a>&nbsp; &nbsp;
                   1587: <a href="javascript:kwhighlight()"; TARGET=_self>Highlight Attribute</a><br /><br />
                   1588: KEYWORDS
1.88      www      1589: #
                   1590: # Load the other essays for similarity check
                   1591: #
                   1592:             my $essayurl=&Apache::lonnet::declutter($url);
                   1593: 	    my ($adom,$aname,$apath)=($essayurl=~/^(\w+)\/(\w+)\/(.*)$/);
                   1594: 	    $apath=&Apache::lonnet::escape($apath);
                   1595: 	    $apath=~s/\W/\_/gs;
                   1596: 	    %oldessays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname);
1.41      ng       1597:         }
                   1598:     }
1.44      ng       1599: 
1.257     albertel 1600:     if ($env{'form.vProb'} eq 'all' or $env{'form.vAns'} eq 'all') {
1.71      ng       1601: 	$request->print('<br /><br /><br />') if ($counter > 0);
1.144     albertel 1602: 	my $mode;
1.257     albertel 1603: 	if ($env{'form.vProb'} eq 'all' && $env{'form.vAns'} eq 'all') {
1.144     albertel 1604: 	    $mode='both';
1.257     albertel 1605: 	} elsif ($env{'form.vProb'} eq 'all' ) {
1.144     albertel 1606: 	    $mode='text';
1.257     albertel 1607: 	} elsif ($env{'form.vAns'} eq 'all') {
1.144     albertel 1608: 	    $mode='answer';
                   1609: 	}
                   1610: 	$request->print(&show_problem($request,$symb,$uname,$udom,1,1,$mode));
1.58      albertel 1611:     }
1.144     albertel 1612: 
1.257     albertel 1613:     my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);
1.147     albertel 1614:     my ($partlist,$handgrade,$responseType) = &response_type($url,$symb);
1.41      ng       1615: 
1.44      ng       1616:     # Display student info
1.41      ng       1617:     $request->print(($counter == 0 ? '' : '<br />'));
1.45      ng       1618:     my $result='<table border="0" width=100%><tr><td bgcolor="#777777">'."\n".
                   1619: 	'<table border="0" width=100%><tr bgcolor="#edffff"><td>'."\n";
1.44      ng       1620: 
1.257     albertel 1621:     $result.='<b>Fullname: </b>'.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).'<br />'."\n";
1.45      ng       1622:     $result.='<input type="hidden" name="name'.$counter.
1.257     albertel 1623: 	'" value="'.$env{'form.fullname'}.'" />'."\n";
1.41      ng       1624: 
1.118     ng       1625:     # If any part of the problem is an essay-response (handgraded), then check for collaborators
1.45      ng       1626:     my @col_fullnames;
1.56      matthew  1627:     my ($classlist,$fullname);
1.257     albertel 1628:     if ($env{'form.handgrade'} eq 'yes') {
1.80      ng       1629: 	($classlist,undef,$fullname) = &getclasslist('all','0');
1.41      ng       1630: 	for (keys (%$handgrade)) {
1.44      ng       1631: 	    my $ncol = &Apache::lonnet::EXT('resource.'.$_.
1.57      matthew  1632: 					    '.maxcollaborators',
                   1633:                                             $symb,$udom,$uname);
                   1634: 	    next if ($ncol <= 0);
                   1635:             s/\_/\./g;
                   1636:             next if ($record{'resource.'.$_.'.collaborators'} eq '');
1.86      ng       1637:             my @goodcollaborators = ();
                   1638:             my @badcollaborators  = ();
                   1639: 	    foreach (split(/,?\s+/,$record{'resource.'.$_.'.collaborators'})) { 
                   1640: 		$_ =~ s/[\$\^\(\)]//g;
                   1641: 		next if ($_ eq '');
1.80      ng       1642: 		my ($co_name,$co_dom) = split /\@|:/,$_;
1.86      ng       1643: 		$co_dom = $udom if (! defined($co_dom) || $co_dom =~ /^domain$/i);
1.80      ng       1644: 		next if ($co_name eq $uname && $co_dom eq $udom);
1.86      ng       1645: 		# Doing this grep allows 'fuzzy' specification
                   1646: 		my @Matches = grep /^$co_name:$co_dom$/i,keys %$classlist;
                   1647: 		if (! scalar(@Matches)) {
                   1648: 		    push @badcollaborators,$_;
                   1649: 		} else {
                   1650: 		    push @goodcollaborators, @Matches;
                   1651: 		}
1.80      ng       1652: 	    }
1.86      ng       1653:             if (scalar(@goodcollaborators) != 0) {
1.57      matthew  1654:                 $result.='<b>Collaborators: </b>';
1.86      ng       1655:                 foreach (@goodcollaborators) {
                   1656: 		    my ($lastname,$givenn) = split(/,/,$$fullname{$_});
                   1657: 		    push @col_fullnames, $givenn.' '.$lastname;
                   1658: 		    $result.=$$fullname{$_}.'&nbsp; &nbsp; &nbsp;';
                   1659: 		}
1.57      matthew  1660:                 $result.='<br />'."\n";
1.150     albertel 1661: 		my ($part)=split(/\./,$_);
1.86      ng       1662: 		$result.='<input type="hidden" name="collaborator'.$counter.
1.150     albertel 1663: 		    '" value="'.$part.':'.(join ':',@goodcollaborators).'" />'.
                   1664: 		    "\n";
1.86      ng       1665: 	    }
                   1666: 	    if (scalar(@badcollaborators) > 0) {
                   1667: 		$result.='<table border="0"><tr bgcolor="#ffbbbb"><td>';
                   1668: 		$result.='This student has submitted ';
                   1669: 		$result.=(scalar(@badcollaborators) == 1) ? 'an invalid collaborator' : 'invalid collaborators';
                   1670: 		$result .= ': '.join(', ',@badcollaborators);
                   1671: 		$result .= '</td></tr></table>';
                   1672: 	    }         
                   1673: 	    if (scalar(@badcollaborators > $ncol)) {
                   1674: 		$result .= '<table border="0"><tr bgcolor="#ffbbbb"><td>';
                   1675: 		$result .= 'This student has submitted too many '.
                   1676: 		    'collaborators.  Maximum is '.$ncol.'.';
                   1677: 		$result .= '</td></tr></table>';
                   1678: 	    }
1.41      ng       1679: 	}
                   1680:     }
1.44      ng       1681:     $request->print($result."\n");
1.33      ng       1682: 
1.44      ng       1683:     # print student answer/submission
                   1684:     # Options are (1) Handgaded submission only
                   1685:     #             (2) Last submission, includes submission that is not handgraded 
                   1686:     #                  (for multi-response type part)
                   1687:     #             (3) Last submission plus the parts info
                   1688:     #             (4) The whole record for this student
1.257     albertel 1689:     if ($env{'form.lastSub'} =~ /^(lastonly|hdgrade)$/) {
1.151     albertel 1690: 	my ($string,$timestamp)= &get_last_submission(\%record);
                   1691: 	my $lastsubonly=''.
                   1692: 	    ($$timestamp eq '' ? '' : '<b>Date Submitted:</b> '.
                   1693: 	     $$timestamp)."</td></tr>\n";
                   1694: 	if ($$timestamp eq '') {
                   1695: 	    $lastsubonly.='<tr><td bgcolor="#ffffe6">'.$$string[0]; 
                   1696: 	} else {
                   1697: 	    my %seenparts;
                   1698: 	    for my $part (sort keys(%$handgrade)) {
                   1699: 		my ($partid,$respid) = split(/_/,$part);
1.207     albertel 1700: 		my $display_part=&get_display_part($partid,$url,$symb);
1.257     albertel 1701: 		if ($env{"form.$uname:$udom:$partid:submitted_by"}) {
1.151     albertel 1702: 		    if (exists($seenparts{$partid})) { next; }
                   1703: 		    $seenparts{$partid}=1;
1.207     albertel 1704: 		    my $submitby='<b>Part:</b> '.$display_part.
                   1705: 			' <b>Collaborative submission by:</b> '.
1.151     albertel 1706: 			'<a href="javascript:viewSubmitter(\''.
1.257     albertel 1707: 			$env{"form.$uname:$udom:$partid:submitted_by"}.
1.151     albertel 1708: 			'\')"; TARGET=_self>'.
1.257     albertel 1709: 			$$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a><br />';
1.151     albertel 1710: 		    $request->print($submitby);
                   1711: 		    next;
                   1712: 		}
                   1713: 		my $responsetype = $responseType->{$partid}->{$respid};
                   1714: 		if (!exists($record{"resource.$partid.$respid.submission"})) {
1.207     albertel 1715: 		    $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part:</b> '.
                   1716: 			$display_part.' <font color="#999999">( ID '.$respid.
1.151     albertel 1717: 			' )</font>&nbsp; &nbsp;'.
                   1718: 			'<font color="red">Nothing submitted - no attempts</font><br /><br />';
                   1719: 		    next;
                   1720: 		}
                   1721: 		foreach (@$string) {
                   1722: 		    my ($partid,$respid) = /^resource\.([^\.]*)\.([^\.]*)\.submission/;
                   1723: 		    if ($part ne ($partid.'_'.$respid)) { next; }
                   1724: 		    my ($ressub,$subval) = split(/:/,$_,2);
                   1725: 		    # Similarity check
                   1726: 		    my $similar='';
1.257     albertel 1727: 		    if($env{'form.checkPlag'}){
1.151     albertel 1728: 			my ($oname,$odom,$ocrsid,$oessay,$osim)=
                   1729: 			    &most_similar($uname,$udom,$subval);
                   1730: 			if ($osim) {
                   1731: 			    $osim=int($osim*100.0);
                   1732: 			    $similar="<hr /><h3><font color=\"#FF0000\">Essay".
                   1733: 				" is $osim% similar to an essay by ".
                   1734: 				&Apache::loncommon::plainname($oname,$odom).
                   1735: 				'</font></h3><blockquote><i>'.
                   1736: 				&keywords_highlight($oessay).
                   1737: 				'</i></blockquote><hr />';
                   1738: 			}
1.150     albertel 1739: 		    }
1.151     albertel 1740: 		    my $order=&get_order($partid,$respid,$symb,$uname,$udom);
1.257     albertel 1741: 		    if ($env{'form.lastSub'} eq 'lastonly' || 
                   1742: 			($env{'form.lastSub'} eq 'hdgrade' && 
1.151     albertel 1743: 			 $$handgrade{$part} eq 'yes')) {
1.207     albertel 1744: 			my $display_part=&get_display_part($partid,$url,$symb);
                   1745: 			$lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part:</b> '.
                   1746: 			    $display_part.' <font color="#999999">( ID '.$respid.
1.151     albertel 1747: 			    ' )</font>&nbsp; &nbsp;';
1.232     albertel 1748: 			my @files;
1.230     banghart 1749: 			if ($record{"resource.$partid.$respid.portfiles"}) {
1.232     albertel 1750: 			    my $file_url = '/uploaded/'.$udom.'/'.$uname.'/portfolio';
                   1751: 			    foreach my $file (split(',',$record{"resource.$partid.$respid.portfiles"})) {
                   1752: 				push(@files,$file_url.$file);
                   1753: 			    
                   1754: 				&Apache::lonnet::logthis("found a portfolio file".$record{"resource.$partid.$respid.portfiles"});
                   1755: 				&Apache::lonnet::logthis("uploaded URL file".$record{"resource.$partid.$respid.uploadedurl"});
                   1756: 			    }
                   1757: 			}
                   1758: 			if ($record{"resource.$partid.$respid.uploadedurl"}) {
                   1759: 			    push(@files,$record{"resource.$partid.$respid.uploadedurl"});
1.230     banghart 1760: 			}
1.232     albertel 1761: 			if (@files) {
                   1762: 			    $lastsubonly.='<br /><font color="red" size="1">Like all files provided by users, this file may contain virusses</font><br />';
                   1763: 			    foreach my $file (@files) {
                   1764: 				&Apache::lonnet::allowuploaded('/adm/grades',$file);
                   1765: 				$lastsubonly.='<br /><a href="'.$file.'" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border=0"> '.$file.'</a>';
                   1766: 			    }
1.236     albertel 1767: 			    $lastsubonly.='<br />';
1.41      ng       1768: 			}
1.151     albertel 1769: 			$lastsubonly.='<b>Submitted Answer: </b>'.
                   1770: 			    &cleanRecord($subval,$responsetype,$symb,$partid,
                   1771: 					 $respid,\%record,$order);
                   1772: 			if ($similar) {$lastsubonly.="<br /><br />$similar\n";}
1.41      ng       1773: 		    }
                   1774: 		}
                   1775: 	    }
1.151     albertel 1776: 	}
                   1777: 	$lastsubonly.='</td></tr><tr bgcolor="#ffffff"><td>'."\n";
                   1778: 	$request->print($lastsubonly);
1.257     albertel 1779:     } elsif ($env{'form.lastSub'} eq 'datesub') {
1.122     ng       1780: 	my (undef,$responseType,undef,$parts) = &showResourceInfo($url);
1.148     albertel 1781: 	$request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom));
1.257     albertel 1782:     } elsif ($env{'form.lastSub'} =~ /^(last|all)$/) {
1.41      ng       1783: 	$request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,
1.257     albertel 1784: 								 $env{'request.course.id'},
1.44      ng       1785: 								 $last,'.submission',
                   1786: 								 'Apache::grades::keywords_highlight'));
1.41      ng       1787:     }
1.120     ng       1788: 
1.121     ng       1789:     $request->print('<input type="hidden" name="unamedom'.$counter.'" value="'.$uname.':'
                   1790: 	.$udom.'" />'."\n");
1.41      ng       1791:     
1.44      ng       1792:     # return if view submission with no grading option
1.257     albertel 1793:     if ($env{'form.showgrading'} eq '' || (!&canmodify($usec))) {
1.120     ng       1794: 	my $toGrade.='<input type="button" value="Grade Student" '.
1.121     ng       1795: 	    'onClick="javascript:checksubmit(this.form,\'Grade Student\',\''
                   1796: 	    .$counter.'\');" TARGET=_self> &nbsp;'."\n" if (&canmodify($usec));
1.169     albertel 1797: 	$toGrade.='</td></tr></table></td></tr></table>'."\n";
1.257     albertel 1798: 	if (($env{'form.command'} eq 'submission') || 
                   1799: 	    ($env{'form.command'} eq 'processGroup' && $counter == $total)) {
1.169     albertel 1800: 	    $toGrade.='</form>'.&show_grading_menu_form($symb,$url) 
                   1801: 	}
1.180     albertel 1802: 	$request->print($toGrade);
1.41      ng       1803: 	return;
1.180     albertel 1804:     } else {
                   1805: 	$request->print('</td></tr></table></td></tr></table>'."\n");
1.41      ng       1806:     }
1.33      ng       1807: 
1.121     ng       1808:     # essay grading message center
1.257     albertel 1809:     if ($env{'form.handgrade'} eq 'yes') {
                   1810: 	my ($lastname,$givenn) = split(/,/,$env{'form.fullname'});
1.118     ng       1811: 	my $msgfor = $givenn.' '.$lastname;
                   1812: 	if (scalar(@col_fullnames) > 0) {
                   1813: 	    my $lastone = pop @col_fullnames;
                   1814: 	    $msgfor .= ', '.(join ', ',@col_fullnames).' and '.$lastone.'.';
                   1815: 	}
                   1816: 	$msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript
1.121     ng       1817: 	$result='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".
                   1818: 	    '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n";
                   1819: 	$result.='&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.
1.118     ng       1820: 	    ',\''.$msgfor.'\')"; TARGET=_self>'.
                   1821: 	    'Compose Message to student'.(scalar(@col_fullnames) >= 1 ? 's' : '').'</a> &nbsp;'.
                   1822: 	    '<img src="'.$request->dir_config('lonIconsURL').
                   1823: 	    '/mailbkgrd.gif" width="14" height="10" name="mailicon'.$counter.'" />'."\n".
                   1824: 	    '<br />&nbsp;(Message will be sent when you click on Save & Next below.)'."\n" 
1.257     albertel 1825: 	    if ($env{'form.handgrade'} eq 'yes');
1.121     ng       1826: 	$request->print($result);
1.118     ng       1827:     }
1.41      ng       1828: 
                   1829:     my %seen = ();
                   1830:     my @partlist;
1.129     ng       1831:     my @gradePartRespid;
1.41      ng       1832:     for (sort keys(%$handgrade)) {
                   1833: 	my ($partid,$respid) = split(/_/);
                   1834: 	next if ($seen{$partid} > 0);
                   1835: 	$seen{$partid}++;
1.257     albertel 1836: 	next if ($$handgrade{$_} =~ /:no$/ && $env{'form.lastSub'} =~ /^(hdgrade)$/);
1.41      ng       1837: 	push @partlist,$partid;
1.129     ng       1838: 	push @gradePartRespid,$partid.'.'.$respid;
1.41      ng       1839: 
1.71      ng       1840: 	$request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));
1.41      ng       1841:     }
1.45      ng       1842:     $result='<input type="hidden" name="partlist'.$counter.
                   1843: 	'" value="'.(join ":",@partlist).'" />'."\n";
1.129     ng       1844:     $result.='<input type="hidden" name="gradePartRespid'.
                   1845: 	'" value="'.(join ":",@gradePartRespid).'" />'."\n" if ($counter == 0);
1.45      ng       1846:     my $ctr = 0;
                   1847:     while ($ctr < scalar(@partlist)) {
                   1848: 	$result.='<input type="hidden" name="partid'.$counter.'_'.$ctr.'" value="'.
                   1849: 	    $partlist[$ctr].'" />'."\n";
                   1850: 	$ctr++;
                   1851:     }
                   1852:     $request->print($result.'</td></tr></table></td></tr></table>'."\n");
1.41      ng       1853: 
                   1854:     # print end of form
                   1855:     if ($counter == $total) {
1.120     ng       1856: 	my $endform='<table border="0"><tr><td>'."\n";
1.119     ng       1857: 	$endform.='<input type="button" value="Save & Next" '.
                   1858: 	    'onClick="javascript:checksubmit(this.form,\'Save & Next\','.
                   1859: 	    $total.','.scalar(@partlist).');" TARGET=_self> &nbsp;'."\n";
                   1860: 	my $ntstu ='<select name="NTSTU">'.
                   1861: 	    '<option>1</option><option>2</option>'.
                   1862: 	    '<option>3</option><option>5</option>'.
                   1863: 	    '<option>7</option><option>10</option></select>'."\n";
1.257     albertel 1864: 	my $nsel = ($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : '1');
1.119     ng       1865: 	$ntstu =~ s/<option>$nsel</<option selected="on">$nsel</;
                   1866: 	$endform.=$ntstu.'student(s) &nbsp;&nbsp;';
1.126     ng       1867: 	$endform.='<input type="button" value="Previous" '.
                   1868: 	    'onClick="javascript:checksubmit(this.form,\'Previous\');" TARGET=_self> &nbsp;'."\n".
                   1869: 	    '<input type="button" value="Next" '.
                   1870: 	    'onClick="javascript:checksubmit(this.form,\'Next\');" TARGET=_self> &nbsp;';
                   1871: 	$endform.='(Next and Previous (student) do not save the scores.)'."\n" ;
1.45      ng       1872: 	$endform.='</td><tr></table></form>';
1.50      albertel 1873: 	$endform.=&show_grading_menu_form($symb,$url);
1.41      ng       1874: 	$request->print($endform);
                   1875:     }
                   1876:     return '';
1.38      ng       1877: }
                   1878: 
1.44      ng       1879: #--- Retrieve the last submission for all the parts
1.38      ng       1880: sub get_last_submission {
1.119     ng       1881:     my ($returnhash)=@_;
1.46      ng       1882:     my (@string,$timestamp);
1.119     ng       1883:     if ($$returnhash{'version'}) {
1.46      ng       1884: 	my %lasthash=();
                   1885: 	my ($version);
1.119     ng       1886: 	for ($version=1;$version<=$$returnhash{'version'};$version++) {
                   1887: 	    foreach (sort(split(/\:/,$$returnhash{$version.':keys'}))) {
                   1888: 		$lasthash{$_}=$$returnhash{$version.':'.$_};
                   1889: 		   $timestamp = scalar(localtime($$returnhash{$version.':timestamp'}));
1.46      ng       1890: 	    }
                   1891: 	}
                   1892: 	foreach ((keys %lasthash)) {
                   1893: 	    if ($_ =~ /\.submission$/) {
                   1894: 		my ($partid,$foo) = split(/submission$/,$_);
                   1895: 		my $draft  = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ?
                   1896: 		    '<font color="red">Draft Copy</font> ' : '';
                   1897: 		push @string, (join(':',$_,$draft.$lasthash{$_}));
1.41      ng       1898: 	    }
                   1899: 	}
                   1900:     }
1.125     ng       1901:     @string = $string[0] eq '' ? '<font color="red">Nothing submitted - no attempts.</font>' : @string;
1.46      ng       1902:     return \@string,\$timestamp;
1.38      ng       1903: }
1.35      ng       1904: 
1.44      ng       1905: #--- High light keywords, with style choosen by user.
1.38      ng       1906: sub keywords_highlight {
1.44      ng       1907:     my $string    = shift;
1.257     albertel 1908:     my $size      = $env{'form.kwsize'} eq '0' ? '' : 'size='.$env{'form.kwsize'};
                   1909:     my $styleon   = $env{'form.kwstyle'} eq ''  ? '' : $env{'form.kwstyle'};
1.41      ng       1910:     (my $styleoff = $styleon) =~ s/\</\<\//;
1.257     albertel 1911:     my @keylist   = split(/[,\s+]/,$env{'form.keywords'});
1.41      ng       1912:     foreach (@keylist) {
1.257     albertel 1913: 	$string =~ s/\b\Q$_\E(\b|\.)/<font color\=$env{'form.kwclr'} $size\>$styleon$_$styleoff<\/font>/gi;
1.41      ng       1914:     }
                   1915:     return $string;
1.38      ng       1916: }
1.36      ng       1917: 
1.44      ng       1918: #--- Called from submission routine
1.38      ng       1919: sub processHandGrade {
1.41      ng       1920:     my ($request) = shift;
1.257     albertel 1921:     my $url    = $env{'form.url'};
                   1922:     my $symb   = $env{'form.symb'};
                   1923:     my $button = $env{'form.gradeOpt'};
                   1924:     my $ngrade = $env{'form.NCT'};
                   1925:     my $ntstu  = $env{'form.NTSTU'};
1.44      ng       1926:     if ($button eq 'Save & Next') {
                   1927: 	my $ctr = 0;
                   1928: 	while ($ctr < $ngrade) {
1.257     albertel 1929: 	    my ($uname,$udom) = split(/:/,$env{'form.unamedom'.$ctr});
1.77      ng       1930: 	    my ($errorflag,$pts,$wgt) = &saveHandGrade($request,$url,$symb,$uname,$udom,$ctr);
1.71      ng       1931: 	    if ($errorflag eq 'no_score') {
                   1932: 		$ctr++;
                   1933: 		next;
                   1934: 	    }
1.104     albertel 1935: 	    if ($errorflag eq 'not_allowed') {
                   1936: 		$request->print("<font color=\"red\">Not allowed to modify grades for $uname:$udom</font>");
                   1937: 		$ctr++;
                   1938: 		next;
                   1939: 	    }
1.257     albertel 1940: 	    my $includemsg = $env{'form.includemsg'.$ctr};
1.44      ng       1941: 	    my ($subject,$message,$msgstatus) = ('','','');
1.62      albertel 1942: 	    if ($includemsg =~ /savemsg|newmsg\Q$ctr\E/) {
1.257     albertel 1943: 		$subject = $env{'form.msgsub'} if ($includemsg =~ /^msgsub/);
1.44      ng       1944: 		my (@msgnum) = split(/,/,$includemsg);
                   1945: 		foreach (@msgnum) {
1.257     albertel 1946: 		    $message.=$env{'form.'.$_} if ($_ =~ /savemsg|newmsg/ && $_ ne '');
1.44      ng       1947: 		}
1.80      ng       1948: 		$message =&Apache::lonfeedback::clear_out_html($message);
1.77      ng       1949: 		$message.="\n\nPoint".($pts > 1 ? 's':'').' awarded = '.$pts.' out of '.$wgt;
1.80      ng       1950: 		$message.=" for <a href=\"".
                   1951: 		    &Apache::lonnet::clutter($url).
1.257     albertel 1952: 		    "?symb=$symb\">$env{'form.probTitle'}</a>";
1.44      ng       1953: 		$msgstatus = &Apache::lonmsg::user_normal_msg ($uname,$udom,
1.257     albertel 1954: 							       $env{'form.msgsub'},$message);
1.44      ng       1955: 	    }
1.257     albertel 1956: 	    if ($env{'form.collaborator'.$ctr}) {
1.155     albertel 1957: 		my @collabstrs=&Apache::loncommon::get_env_multiple("form.collaborator$ctr");
1.150     albertel 1958: 		foreach my $collabstr (@collabstrs) {
                   1959: 		    my ($part,@collaborators) = split(/:/,$collabstr);
                   1960: 		    foreach (@collaborators) {
                   1961: 			my ($errorflag,$pts,$wgt) = 
                   1962: 			    &saveHandGrade($request,$url,$symb,$_,$udom,$ctr,
1.257     albertel 1963: 					   $env{'form.unamedom'.$ctr},$part);
1.150     albertel 1964: 			if ($errorflag eq 'not_allowed') {
                   1965: 			    $request->print("<font color=\"red\">Not allowed to modify grades for $_:$udom</font>");
                   1966: 			    next;
                   1967: 			} else {
                   1968: 			    if ($message ne '') {
1.257     albertel 1969: 				$msgstatus = &Apache::lonmsg::user_normal_msg($_,$udom,$env{'form.msgsub'},$message);
1.150     albertel 1970: 			    }
                   1971: 			    
1.104     albertel 1972: 			}
1.44      ng       1973: 		    }
                   1974: 		}
                   1975: 	    }
                   1976: 	    $ctr++;
                   1977: 	}
                   1978:     }
                   1979: 
1.257     albertel 1980:     if ($env{'form.handgrade'} eq 'yes') {
1.119     ng       1981: 	# Keywords sorted in alphabatical order
1.257     albertel 1982: 	my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};
1.119     ng       1983: 	my %keyhash = ();
1.257     albertel 1984: 	$env{'form.keywords'}           =~ s/,\s{0,}|\s+/ /g;
                   1985: 	$env{'form.keywords'}           =~ s/^\s+|\s+$//;
                   1986: 	my (@keywords) = sort(split(/\s+/,$env{'form.keywords'}));
                   1987: 	$env{'form.keywords'} = join(' ',@keywords);
                   1988: 	$keyhash{$symb.'_keywords'}     = $env{'form.keywords'};
                   1989: 	$keyhash{$symb.'_subject'}      = $env{'form.msgsub'};
                   1990: 	$keyhash{$loginuser.'_kwclr'}   = $env{'form.kwclr'};
                   1991: 	$keyhash{$loginuser.'_kwsize'}  = $env{'form.kwsize'};
                   1992: 	$keyhash{$loginuser.'_kwstyle'} = $env{'form.kwstyle'};
1.119     ng       1993: 
                   1994: 	# message center - Order of message gets changed. Blank line is eliminated.
1.257     albertel 1995: 	# New messages are saved in env for the next student.
1.119     ng       1996: 	# All messages are saved in nohist_handgrade.db
                   1997: 	my ($ctr,$idx) = (1,1);
1.257     albertel 1998: 	while ($ctr <= $env{'form.savemsgN'}) {
                   1999: 	    if ($env{'form.savemsg'.$ctr} ne '') {
                   2000: 		$keyhash{$symb.'_savemsg'.$idx} = $env{'form.savemsg'.$ctr};
1.119     ng       2001: 		$idx++;
                   2002: 	    }
                   2003: 	    $ctr++;
1.41      ng       2004: 	}
1.119     ng       2005: 	$ctr = 0;
                   2006: 	while ($ctr < $ngrade) {
1.257     albertel 2007: 	    if ($env{'form.newmsg'.$ctr} ne '') {
                   2008: 		$keyhash{$symb.'_savemsg'.$idx} = $env{'form.newmsg'.$ctr};
                   2009: 		$env{'form.savemsg'.$idx} = $env{'form.newmsg'.$ctr};
1.119     ng       2010: 		$idx++;
                   2011: 	    }
                   2012: 	    $ctr++;
1.41      ng       2013: 	}
1.257     albertel 2014: 	$env{'form.savemsgN'} = --$idx;
                   2015: 	$keyhash{$symb.'_savemsgN'} = $env{'form.savemsgN'};
1.119     ng       2016: 	my $putresult = &Apache::lonnet::put
                   2017: 	    ('nohist_handgrade',\%keyhash,
1.257     albertel 2018: 	     $env{'course.'.$env{'request.course.id'}.'.domain'},
                   2019: 	     $env{'course.'.$env{'request.course.id'}.'.num'});
1.41      ng       2020:     }
1.44      ng       2021:     # Called by Save & Refresh from Highlight Attribute Window
1.257     albertel 2022:     my (undef,undef,$fullname) = &getclasslist($env{'form.section'},'1');
                   2023:     if ($env{'form.refresh'} eq 'on') {
1.86      ng       2024: 	my ($ctr,$total) = (0,0);
                   2025: 	while ($ctr < $ngrade) {
1.257     albertel 2026: 	    $total++ if  $env{'form.unamedom'.$ctr} ne '';
1.86      ng       2027: 	    $ctr++;
                   2028: 	}
1.257     albertel 2029: 	$env{'form.NTSTU'}=$ngrade;
1.86      ng       2030: 	$ctr = 0;
                   2031: 	while ($ctr < $total) {
1.257     albertel 2032: 	    my $processUser = $env{'form.unamedom'.$ctr};
                   2033: 	    ($env{'form.student'},$env{'form.userdom'}) = split(/:/,$processUser);
                   2034: 	    $env{'form.fullname'} = $$fullname{$processUser};
1.86      ng       2035: 	    &submission($request,$ctr,$total-1);
1.41      ng       2036: 	    $ctr++;
                   2037: 	}
                   2038: 	return '';
                   2039:     }
1.36      ng       2040: 
1.121     ng       2041: # Go directly to grade student - from submission or link from chart page
1.120     ng       2042:     if ($button eq 'Grade Student') {
1.257     albertel 2043: 	(undef,undef,$env{'form.handgrade'},undef,undef) = &showResourceInfo($url);
                   2044: 	my $processUser = $env{'form.unamedom'.$env{'form.studentNo'}};
                   2045: 	($env{'form.student'},$env{'form.userdom'}) = split(/:/,$processUser);
                   2046: 	$env{'form.fullname'} = $$fullname{$processUser};
1.120     ng       2047: 	&submission($request,0,0);
                   2048: 	return '';
                   2049:     }
                   2050: 
1.44      ng       2051:     # Get the next/previous one or group of students
1.257     albertel 2052:     my $firststu = $env{'form.unamedom0'};
                   2053:     my $laststu = $env{'form.unamedom'.($ngrade-1)};
1.119     ng       2054:     my $ctr = 2;
1.41      ng       2055:     while ($laststu eq '') {
1.257     albertel 2056: 	$laststu  = $env{'form.unamedom'.($ngrade-$ctr)};
1.41      ng       2057: 	$ctr++;
                   2058: 	$laststu = $firststu if ($ctr > $ngrade);
                   2059:     }
1.44      ng       2060: 
1.41      ng       2061:     my (@parsedlist,@nextlist);
                   2062:     my ($nextflg) = 0;
1.53      albertel 2063:     foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
1.41      ng       2064: 	if ($nextflg == 1 && $button =~ /Next$/) {
                   2065: 	    push @parsedlist,$_;
                   2066: 	}
                   2067: 	$nextflg = 1 if ($_ eq $laststu);
                   2068: 	if ($button eq 'Previous') {
                   2069: 	    last if ($_ eq $firststu);
                   2070: 	    push @parsedlist,$_;
                   2071: 	}
                   2072:     }
                   2073:     $ctr = 0;
                   2074:     @parsedlist = reverse @parsedlist if ($button eq 'Previous');
1.145     albertel 2075:     my ($partlist) = &response_type($url);
1.41      ng       2076:     foreach my $student (@parsedlist) {
1.257     albertel 2077: 	my $submitonly=$env{'form.submitonly'};
1.41      ng       2078: 	my ($uname,$udom) = split(/:/,$student);
1.156     albertel 2079: 	if ($submitonly =~ /^(yes|graded|incorrect)$/) {
1.257     albertel 2080: #	    my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);
1.145     albertel 2081: 	    my %status=&student_gradeStatus($url,$symb,$udom,$uname,$partlist);
                   2082: 	    my $submitted = 0;
1.248     albertel 2083: 	    my $ungraded = 0;
                   2084: 	    my $incorrect = 0;
1.145     albertel 2085: 	    foreach (keys(%status)) {
                   2086: 		$submitted = 1 if ($status{$_} ne 'nothing');
1.248     albertel 2087: 		$ungraded = 1 if ($status{$_} =~ /^ungraded/);
                   2088: 		$incorrect = 1 if ($status{$_} =~ /^incorrect/);
1.145     albertel 2089: 		my ($foo,$partid,$foo1) = split(/\./,$_);
                   2090: 		if ($status{'resource.'.$partid.'.submitted_by'} ne '') {
                   2091: 		    $submitted = 0;
                   2092: 		}
1.41      ng       2093: 	    }
1.156     albertel 2094: 	    next if (!$submitted && ($submitonly eq 'yes' ||
                   2095: 				     $submitonly eq 'incorrect' ||
                   2096: 				     $submitonly eq 'graded'));
1.248     albertel 2097: 	    next if (!$ungraded && ($submitonly eq 'graded'));
                   2098: 	    next if (!$incorrect && $submitonly eq 'incorrect');
1.41      ng       2099: 	}
                   2100: 	push @nextlist,$student if ($ctr < $ntstu);
1.129     ng       2101: 	last if ($ctr == $ntstu);
1.41      ng       2102: 	$ctr++;
                   2103:     }
1.36      ng       2104: 
1.41      ng       2105:     $ctr = 0;
                   2106:     my $total = scalar(@nextlist)-1;
1.39      ng       2107: 
1.41      ng       2108:     foreach (sort @nextlist) {
                   2109: 	my ($uname,$udom,$submitter) = split(/:/);
1.257     albertel 2110: 	$env{'form.student'}  = $uname;
                   2111: 	$env{'form.userdom'}  = $udom;
                   2112: 	$env{'form.fullname'} = $$fullname{$_};
1.41      ng       2113: 	&submission($request,$ctr,$total);
                   2114: 	$ctr++;
                   2115:     }
                   2116:     if ($total < 0) {
                   2117: 	my $the_end = '<h3><font color="red">LON-CAPA User Message</font></h3><br />'."\n";
                   2118: 	$the_end.='<b>Message: </b> No more students for this section or class.<br /><br />'."\n";
                   2119: 	$the_end.='Click on the button below to return to the grading menu.<br /><br />'."\n";
                   2120: 	$the_end.=&show_grading_menu_form ($symb,$url);
                   2121: 	$request->print($the_end);
                   2122:     }
                   2123:     return '';
1.38      ng       2124: }
1.36      ng       2125: 
1.44      ng       2126: #---- Save the score and award for each student, if changed
1.38      ng       2127: sub saveHandGrade {
1.150     albertel 2128:     my ($request,$url,$symb,$stuname,$domain,$newflg,$submitter,$part) = @_;
1.262     banghart 2129:     my @v_flag;
1.104     albertel 2130:     my $usec = &Apache::lonnet::getsection($domain,$stuname,
1.257     albertel 2131: 					   $env{'request.course.id'});
1.104     albertel 2132:     if (!&canmodify($usec)) { return('not_allowed'); }
1.257     albertel 2133:     my %record     = &Apache::lonnet::restore($symb,$env{'request.course.id'},$domain,$stuname);
1.251     banghart 2134:     my @parts_graded;
1.77      ng       2135:     my %newrecord  = ();
                   2136:     my ($pts,$wgt) = ('','');
1.269     raeburn  2137:     my %aggregate = ();
                   2138:     my $aggregateflag = 0;
1.259     banghart 2139:     foreach my $new_part (split(/:/,$env{'form.partlist'.$newflg})) {
1.150     albertel 2140: 	#collaborator may vary for different parts
1.259     banghart 2141: 	if ($submitter && $new_part ne $part) { next; }
                   2142: 	my $dropMenu = $env{'form.GD_SEL'.$newflg.'_'.$new_part};
1.125     ng       2143: 	if ($dropMenu eq 'excused') {
1.259     banghart 2144: 	    if ($record{'resource.'.$new_part.'.solved'} ne 'excused') {
                   2145: 		$newrecord{'resource.'.$new_part.'.solved'} = 'excused';
                   2146: 		if (exists($record{'resource.'.$new_part.'.awarded'})) {
                   2147: 		    $newrecord{'resource.'.$new_part.'.awarded'} = '';
1.58      albertel 2148: 		}
1.259     banghart 2149: 	    $newrecord{'resource.'.$new_part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}";
1.58      albertel 2150: 	    }
1.125     ng       2151: 	} elsif ($dropMenu eq 'reset status'
1.259     banghart 2152: 		 && exists($record{'resource.'.$new_part.'.solved'})) { #don't bother if no old records -> no attempts
1.197     albertel 2153: 	    foreach my $key (keys (%record)) {
1.259     banghart 2154: 		if ($key=~/^resource\.\Q$new_part\E\./) { $newrecord{$key} = ''; }
1.197     albertel 2155: 	    }
1.259     banghart 2156: 	    $newrecord{'resource.'.$new_part.'.regrader'}=
1.257     albertel 2157: 		"$env{'user.name'}:$env{'user.domain'}";
1.270     albertel 2158:             my $totaltries = $record{'resource.'.$part.'.tries'};
                   2159: 
                   2160:             my %last_resets = &get_last_resets($symb,$env{'request.course.id'},
                   2161: 					       [$new_part]);
                   2162:             my $aggtries =$totaltries;
1.269     raeburn  2163:             if ($last_resets{$new_part}) {
1.270     albertel 2164:                 $aggtries = &get_num_tries(\%record,$last_resets{$new_part},
                   2165: 					   $new_part);
1.269     raeburn  2166:             }
1.270     albertel 2167: 
                   2168:             my $solvedstatus = $record{'resource.'.$new_part.'.solved'};
1.269     raeburn  2169:             if ($aggtries > 0) {
                   2170:                 &decrement($symb,$new_part,\%aggregate,$aggtries,$totaltries,$solvedstatus);
                   2171:                 $aggregateflag = 1;
                   2172:             }
1.125     ng       2173: 	} elsif ($dropMenu eq '') {
1.259     banghart 2174: 	    $pts = ($env{'form.GD_BOX'.$newflg.'_'.$new_part} ne '' ? 
                   2175: 		    $env{'form.GD_BOX'.$newflg.'_'.$new_part} : 
                   2176: 		    $env{'form.RADVAL'.$newflg.'_'.$new_part});
                   2177: 	    if ($pts eq '' && $env{'form.GD_SEL'.$newflg.'_'.$new_part} eq '') {
1.153     albertel 2178: 		next;
                   2179: 	    }
1.259     banghart 2180: 	    $wgt = $env{'form.WGT'.$newflg.'_'.$new_part} eq '' ? 1 : 
                   2181: 		$env{'form.WGT'.$newflg.'_'.$new_part};
1.41      ng       2182: 	    my $partial= $pts/$wgt;
1.259     banghart 2183: 	    if ($partial eq $record{'resource.'.$new_part.'.awarded'}) {
1.153     albertel 2184: 		#do not update score for part if not changed.
                   2185: 		next;
1.251     banghart 2186: 	    } else {
1.259     banghart 2187: 	        push @parts_graded, $new_part;
1.153     albertel 2188: 	    }
1.259     banghart 2189: 	    if ($record{'resource.'.$new_part.'.awarded'} ne $partial) {
                   2190: 		$newrecord{'resource.'.$new_part.'.awarded'}  = $partial;
1.153     albertel 2191: 	    }
1.259     banghart 2192: 	    my $reckey = 'resource.'.$new_part.'.solved';
1.41      ng       2193: 	    if ($partial == 0) {
1.153     albertel 2194: 		if ($record{$reckey} ne 'incorrect_by_override') {
                   2195: 		    $newrecord{$reckey} = 'incorrect_by_override';
                   2196: 		}
1.41      ng       2197: 	    } else {
1.153     albertel 2198: 		if ($record{$reckey} ne 'correct_by_override') {
                   2199: 		    $newrecord{$reckey} = 'correct_by_override';
                   2200: 		}
                   2201: 	    }	    
                   2202: 	    if ($submitter && 
1.259     banghart 2203: 		($record{'resource.'.$new_part.'.submitted_by'} ne $submitter)) {
                   2204: 		$newrecord{'resource.'.$new_part.'.submitted_by'} = $submitter;
1.41      ng       2205: 	    }
1.259     banghart 2206: 	    $newrecord{'resource.'.$new_part.'.regrader'}=
1.257     albertel 2207: 		"$env{'user.name'}:$env{'user.domain'}";
1.41      ng       2208: 	}
1.259     banghart 2209: 	# unless problem has been graded, set flag to version the submitted files
                   2210: 	unless ($record{'resource.'.$new_part.'.solved'} =~ /^correct_/  || $record{'resource.'.$new_part.'.solved'} eq 'incorrect_by_override') {
1.262     banghart 2211: 	    push (@v_flag,$new_part);
1.259     banghart 2212: 	}
1.41      ng       2213:     }
1.44      ng       2214:     if (scalar(keys(%newrecord)) > 0) {
1.263     banghart 2215:         if (scalar(@v_flag)) {
                   2216:             &version_portfiles(\%record, \@parts_graded, $env{'request.course.id'}, $symb, $domain, $stuname, \@v_flag);
1.259     banghart 2217:         }
1.44      ng       2218: 	&Apache::lonnet::cstore(\%newrecord,$symb,
1.257     albertel 2219: 				$env{'request.course.id'},$domain,$stuname);
1.41      ng       2220:     }
1.269     raeburn  2221:     if ($aggregateflag) {
                   2222:         &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
                   2223:                   $env{'course.'.$env{'request.course.id'}.'.domain'},
                   2224:                   $env{'course.'.$env{'request.course.id'}.'.num'});
                   2225:     }
1.77      ng       2226:     return '',$pts,$wgt;
1.36      ng       2227: }
1.38      ng       2228: 
1.269     raeburn  2229: # ----------- Provides number of tries since last reset.
                   2230: sub get_num_tries {
                   2231:     my ($record,$last_reset,$part) = @_;
                   2232:     my $timestamp = '';
                   2233:     my $num_tries = 0;
                   2234:     if ($$record{'version'}) {
                   2235:         for (my $version=$$record{'version'};$version>=1;$version--) {
                   2236:             if (exists($$record{$version.':resource.'.$part.'.solved'})) {
                   2237:                 $timestamp = $$record{$version.':timestamp'};
                   2238:                 if ($timestamp > $last_reset) {
                   2239:                     $num_tries ++;
                   2240:                 } else {
                   2241:                     last;
                   2242:                 }
                   2243:             }
                   2244:         }
                   2245:     }
                   2246:     return $num_tries;
                   2247: }
                   2248: 
                   2249: # ----------- Determine decrements required in aggregate totals 
                   2250: sub decrement_aggs {
                   2251:     my ($symb,$part,$aggregate,$aggtries,$totaltries,$solvedstatus) = @_;
                   2252:     my %decrement = (
                   2253:                         attempts => 0,
                   2254:                         users => 0,
                   2255:                         correct => 0
                   2256:                     );
                   2257:     $decrement{'attempts'} = $aggtries;
                   2258:     if ($solvedstatus =~ /^correct/) {
                   2259:         $decrement{'correct'} = 1;
                   2260:     }
                   2261:     if ($aggtries == $totaltries) {
                   2262:         $decrement{'users'} = 1;
                   2263:     }
                   2264:     foreach my $type (keys (%decrement)) {
                   2265:         $$aggregate{$symb."\0".$part."\0".$type} = -$decrement{$type};
                   2266:     }
                   2267:     return;
                   2268: }
                   2269: 
                   2270: # ----------- Determine timestamps for last reset of aggregate totals for parts  
                   2271: sub get_last_resets {
1.270     albertel 2272:     my ($symb,$courseid,$partids) =@_;
                   2273:     my %last_resets;
1.269     raeburn  2274:     my $cdom = $env{'course.'.$courseid.'.domain'};
                   2275:     my $cname = $env{'course.'.$courseid.'.num'};
1.271     albertel 2276:     my @keys;
                   2277:     foreach my $part (@{$partids}) {
                   2278: 	push(@keys,"$symb\0$part\0resettime");
                   2279:     }
                   2280:     my %results=&Apache::lonnet::get('nohist_resourcetracker',\@keys,
                   2281: 				     $cdom,$cname);
                   2282:     foreach my $part (@{$partids}) {
                   2283: 	$last_resets{$part}=$results{"$symb\0$part\0resettime"};
1.269     raeburn  2284:     }
1.270     albertel 2285:     return %last_resets;
1.269     raeburn  2286: }
                   2287: 
1.251     banghart 2288: # ----------- Handles creating versions for portfolio files as answers
                   2289: sub version_portfiles {
1.263     banghart 2290:     my ($record, $parts_graded, $courseid, $symb, $domain, $stuname, $v_flag) = @_;
                   2291:     my $version_parts = join('|',@$v_flag);
1.255     banghart 2292:     my $parts = join('|', @$parts_graded);
1.252     banghart 2293:     my $portfolio_root = &Apache::loncommon::propath($domain,
                   2294: 						 $stuname).
                   2295: 						'/userfiles/portfolio';
1.255     banghart 2296:     foreach my $key(keys %$record) {
1.259     banghart 2297:         my $new_portfiles;
1.263     banghart 2298:         if ($key =~ /^resource\.($version_parts)\./ && $key =~ /\.portfiles$/ ) {
1.259     banghart 2299:             my @v_portfiles;
1.255     banghart 2300:             my @portfiles = split(/,/,$$record{$key});
1.264     banghart 2301:             # &Apache::lonnet::logthis("should be unmarking and remarking");
1.252     banghart 2302:             foreach my $file (@portfiles) {
1.264     banghart 2303:                 &Apache::lonnet::unmark_as_readonly($domain,$stuname,[$symb,$env{'request.course.id'}],$file);
1.255     banghart 2304:                 my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*$)/);
1.252     banghart 2305:                 my $version = 0;
1.255     banghart 2306:                 my @answer_file_parts = split(/\./, $answer_file);
1.252     banghart 2307:                 my @dir_list = &Apache::lonnet::dirlist($directory,$domain,$stuname,$portfolio_root);
                   2308:                 my @file_names;
                   2309:                 my @file_name_parts;
1.253     banghart 2310:                 foreach my $row (@dir_list) {
1.255     banghart 2311:                     @file_names = split(/\&/,$row,2);
                   2312:                     @file_name_parts = split(/\./, $file_names[0]);
1.252     banghart 2313:                     # ($file_name_parts[scalar @file_name_parts] eq $answer_file_parts[scalar @answer_file_parts])
                   2314:                     if (($file_name_parts[0] eq $answer_file_parts[0]) && 
1.253     banghart 2315:                         ($file_name_parts[-1] eq $answer_file_parts[-1])) {
1.252     banghart 2316:                         # gets here if filename and extension match, regardless of version
                   2317:                         if (scalar @file_name_parts == 3) { # a versioned file is found
                   2318:                             # so save it for later
                   2319:                             if ($file_name_parts[1] > $version) {$version = $file_name_parts[1]};
                   2320:                         }
                   2321:                     }
                   2322:                 }
1.255     banghart 2323:                 $version++;
1.256     banghart 2324:                 my $home_server = &Apache::lonnet::homeserver($stuname,$domain,undef);
1.259     banghart 2325:                 $env{'form.copy'} = &Apache::lonnet::getfile("/uploaded/$domain/$stuname/portfolio$directory$answer_file");
                   2326:                 if($env{'form.copy'} eq '-1') {
                   2327:                     &Apache::lonnet::logthis('problem getting file '.$directory.$answer_file);
                   2328:                 } else {
                   2329:                    my $copy_result = &Apache::lonnet::finishuserfileupload($stuname,$domain,$home_server,'copy',
1.256     banghart 2330:                                     '/portfolio'.$directory.$answer_file_parts[0].'.'.$version.'.'.$answer_file_parts[-1]);
1.259     banghart 2331:                     push(@v_portfiles, $answer_file_parts[0].'.'.$version.'.'.$answer_file_parts[-1]);
1.264     banghart 2332:                     &Apache::lonnet::mark_as_readonly($domain,$stuname,
                   2333:                                 ['/portfolio'.$directory.$answer_file_parts[0].'.'.$version.'.'.$answer_file_parts[-1]],
                   2334:                                 [$symb,$env{'request.course.id'},'graded']);
1.259     banghart 2335:                 }
1.252     banghart 2336:             }
1.261     albertel 2337:             $$record{$key} = join(',',@v_portfiles);
1.251     banghart 2338:         }
                   2339:     } 
1.259     banghart 2340:     return 'ok';   
1.251     banghart 2341:     
                   2342: }
                   2343: 
1.44      ng       2344: #--------------------------------------------------------------------------------------
                   2345: #
                   2346: #-------------------------- Next few routines handles grading by section or whole class
                   2347: #
                   2348: #--- Javascript to handle grading by section or whole class
1.42      ng       2349: sub viewgrades_js {
                   2350:     my ($request) = shift;
                   2351: 
1.41      ng       2352:     $request->print(<<VIEWJAVASCRIPT);
                   2353: <script type="text/javascript" language="javascript">
1.45      ng       2354:    function writePoint(partid,weight,point) {
1.125     ng       2355: 	var radioButton = document.classgrade["RADVAL_"+partid];
                   2356: 	var textbox = document.classgrade["TEXTVAL_"+partid];
1.42      ng       2357: 	if (point == "textval") {
1.125     ng       2358: 	    point = document.classgrade["TEXTVAL_"+partid].value;
1.109     matthew  2359: 	    if (isNaN(point) || parseFloat(point) < 0) {
                   2360: 		alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point));
1.42      ng       2361: 		var resetbox = false;
                   2362: 		for (var i=0; i<radioButton.length; i++) {
                   2363: 		    if (radioButton[i].checked) {
                   2364: 			textbox.value = i;
                   2365: 			resetbox = true;
                   2366: 		    }
                   2367: 		}
                   2368: 		if (!resetbox) {
                   2369: 		    textbox.value = "";
                   2370: 		}
                   2371: 		return;
                   2372: 	    }
1.109     matthew  2373: 	    if (parseFloat(point) > parseFloat(weight)) {
                   2374: 		var resp = confirm("You entered a value ("+parseFloat(point)+
1.44      ng       2375: 				   ") greater than the weight for the part. Accept?");
                   2376: 		if (resp == false) {
                   2377: 		    textbox.value = "";
                   2378: 		    return;
                   2379: 		}
                   2380: 	    }
1.42      ng       2381: 	    for (var i=0; i<radioButton.length; i++) {
                   2382: 		radioButton[i].checked=false;
1.109     matthew  2383: 		if (parseFloat(point) == i) {
1.42      ng       2384: 		    radioButton[i].checked=true;
                   2385: 		}
                   2386: 	    }
1.41      ng       2387: 
1.42      ng       2388: 	} else {
1.125     ng       2389: 	    textbox.value = parseFloat(point);
1.42      ng       2390: 	}
1.41      ng       2391: 	for (i=0;i<document.classgrade.total.value;i++) {
1.125     ng       2392: 	    var user = document.classgrade["ctr"+i].value;
                   2393: 	    var scorename = document.classgrade["GD_"+user+"_"+partid+"_awarded"];
                   2394: 	    var saveval   = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value;
                   2395: 	    var selname   = document.classgrade["GD_"+user+"_"+partid+"_solved"];
1.42      ng       2396: 	    if (saveval != "correct") {
                   2397: 		scorename.value = point;
1.43      ng       2398: 		if (selname[0].selected != true) {
                   2399: 		    selname[0].selected = true;
                   2400: 		}
1.42      ng       2401: 	    }
                   2402: 	}
1.125     ng       2403: 	document.classgrade["SELVAL_"+partid][0].selected = true;
1.42      ng       2404:     }
                   2405: 
                   2406:     function writeRadText(partid,weight) {
1.125     ng       2407: 	var selval   = document.classgrade["SELVAL_"+partid];
                   2408: 	var radioButton = document.classgrade["RADVAL_"+partid];
1.265     www      2409:         var override = document.classgrade["FORCE_"+partid].checked;
1.125     ng       2410: 	var textbox = document.classgrade["TEXTVAL_"+partid];
                   2411: 	if (selval[1].selected || selval[2].selected) {
1.42      ng       2412: 	    for (var i=0; i<radioButton.length; i++) {
                   2413: 		radioButton[i].checked=false;
                   2414: 
                   2415: 	    }
                   2416: 	    textbox.value = "";
                   2417: 
                   2418: 	    for (i=0;i<document.classgrade.total.value;i++) {
1.125     ng       2419: 		var user = document.classgrade["ctr"+i].value;
                   2420: 		var scorename = document.classgrade["GD_"+user+"_"+partid+"_awarded"];
                   2421: 		var saveval   = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value;
                   2422: 		var selname   = document.classgrade["GD_"+user+"_"+partid+"_solved"];
1.265     www      2423: 		if ((saveval != "correct") || override) {
1.42      ng       2424: 		    scorename.value = "";
1.125     ng       2425: 		    if (selval[1].selected) {
                   2426: 			selname[1].selected = true;
                   2427: 		    } else {
                   2428: 			selname[2].selected = true;
                   2429: 			if (Number(document.classgrade["GD_"+user+"_"+partid+"_tries"].value)) 
                   2430: 			{document.classgrade["GD_"+user+"_"+partid+"_tries"].value = '0';}
                   2431: 		    }
1.42      ng       2432: 		}
                   2433: 	    }
1.43      ng       2434: 	} else {
                   2435: 	    for (i=0;i<document.classgrade.total.value;i++) {
1.125     ng       2436: 		var user = document.classgrade["ctr"+i].value;
                   2437: 		var scorename = document.classgrade["GD_"+user+"_"+partid+"_awarded"];
                   2438: 		var saveval   = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value;
                   2439: 		var selname   = document.classgrade["GD_"+user+"_"+partid+"_solved"];
1.265     www      2440: 		if ((saveval != "correct") || override) {
1.125     ng       2441: 		    scorename.value = document.classgrade["GD_"+user+"_"+partid+"_awarded_s"].value;
1.43      ng       2442: 		    selname[0].selected = true;
                   2443: 		}
                   2444: 	    }
                   2445: 	}	    
1.42      ng       2446:     }
                   2447: 
                   2448:     function changeSelect(partid,user) {
1.125     ng       2449: 	var selval = document.classgrade["GD_"+user+'_'+partid+"_solved"];
                   2450: 	var textbox = document.classgrade["GD_"+user+'_'+partid+"_awarded"];
1.44      ng       2451: 	var point  = textbox.value;
1.125     ng       2452: 	var weight = document.classgrade["weight_"+partid].value;
1.44      ng       2453: 
1.109     matthew  2454: 	if (isNaN(point) || parseFloat(point) < 0) {
                   2455: 	    alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point));
1.44      ng       2456: 	    textbox.value = "";
                   2457: 	    return;
                   2458: 	}
1.109     matthew  2459: 	if (parseFloat(point) > parseFloat(weight)) {
                   2460: 	    var resp = confirm("You entered a value ("+parseFloat(point)+
1.44      ng       2461: 			       ") greater than the weight of the part. Accept?");
                   2462: 	    if (resp == false) {
                   2463: 		textbox.value = "";
                   2464: 		return;
                   2465: 	    }
                   2466: 	}
1.42      ng       2467: 	selval[0].selected = true;
                   2468:     }
                   2469: 
                   2470:     function changeOneScore(partid,user) {
1.125     ng       2471: 	var selval = document.classgrade["GD_"+user+'_'+partid+"_solved"];
                   2472: 	if (selval[1].selected || selval[2].selected) {
                   2473: 	    document.classgrade["GD_"+user+'_'+partid+"_awarded"].value = "";
                   2474: 	    if (selval[2].selected) {
                   2475: 		document.classgrade["GD_"+user+'_'+partid+"_tries"].value = "0";
                   2476: 	    }
1.269     raeburn  2477:         }
1.42      ng       2478:     }
                   2479: 
                   2480:     function resetEntry(numpart) {
                   2481: 	for (ctpart=0;ctpart<numpart;ctpart++) {
1.125     ng       2482: 	    var partid = document.classgrade["partid_"+ctpart].value;
                   2483: 	    var radioButton = document.classgrade["RADVAL_"+partid];
                   2484: 	    var textbox = document.classgrade["TEXTVAL_"+partid];
                   2485: 	    var selval  = document.classgrade["SELVAL_"+partid];
1.42      ng       2486: 	    for (var i=0; i<radioButton.length; i++) {
                   2487: 		radioButton[i].checked=false;
                   2488: 
                   2489: 	    }
                   2490: 	    textbox.value = "";
                   2491: 	    selval[0].selected = true;
                   2492: 
                   2493: 	    for (i=0;i<document.classgrade.total.value;i++) {
1.125     ng       2494: 		var user = document.classgrade["ctr"+i].value;
                   2495: 		var resetscore = document.classgrade["GD_"+user+"_"+partid+"_awarded"];
                   2496: 		resetscore.value = document.classgrade["GD_"+user+"_"+partid+"_awarded_s"].value;
                   2497: 		var resettries = document.classgrade["GD_"+user+"_"+partid+"_tries"];
                   2498: 		resettries.value = document.classgrade["GD_"+user+"_"+partid+"_tries_s"].value;
                   2499: 		var saveselval   = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value;
                   2500: 		var selname   = document.classgrade["GD_"+user+"_"+partid+"_solved"];
1.42      ng       2501: 		if (saveselval == "excused") {
1.43      ng       2502: 		    if (selname[1].selected == false) { selname[1].selected = true;}
1.42      ng       2503: 		} else {
1.43      ng       2504: 		    if (selname[0].selected == false) {selname[0].selected = true};
1.42      ng       2505: 		}
                   2506: 	    }
1.41      ng       2507: 	}
1.42      ng       2508:     }
                   2509: 
1.41      ng       2510: </script>
                   2511: VIEWJAVASCRIPT
1.42      ng       2512: }
                   2513: 
1.44      ng       2514: #--- show scores for a section or whole class w/ option to change/update a score
1.42      ng       2515: sub viewgrades {
                   2516:     my ($request) = shift;
                   2517:     &viewgrades_js($request);
1.41      ng       2518: 
1.257     albertel 2519:     my ($symb,$url) = ($env{'form.symb'},$env{'form.url'}); 
1.168     albertel 2520:     #need to make sure we have the correct data for later EXT calls, 
                   2521:     #thus invalidate the cache
                   2522:     &Apache::lonnet::devalidatecourseresdata(
1.257     albertel 2523:                  $env{'course.'.$env{'request.course.id'}.'.num'},
                   2524:                  $env{'course.'.$env{'request.course.id'}.'.domain'});
1.168     albertel 2525:     &Apache::lonnet::clear_EXT_cache_status();
                   2526: 
1.167     sakharuk 2527:     my $result='<h3><font color="#339933">'.&mt('Manual Grading').'</font></h3>';
1.257     albertel 2528:     $result.='<font size=+1><b>Current Resource: </b>'.$env{'form.probTitle'}.'</font>'."\n";
1.41      ng       2529: 
                   2530:     #view individual student submission form - called using Javascript viewOneStudent
1.45      ng       2531:     $result.=&jscriptNform($url,$symb);
1.41      ng       2532: 
1.44      ng       2533:     #beginning of class grading form
1.41      ng       2534:     $result.= '<form action="/adm/grades" method="post" name="classgrade">'."\n".
1.106     albertel 2535: 	'<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
1.41      ng       2536: 	'<input type="hidden" name="url"     value="'.$url.'" />'."\n".
1.38      ng       2537: 	'<input type="hidden" name="command" value="editgrades" />'."\n".
1.257     albertel 2538: 	'<input type="hidden" name="section" value="'.$env{'form.section'}.'" />'."\n".
                   2539: 	'<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".
                   2540: 	'<input type="hidden" name="Status" value="'.$env{'form.Status'}.'" />'."\n".
                   2541: 	'<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n";
1.72      ng       2542: 
1.126     ng       2543:     my $sectionClass;
1.257     albertel 2544:     if ($env{'form.section'} eq 'all') {
1.126     ng       2545: 	$sectionClass='Class </h3>';
1.257     albertel 2546:     } elsif ($env{'form.section'} eq 'none') {
1.126     ng       2547: 	$sectionClass='Students in no Section </h3>';
1.52      albertel 2548:     } else {
1.257     albertel 2549: 	$sectionClass='Students in Section '.$env{'form.section'}.'</h3>';
1.52      albertel 2550:     }
1.126     ng       2551:     $result.='<h3>Assign Common Grade To '.$sectionClass;
1.52      albertel 2552:     $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".
                   2553: 	'<table border=0><tr bgcolor="#ffffdd"><td>';
1.44      ng       2554:     #radio buttons/text box for assigning points for a section or class.
                   2555:     #handles different parts of a problem
1.125     ng       2556:     my ($partlist,$handgrade) = &response_type($url,$symb);
1.42      ng       2557:     my %weight = ();
                   2558:     my $ctsparts = 0;
1.41      ng       2559:     $result.='<table border="0">';
1.45      ng       2560:     my %seen = ();
1.42      ng       2561:     for (sort keys(%$handgrade)) {
1.54      albertel 2562: 	my ($partid,$respid) = split (/_/,$_,2);
1.45      ng       2563: 	next if $seen{$partid};
                   2564: 	$seen{$partid}++;
1.147     albertel 2565: 	my $handgrade=$$handgrade{$_};
1.42      ng       2566: 	my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);
                   2567: 	$weight{$partid} = $wgt eq '' ? '1' : $wgt;
                   2568: 
1.44      ng       2569: 	$result.='<input type="hidden" name="partid_'.
                   2570: 	    $ctsparts.'" value="'.$partid.'" />'."\n";
                   2571: 	$result.='<input type="hidden" name="weight_'.
                   2572: 	    $partid.'" value="'.$weight{$partid}.'" />'."\n";
1.207     albertel 2573: 	my $display_part=&get_display_part($partid,$url,$symb);
                   2574: 	$result.='<tr><td><b>Part:</b> '.$display_part.'&nbsp; &nbsp;<b>Point:</b> </td><td>';
1.42      ng       2575: 	$result.='<table border="0"><tr>';  
1.41      ng       2576: 	my $ctr = 0;
1.42      ng       2577: 	while ($ctr<=$weight{$partid}) { # display radio buttons in a nice table 10 across
                   2578: 	    $result.= '<td><input type="radio" name="RADVAL_'.$partid.'" '.
1.54      albertel 2579: 		'onclick="javascript:writePoint(\''.$partid.'\','.$weight{$partid}.
1.41      ng       2580: 		','.$ctr.')" />'.$ctr."</td>\n";
                   2581: 	    $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
                   2582: 	    $ctr++;
                   2583: 	}
                   2584: 	$result.='</tr></table>';
1.44      ng       2585: 	$result.= '</td><td><b> or </b><input type="text" name="TEXTVAL_'.
1.54      albertel 2586: 	    $partid.'" size="4" '.'onChange="javascript:writePoint(\''.
                   2587: 		$partid.'\','.$weight{$partid}.',\'textval\')" /> /'.
1.42      ng       2588: 	    $weight{$partid}.' (problem weight)</td>'."\n";
                   2589: 	$result.= '</td><td><select name="SELVAL_'.$partid.'"'.
1.54      albertel 2590: 	    'onChange="javascript:writeRadText(\''.$partid.'\','.
1.59      albertel 2591: 		$weight{$partid}.')"> '.
1.42      ng       2592: 	    '<option selected="on"> </option>'.
1.125     ng       2593: 	    '<option>excused</option>'.
1.265     www      2594: 	    '<option>reset status</option></select></td>'.
1.266     albertel 2595:             '<td><label><input type="checkbox" name="FORCE_'.$partid.'" /> Override "Correct"</label></td></tr>'."\n";
1.42      ng       2596: 	$ctsparts++;
1.41      ng       2597:     }
1.52      albertel 2598:     $result.='</table>'.'</td></tr></table>'.'</td></tr></table>'."\n".
                   2599: 	'<input type="hidden" name="totalparts" value="'.$ctsparts.'" />';
1.42      ng       2600:     $result.='<input type="button" value="Reset" '.
1.111     ng       2601: 	'onClick="javascript:resetEntry('.$ctsparts.');" TARGET=_self>';
1.41      ng       2602: 
1.44      ng       2603:     #table listing all the students in a section/class
                   2604:     #header of table
1.126     ng       2605:     $result.= '<h3>Assign Grade to Specific Students in '.$sectionClass;
1.42      ng       2606:     $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".
1.126     ng       2607: 	'<table border=0><tr bgcolor="#deffff"><td>&nbsp;<b>No.</b>&nbsp;</td>'.
1.129     ng       2608: 	'<td>'.&nameUserString('header')."</td>\n";
1.146     albertel 2609:     my (@parts) = sort(&getpartlist($url,$symb));
1.269     raeburn  2610:     my @partids = ();
1.41      ng       2611:     foreach my $part (@parts) {
                   2612: 	my $display=&Apache::lonnet::metadata($url,$part.'.display');
1.126     ng       2613: 	$display =~ s|^Number of Attempts|Tries<br />|; # makes the column narrower
1.41      ng       2614: 	if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
1.207     albertel 2615: 	my ($partid) = &split_part_type($part);
1.269     raeburn  2616:         push(@partids, $partid);
1.207     albertel 2617: 	my $display_part=&get_display_part($partid,$url,$symb);
1.41      ng       2618: 	if ($display =~ /^Partial Credit Factor/) {
1.207     albertel 2619: 	    $result.='<td><b>Score Part:</b> '.$display_part.
                   2620: 		' <br /><b>(weight = '.$weight{$partid}.')</b></td>'."\n";
1.41      ng       2621: 	    next;
1.207     albertel 2622: 	} else {
                   2623: 	    $display =~s/\[Part: \Q$partid\E\]/Part:<\/b> $display_part/;
1.41      ng       2624: 	}
1.53      albertel 2625: 	$display =~ s|Problem Status|Grade Status<br />|;
1.207     albertel 2626: 	$result.='<td><b>'.$display.'</td>'."\n";
1.41      ng       2627:     }
                   2628:     $result.='</tr>';
1.44      ng       2629: 
1.270     albertel 2630:     my %last_resets = 
                   2631: 	&get_last_resets($symb,$env{'request.course.id'},\@partids);
1.269     raeburn  2632: 
1.41      ng       2633:     #get info for each student
1.44      ng       2634:     #list all the students - with points and grade status
1.257     albertel 2635:     my (undef,undef,$fullname) = &getclasslist($env{'form.section'},'1');
1.41      ng       2636:     my $ctr = 0;
1.53      albertel 2637:     foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
1.126     ng       2638: 	$ctr++;
1.257     albertel 2639: 	$result.=&viewstudentgrade($url,$symb,$env{'request.course.id'},
1.269     raeburn  2640: 				   $_,$$fullname{$_},\@parts,\%weight,$ctr,\%last_resets);
1.41      ng       2641:     }
                   2642:     $result.='</table></td></tr></table>';
                   2643:     $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n";
1.126     ng       2644:     $result.='<input type="button" value="Save" '.
1.45      ng       2645: 	'onClick="javascript:submit();" TARGET=_self /></form>'."\n";
1.96      albertel 2646:     if (scalar(%$fullname) eq 0) {
                   2647: 	my $colspan=3+scalar(@parts);
1.257     albertel 2648: 	$result='<font color="red">There are no students in section "'.$env{'form.section'}.
                   2649: 	    '" with enrollment status "'.$env{'form.Status'}.'" to modify or grade.</font>';
1.96      albertel 2650:     }
1.41      ng       2651:     $result.=&show_grading_menu_form($symb,$url);
                   2652:     return $result;
                   2653: }
                   2654: 
1.44      ng       2655: #--- call by previous routine to display each student
1.41      ng       2656: sub viewstudentgrade {
1.269     raeburn  2657:     my ($url,$symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets) = @_;
1.44      ng       2658:     my ($uname,$udom) = split(/:/,$student);
1.90      albertel 2659:     $student=~s/:/_/;
1.44      ng       2660:     my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);
1.269     raeburn  2661:     my %aggregates = (); 
1.233     albertel 2662:     my $result='<tr bgcolor="#ffffdd"><td align="right">'.
                   2663: 	'<input type="hidden" name="ctr'.($ctr-1).'" value="'.$student.'" />'.
                   2664: 	"\n".$ctr.'&nbsp;</td><td>&nbsp;'.
1.44      ng       2665: 	'<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.
1.112     ng       2666: 	'\')"; TARGET=_self>'.$fullname.'</a> '.
1.257     albertel 2667: 	'<font color="#999999">('.$uname.($env{'user.domain'} eq $udom ? '' : ':'.$udom).')</font></td>'."\n";
1.63      albertel 2668:     foreach my $apart (@$parts) {
                   2669: 	my ($part,$type) = &split_part_type($apart);
1.41      ng       2670: 	my $score=$record{"resource.$part.$type"};
1.269     raeburn  2671:         $result.='<td align="middle">';
                   2672:         my ($aggtries,$totaltries);
                   2673:         unless (exists($aggregates{$part})) {
1.270     albertel 2674: 	    $totaltries = $record{'resource.'.$part.'.tries'};
                   2675: 
                   2676: 	    $aggtries = $totaltries;
1.269     raeburn  2677:             if ($$last_resets{$part}) {  
1.270     albertel 2678:                 $aggtries = &get_num_tries(\%record,$$last_resets{$part},
                   2679: 					   $part);
                   2680:             }
1.269     raeburn  2681:             $result.='<input type="hidden" name="'.
                   2682:                 'GD_'.$student.'_'.$part.'_aggtries" value="'.$aggtries.'" />'."\n";
                   2683:             $result.='<input type="hidden" name="'.
                   2684:                 'GD_'.$student.'_'.$part.'_totaltries" value="'.$totaltries.'" />'."\n";
                   2685:             $aggregates{$part} = 1;
                   2686:         }
1.41      ng       2687: 	if ($type eq 'awarded') {
1.42      ng       2688: 	    my $pts = $score eq '' ? '' : $score*$$weight{$part};
                   2689: 	    $result.='<input type="hidden" name="'.
1.89      albertel 2690: 		'GD_'.$student.'_'.$part.'_awarded_s" value="'.$pts.'" />'."\n";
1.233     albertel 2691: 	    $result.='<input type="text" name="'.
1.89      albertel 2692: 		'GD_'.$student.'_'.$part.'_awarded" '.
                   2693: 		'onChange="javascript:changeSelect(\''.$part.'\',\''.$student.
1.44      ng       2694: 		'\')" value="'.$pts.'" size="4" /></td>'."\n";
1.41      ng       2695: 	} elsif ($type eq 'solved') {
                   2696: 	    my ($status,$foo)=split(/_/,$score,2);
                   2697: 	    $status = 'nothing' if ($status eq '');
1.89      albertel 2698: 	    $result.='<input type="hidden" name="'.'GD_'.$student.'_'.
1.54      albertel 2699: 		$part.'_solved_s" value="'.$status.'" />'."\n";
1.233     albertel 2700: 	    $result.='&nbsp;<select name="'.
1.89      albertel 2701: 		'GD_'.$student.'_'.$part.'_solved" '.
                   2702: 		'onChange="javascript:changeOneScore(\''.$part.'\',\''.$student.'\')" >'."\n";
1.125     ng       2703: 	    $result.= (($status eq 'excused') ? '<option> </option><option selected="on">excused</option>' 
                   2704: 		: '<option selected="on"> </option><option>excused</option>')."\n";
                   2705: 	    $result.='<option>reset status</option>';
1.126     ng       2706: 	    $result.="</select>&nbsp;</td>\n";
1.122     ng       2707: 	} else {
                   2708: 	    $result.='<input type="hidden" name="'.
                   2709: 		'GD_'.$student.'_'.$part.'_'.$type.'_s" value="'.$score.'" />'.
                   2710: 		    "\n";
1.233     albertel 2711: 	    $result.='<input type="text" name="'.
1.122     ng       2712: 		'GD_'.$student.'_'.$part.'_'.$type.'" '.
                   2713: 		'value="'.$score.'" size="4" /></td>'."\n";
1.41      ng       2714: 	}
                   2715:     }
                   2716:     $result.='</tr>';
                   2717:     return $result;
1.38      ng       2718: }
                   2719: 
1.44      ng       2720: #--- change scores for all the students in a section/class
                   2721: #    record does not get update if unchanged
1.38      ng       2722: sub editgrades {
1.41      ng       2723:     my ($request) = @_;
                   2724: 
1.257     albertel 2725:     my $symb=$env{'form.symb'};
                   2726:     my $url =$env{'form.url'};
1.45      ng       2727:     my $title='<h3><font color="#339933">Current Grade Status</font></h3>';
1.257     albertel 2728:     $title.='<font size=+1><b>Current Resource: </b>'.$env{'form.probTitle'}.'</font><br />'."\n";
                   2729:     $title.='<font size=+1><b>Section: </b>'.$env{'form.section'}.'</font>'."\n";
1.126     ng       2730: 
1.44      ng       2731:     my $result= '<table border="0"><tr><td bgcolor="#777777">'."\n";
1.129     ng       2732:     $result.= '<table border="0"><tr bgcolor="#deffff">'.
                   2733: 	'<td rowspan=2 valign="center">&nbsp;<b>No.</b>&nbsp;</td>'.
                   2734: 	'<td rowspan=2 valign="center">'.&nameUserString('header')."</td>\n";
1.43      ng       2735: 
                   2736:     my %scoreptr = (
                   2737: 		    'correct'  =>'correct_by_override',
                   2738: 		    'incorrect'=>'incorrect_by_override',
                   2739: 		    'excused'  =>'excused',
                   2740: 		    'ungraded' =>'ungraded_attempted',
                   2741: 		    'nothing'  => '',
                   2742: 		    );
1.257     albertel 2743:     my ($classlist,undef,$fullname) = &getclasslist($env{'form.section'},'0');
1.34      ng       2744: 
1.44      ng       2745:     my (@partid);
                   2746:     my %weight = ();
1.54      albertel 2747:     my %columns = ();
1.44      ng       2748:     my ($i,$ctr,$count,$rec_update) = (0,0,0,0);
1.54      albertel 2749: 
1.146     albertel 2750:     my (@parts) = sort(&getpartlist($url,$symb));
1.54      albertel 2751:     my $header;
1.257     albertel 2752:     while ($ctr < $env{'form.totalparts'}) {
                   2753: 	my $partid = $env{'form.partid_'.$ctr};
1.44      ng       2754: 	push @partid,$partid;
1.257     albertel 2755: 	$weight{$partid} = $env{'form.weight_'.$partid};
1.44      ng       2756: 	$ctr++;
1.54      albertel 2757:     }
                   2758:     foreach my $partid (@partid) {
                   2759: 	$header .= '<td align="center">&nbsp;<b>Old Score</b>&nbsp;</td>'.
                   2760: 	    '<td align="center">&nbsp;<b>New Score</b>&nbsp;</td>';
                   2761: 	$columns{$partid}=2;
                   2762: 	foreach my $stores (@parts) {
                   2763: 	    my ($part,$type) = &split_part_type($stores);
                   2764: 	    if ($part !~ m/^\Q$partid\E/) { next;}
                   2765: 	    if ($type eq 'awarded' || $type eq 'solved') { next; }
                   2766: 	    my $display=&Apache::lonnet::metadata($url,$stores.'.display');
                   2767: 	    $display =~ s/\[Part: (\w)+\]//;
1.125     ng       2768: 	    $display =~ s/Number of Attempts/Tries/;
                   2769: 	    $header .= '<td align="center">&nbsp;<b>Old '.$display.'</b>&nbsp;</td>'.
                   2770: 		'<td align="center">&nbsp;<b>New '.$display.'</b>&nbsp;</td>';
1.54      albertel 2771: 	    $columns{$partid}+=2;
                   2772: 	}
                   2773:     }
                   2774:     foreach my $partid (@partid) {
1.207     albertel 2775: 	my $display_part=&get_display_part($partid,$url,$symb);
1.54      albertel 2776: 	$result .= '<td colspan="'.$columns{$partid}.
1.207     albertel 2777: 	    '" align="center"><b>Part:</b> '.$display_part.
                   2778: 	    ' (Weight = '.$weight{$partid}.')</td>';
1.54      albertel 2779: 
1.44      ng       2780:     }
                   2781:     $result .= '</tr><tr bgcolor="#deffff">';
1.54      albertel 2782:     $result .= $header;
1.44      ng       2783:     $result .= '</tr>'."\n";
1.93      albertel 2784:     my $noupdate;
1.126     ng       2785:     my ($updateCtr,$noupdateCtr) = (1,1);
1.257     albertel 2786:     for ($i=0; $i<$env{'form.total'}; $i++) {
1.93      albertel 2787: 	my $line;
1.257     albertel 2788: 	my $user = $env{'form.ctr'.$i};
1.92      albertel 2789: 	my $usercolon = $user;
                   2790: 	$usercolon =~s/_/:/;
                   2791: 	my ($uname,$udom)=split(/_/,$user);
1.44      ng       2792: 	my %newrecord;
                   2793: 	my $updateflag = 0;
1.129     ng       2794: 	$line .= '<td>'.&nameUserString(undef,$$fullname{$usercolon},$uname,$udom).'</td>';
1.108     albertel 2795: 	my $usec=$classlist->{"$uname:$udom"}[5];
1.105     albertel 2796: 	if (!&canmodify($usec)) {
1.126     ng       2797: 	    my $numcols=scalar(@partid)*4+2;
1.105     albertel 2798: 	    $noupdate.=$line."<td colspan=\"$numcols\"><font color=\"red\">Not allowed to modify student</font></td></tr>";
                   2799: 	    next;
                   2800: 	}
1.269     raeburn  2801:         my %aggregate = ();
                   2802:         my $aggregateflag = 0;
1.44      ng       2803: 	foreach (@partid) {
1.257     albertel 2804: 	    my $old_aw    = $env{'form.GD_'.$user.'_'.$_.'_awarded_s'};
1.54      albertel 2805: 	    my $old_part_pcr = $old_aw/($weight{$_} ne '0' ? $weight{$_}:1);
                   2806: 	    my $old_part  = $old_aw eq '' ? '' : $old_part_pcr;
1.257     albertel 2807: 	    my $old_score = $scoreptr{$env{'form.GD_'.$user.'_'.$_.'_solved_s'}};
                   2808: 	    my $awarded   = $env{'form.GD_'.$user.'_'.$_.'_awarded'};
1.54      albertel 2809: 	    my $pcr       = $awarded/($weight{$_} ne '0' ? $weight{$_} : 1);
                   2810: 	    my $partial   = $awarded eq '' ? '' : $pcr;
1.44      ng       2811: 	    my $score;
                   2812: 	    if ($partial eq '') {
1.257     albertel 2813: 		$score = $scoreptr{$env{'form.GD_'.$user.'_'.$_.'_solved_s'}};
1.44      ng       2814: 	    } elsif ($partial > 0) {
                   2815: 		$score = 'correct_by_override';
                   2816: 	    } elsif ($partial == 0) {
                   2817: 		$score = 'incorrect_by_override';
                   2818: 	    }
1.257     albertel 2819: 	    my $dropMenu = $env{'form.GD_'.$user.'_'.$_.'_solved'};
1.125     ng       2820: 	    $score = 'excused' if (($dropMenu eq 'excused') && ($score ne 'excused'));
                   2821: 
                   2822: 	    if ($dropMenu eq 'reset status' &&
                   2823: 		$old_score ne '') { # ignore if no previous attempts => nothing to reset
                   2824: 		$newrecord{'resource.'.$_.'.tries'} = 0;
                   2825: 		$newrecord{'resource.'.$_.'.solved'} = '';
                   2826: 		$newrecord{'resource.'.$_.'.award'} = '';
                   2827: 		$newrecord{'resource.'.$_.'.awarded'} = 0;
1.257     albertel 2828: 		$newrecord{'resource.'.$_.'.regrader'}="$env{'user.name'}:$env{'user.domain'}";
1.125     ng       2829: 		$updateflag = 1;
1.269     raeburn  2830:                 if ($env{'form.GD_'.$user.'_'.$_.'_aggtries'} > 0) {
                   2831:                     my $aggtries = $env{'form.GD_'.$user.'_'.$_.'_aggtries'};
                   2832:                     my $totaltries = $env{'form.GD_'.$user.'_'.$_.'_totaltries'};
                   2833:                     my $solvedstatus = $env{'form.GD_'.$user.'_'.$_.'_solved_s'};
                   2834:                     &decrement_aggs($symb,$_,\%aggregate,$aggtries,$totaltries,$solvedstatus);
                   2835:                     $aggregateflag = 1;
                   2836:                 }
1.139     albertel 2837: 	    } elsif (!($old_part eq $partial && $old_score eq $score)) {
                   2838: 		$updateflag = 1;
                   2839: 		$newrecord{'resource.'.$_.'.awarded'}  = $partial if $partial ne '';
                   2840: 		$newrecord{'resource.'.$_.'.solved'}   = $score;
                   2841: 		$rec_update++;
1.125     ng       2842: 	    }
                   2843: 
1.93      albertel 2844: 	    $line .= '<td align="center">'.$old_aw.'&nbsp;</td>'.
1.44      ng       2845: 		'<td align="center">'.$awarded.
                   2846: 		($score eq 'excused' ? $score : '').'&nbsp;</td>';
1.5       albertel 2847: 
1.54      albertel 2848: 
                   2849: 	    my $partid=$_;
                   2850: 	    foreach my $stores (@parts) {
                   2851: 		my ($part,$type) = &split_part_type($stores);
                   2852: 		if ($part !~ m/^\Q$partid\E/) { next;}
                   2853: 		if ($type eq 'awarded' || $type eq 'solved') { next; }
1.257     albertel 2854: 		my $old_aw    = $env{'form.GD_'.$user.'_'.$part.'_'.$type.'_s'};
                   2855: 		my $awarded   = $env{'form.GD_'.$user.'_'.$part.'_'.$type};
1.54      albertel 2856: 		if ($awarded ne '' && $awarded ne $old_aw) {
                   2857: 		    $newrecord{'resource.'.$part.'.'.$type}= $awarded;
1.257     albertel 2858: 		    $newrecord{'resource.'.$part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}";
1.54      albertel 2859: 		    $updateflag=1;
                   2860: 		}
1.93      albertel 2861: 		$line .= '<td align="center">'.$old_aw.'&nbsp;</td>'.
1.54      albertel 2862: 		    '<td align="center">'.$awarded.'&nbsp;</td>';
                   2863: 	    }
1.44      ng       2864: 	}
1.93      albertel 2865: 	$line.='</tr>'."\n";
1.44      ng       2866: 	if ($updateflag) {
                   2867: 	    $count++;
1.257     albertel 2868: 	    &Apache::lonnet::cstore(\%newrecord,$symb,$env{'request.course.id'},
1.89      albertel 2869: 				    $udom,$uname);
1.126     ng       2870: 	    $result.='<tr bgcolor="#ffffde"><td align="right">&nbsp;'.$updateCtr.'&nbsp;</td>'.$line;
                   2871: 	    $updateCtr++;
1.93      albertel 2872: 	} else {
1.126     ng       2873: 	    $noupdate.='<tr bgcolor="#ffffde"><td align="right">&nbsp;'.$noupdateCtr.'&nbsp;</td>'.$line;
                   2874: 	    $noupdateCtr++;
1.44      ng       2875: 	}
1.269     raeburn  2876:         if ($aggregateflag) {
                   2877:             &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
                   2878:                       $env{'course.'.$env{'request.course.id'}.'.domain'},
                   2879:                       $env{'course.'.$env{'request.course.id'}.'.num'});
                   2880:         }
1.93      albertel 2881:     }
                   2882:     if ($noupdate) {
1.126     ng       2883: #	my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3;
                   2884: 	my $numcols=scalar(@partid)*4+2;
1.204     albertel 2885: 	$result .= '<tr bgcolor="#ffffff"><td align="center" colspan="'.$numcols.'">No Changes Occurred For the Students Below</td></tr><tr bgcolor="#ffffde">'.$noupdate;
1.44      ng       2886:     }
1.72      ng       2887:     $result .= '</table></td></tr></table>'."\n".
                   2888: 	&show_grading_menu_form ($symb,$url);
1.125     ng       2889:     my $msg = '<br /><b>Number of records updated = '.$rec_update.
1.44      ng       2890: 	' for '.$count.' student'.($count <= 1 ? '' : 's').'.</b><br />'.
1.257     albertel 2891: 	'<b>Total number of students = '.$env{'form.total'}.'</b><br />';
1.44      ng       2892:     return $title.$msg.$result;
1.5       albertel 2893: }
1.54      albertel 2894: 
                   2895: sub split_part_type {
                   2896:     my ($partstr) = @_;
                   2897:     my ($temp,@allparts)=split(/_/,$partstr);
                   2898:     my $type=pop(@allparts);
                   2899:     my $part=join('.',@allparts);
                   2900:     return ($part,$type);
                   2901: }
                   2902: 
1.44      ng       2903: #------------- end of section for handling grading by section/class ---------
                   2904: #
                   2905: #----------------------------------------------------------------------------
                   2906: 
1.5       albertel 2907: 
1.44      ng       2908: #----------------------------------------------------------------------------
                   2909: #
                   2910: #-------------------------- Next few routines handles grading by csv upload
                   2911: #
                   2912: #--- Javascript to handle csv upload
1.27      albertel 2913: sub csvupload_javascript_reverse_associate {
1.246     albertel 2914:     my $error1=&mt('You need to specify the username or ID');
                   2915:     my $error2=&mt('You need to specify at least one grading field');
1.27      albertel 2916:   return(<<ENDPICK);
                   2917:   function verify(vf) {
                   2918:     var foundsomething=0;
                   2919:     var founduname=0;
1.243     albertel 2920:     var foundID=0;
1.27      albertel 2921:     for (i=0;i<=vf.nfields.value;i++) {
                   2922:       tw=eval('vf.f'+i+'.selectedIndex');
1.243     albertel 2923:       if (i==0 && tw!=0) { foundID=1; }
                   2924:       if (i==1 && tw!=0) { founduname=1; }
                   2925:       if (i!=0 && i!=1 && i!=2 && tw!=0) { foundsomething=1; }
1.27      albertel 2926:     }
1.246     albertel 2927:     if (founduname==0 && foundID==0) {
                   2928: 	alert('$error1');
                   2929: 	return;
1.27      albertel 2930:     }
                   2931:     if (foundsomething==0) {
1.246     albertel 2932: 	alert('$error2');
                   2933: 	return;
1.27      albertel 2934:     }
                   2935:     vf.submit();
                   2936:   }
                   2937:   function flip(vf,tf) {
                   2938:     var nw=eval('vf.f'+tf+'.selectedIndex');
                   2939:     var i;
                   2940:     for (i=0;i<=vf.nfields.value;i++) {
                   2941:       //can not pick the same destination field for both name and domain
                   2942:       if (((i ==0)||(i ==1)) && 
                   2943:           ((tf==0)||(tf==1)) && 
                   2944:           (i!=tf) &&
                   2945:           (eval('vf.f'+i+'.selectedIndex')==nw)) {
                   2946:         eval('vf.f'+i+'.selectedIndex=0;')
                   2947:       }
                   2948:     }
                   2949:   }
                   2950: ENDPICK
                   2951: }
                   2952: 
                   2953: sub csvupload_javascript_forward_associate {
1.246     albertel 2954:     my $error1=&mt('You need to specify the username or ID');
                   2955:     my $error2=&mt('You need to specify at least one grading field');
1.27      albertel 2956:   return(<<ENDPICK);
                   2957:   function verify(vf) {
                   2958:     var foundsomething=0;
                   2959:     var founduname=0;
1.243     albertel 2960:     var foundID=0;
1.27      albertel 2961:     for (i=0;i<=vf.nfields.value;i++) {
                   2962:       tw=eval('vf.f'+i+'.selectedIndex');
1.243     albertel 2963:       if (tw==1) { foundID=1; }
                   2964:       if (tw==2) { founduname=1; }
                   2965:       if (tw>3) { foundsomething=1; }
1.27      albertel 2966:     }
1.246     albertel 2967:     if (founduname==0 && foundID==0) {
                   2968: 	alert('$error1');
                   2969: 	return;
1.27      albertel 2970:     }
                   2971:     if (foundsomething==0) {
1.246     albertel 2972: 	alert('$error2');
                   2973: 	return;
1.27      albertel 2974:     }
                   2975:     vf.submit();
                   2976:   }
                   2977:   function flip(vf,tf) {
                   2978:     var nw=eval('vf.f'+tf+'.selectedIndex');
                   2979:     var i;
                   2980:     //can not pick the same destination field twice
                   2981:     for (i=0;i<=vf.nfields.value;i++) {
                   2982:       if ((i!=tf) && (eval('vf.f'+i+'.selectedIndex')==nw)) {
                   2983:         eval('vf.f'+i+'.selectedIndex=0;')
                   2984:       }
                   2985:     }
                   2986:   }
                   2987: ENDPICK
                   2988: }
                   2989: 
1.26      albertel 2990: sub csvuploadmap_header {
1.41      ng       2991:     my ($request,$symb,$url,$datatoken,$distotal)= @_;
                   2992:     my $javascript;
1.257     albertel 2993:     if ($env{'form.upfile_associate'} eq 'reverse') {
1.41      ng       2994: 	$javascript=&csvupload_javascript_reverse_associate();
                   2995:     } else {
                   2996: 	$javascript=&csvupload_javascript_forward_associate();
                   2997:     }
1.45      ng       2998: 
1.257     albertel 2999:     my ($result) = &showResourceInfo($url,$env{'form.probTitle'});
                   3000:     my $checked=(($env{'form.noFirstLine'})?' checked="checked"':'');
1.245     albertel 3001:     my $ignore=&mt('Ignore First Line');
1.41      ng       3002:     $request->print(<<ENDPICK);
1.26      albertel 3003: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
1.45      ng       3004: <h3><font color="#339933">Uploading Class Grades</font></h3>
                   3005: $result
1.26      albertel 3006: <hr>
                   3007: <h3>Identify fields</h3>
                   3008: Total number of records found in file: $distotal <hr />
                   3009: Enter as many fields as you can. The system will inform you and bring you back
                   3010: to this page if the data selected is insufficient to run your class.<hr />
                   3011: <input type="button" value="Reverse Association" onClick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" />
1.245     albertel 3012: <label><input type="checkbox" name="noFirstLine" $checked />$ignore</label>
1.26      albertel 3013: <input type="hidden" name="associate"  value="" />
                   3014: <input type="hidden" name="phase"      value="three" />
                   3015: <input type="hidden" name="datatoken"  value="$datatoken" />
1.257     albertel 3016: <input type="hidden" name="fileupload" value="$env{'form.fileupload'}" />
                   3017: <input type="hidden" name="upfiletype" value="$env{'form.upfiletype'}" />
1.26      albertel 3018: <input type="hidden" name="upfile_associate" 
1.257     albertel 3019:                                        value="$env{'form.upfile_associate'}" />
1.26      albertel 3020: <input type="hidden" name="symb"       value="$symb" />
                   3021: <input type="hidden" name="url"        value="$url" />
1.257     albertel 3022: <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />
                   3023: <input type="hidden" name="probTitle"  value="$env{'form.probTitle'}" />
1.246     albertel 3024: <input type="hidden" name="command"    value="csvuploadoptions" />
1.26      albertel 3025: <hr />
                   3026: <script type="text/javascript" language="Javascript">
                   3027: $javascript
                   3028: </script>
                   3029: ENDPICK
1.118     ng       3030:     return '';
1.26      albertel 3031: 
                   3032: }
                   3033: 
                   3034: sub csvupload_fields {
1.146     albertel 3035:     my ($url,$symb) = @_;
                   3036:     my (@parts) = &getpartlist($url,$symb);
1.243     albertel 3037:     my @fields=(['ID','Student ID'],
                   3038: 		['username','Student Username'],
                   3039: 		['domain','Student Domain']);
1.41      ng       3040:     foreach my $part (sort(@parts)) {
                   3041: 	my @datum;
                   3042: 	my $display=&Apache::lonnet::metadata($url,$part.'.display');
                   3043: 	my $name=$part;
                   3044: 	if  (!$display) { $display = $name; }
                   3045: 	@datum=($name,$display);
1.244     albertel 3046: 	if ($name=~/^stores_(.*)_awarded/) {
                   3047: 	    push(@fields,['stores_'.$1.'_points',"Points [Part: $1]"]);
                   3048: 	}
1.41      ng       3049: 	push(@fields,\@datum);
                   3050:     }
                   3051:     return (@fields);
1.26      albertel 3052: }
                   3053: 
                   3054: sub csvuploadmap_footer {
1.41      ng       3055:     my ($request,$i,$keyfields) =@_;
                   3056:     $request->print(<<ENDPICK);
1.26      albertel 3057: </table>
                   3058: <input type="hidden" name="nfields" value="$i" />
                   3059: <input type="hidden" name="keyfields" value="$keyfields" />
                   3060: <input type="button" onClick="javascript:verify(this.form)" value="Assign Grades" /><br />
                   3061: </form>
                   3062: ENDPICK
                   3063: }
                   3064: 
1.86      ng       3065: sub upcsvScores_form {
                   3066:     my ($request) = shift;
                   3067:     my ($symb,$url)=&get_symb_and_url($request);
                   3068:     if (!$symb) {return '';}
                   3069:     my $result =<<CSVFORMJS;
                   3070: <script type="text/javascript" language="javascript">
                   3071:     function checkUpload(formname) {
                   3072: 	if (formname.upfile.value == "") {
                   3073: 	    alert("Please use the browse button to select a file from your local directory.");
                   3074: 	    return false;
                   3075: 	}
                   3076: 	formname.submit();
                   3077:     }
                   3078:     </script>
                   3079: CSVFORMJS
1.257     albertel 3080:     $env{'form.probTitle'} = &Apache::lonnet::gettitle($symb);
                   3081:     my ($table) = &showResourceInfo($url,$env{'form.probTitle'});
1.118     ng       3082:     $result.=$table;
1.86      ng       3083:     $result.='<br /><table width=100% border=0><tr><td bgcolor="#777777">'."\n";
                   3084:     $result.='<table width=100% border=0><tr bgcolor="#e6ffff"><td>'."\n";
1.118     ng       3085:     $result.='&nbsp;<b>Specify a file containing the class scores for current resource'.
1.86      ng       3086: 	'.</b></td></tr>'."\n";
                   3087:     $result.='<tr bgcolor=#ffffe6><td>'."\n";
                   3088:     my $upfile_select=&Apache::loncommon::upfile_select_html();
1.245     albertel 3089:     my $ignore=&mt('Ignore First Line');
1.86      ng       3090:     $result.=<<ENDUPFORM;
1.106     albertel 3091: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
1.86      ng       3092: <input type="hidden" name="symb" value="$symb" />
                   3093: <input type="hidden" name="url" value="$url" />
                   3094: <input type="hidden" name="command" value="csvuploadmap" />
1.257     albertel 3095: <input type="hidden" name="probTitle" value="$env{'form.probTitle'}" />
                   3096: <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />
1.86      ng       3097: $upfile_select
                   3098: <br /><input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scores" />
1.245     albertel 3099: <label><input type="checkbox" name="noFirstLine" />$ignore</lable>
1.86      ng       3100: </form>
                   3101: ENDUPFORM
                   3102:     $result.='</td></tr></table>'."\n";
                   3103:     $result.='</td></tr></table><br /><br />'."\n";
                   3104:     $result.=&show_grading_menu_form($symb,$url);
                   3105:     return $result;
                   3106: }
                   3107: 
                   3108: 
1.26      albertel 3109: sub csvuploadmap {
1.41      ng       3110:     my ($request)= @_;
                   3111:     my ($symb,$url)=&get_symb_and_url($request);
                   3112:     if (!$symb) {return '';}
1.72      ng       3113: 
1.41      ng       3114:     my $datatoken;
1.257     albertel 3115:     if (!$env{'form.datatoken'}) {
1.41      ng       3116: 	$datatoken=&Apache::loncommon::upfile_store($request);
1.26      albertel 3117:     } else {
1.257     albertel 3118: 	$datatoken=$env{'form.datatoken'};
1.41      ng       3119: 	&Apache::loncommon::load_tmp_file($request);
1.26      albertel 3120:     }
1.41      ng       3121:     my @records=&Apache::loncommon::upfile_record_sep();
1.257     albertel 3122:     if ($env{'form.noFirstLine'}) { shift(@records); }
1.41      ng       3123:     &csvuploadmap_header($request,$symb,$url,$datatoken,$#records+1);
                   3124:     my ($i,$keyfields);
                   3125:     if (@records) {
1.146     albertel 3126: 	my @fields=&csvupload_fields($url,$symb);
1.45      ng       3127: 
1.257     albertel 3128: 	if ($env{'form.upfile_associate'} eq 'reverse') {	
1.41      ng       3129: 	    &Apache::loncommon::csv_print_samples($request,\@records);
                   3130: 	    $i=&Apache::loncommon::csv_print_select_table($request,\@records,
                   3131: 							  \@fields);
                   3132: 	    foreach (@fields) { $keyfields.=$_->[0].','; }
                   3133: 	    chop($keyfields);
                   3134: 	} else {
                   3135: 	    unshift(@fields,['none','']);
                   3136: 	    $i=&Apache::loncommon::csv_samples_select_table($request,\@records,
                   3137: 							    \@fields);
                   3138: 	    my %sone=&Apache::loncommon::record_sep($records[0]);
                   3139: 	    $keyfields=join(',',sort(keys(%sone)));
                   3140: 	}
                   3141:     }
                   3142:     &csvuploadmap_footer($request,$i,$keyfields);
1.72      ng       3143:     $request->print(&show_grading_menu_form($symb,$url));
                   3144: 
1.41      ng       3145:     return '';
1.27      albertel 3146: }
                   3147: 
1.246     albertel 3148: sub csvuploadoptions {
1.41      ng       3149:     my ($request)= @_;
                   3150:     my ($symb,$url)=&get_symb_and_url($request);
1.257     albertel 3151:     my $checked=(($env{'form.noFirstLine'})?'1':'0');
1.246     albertel 3152:     my $ignore=&mt('Ignore First Line');
                   3153:     $request->print(<<ENDPICK);
                   3154: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
                   3155: <h3><font color="#339933">Uploading Class Grade Options</font></h3>
                   3156: <input type="hidden" name="command"    value="csvuploadassign" />
                   3157: <input type="submit" value="Assign Grades" /><br />
                   3158: <p>
                   3159: <label>
                   3160:    <input type="checkbox" name="show_full_results" />
                   3161:    Show a table of all changes
                   3162: </label>
                   3163: </p>
                   3164: <p>
                   3165: <label>
                   3166:    <input type="checkbox" name="overwite_scores" checked="checked" />
                   3167:    Overwrite any existing score
                   3168: </label>
                   3169: </p>
                   3170: ENDPICK
                   3171:     my %fields=&get_fields();
                   3172:     if (!defined($fields{'domain'})) {
1.257     albertel 3173: 	my $domform = &Apache::loncommon::select_dom_form($env{'request.role.domain'},'default_domain');
1.246     albertel 3174: 	$request->print("\n<p> Users are in domain: ".$domform."</p>\n");
                   3175:     }
1.257     albertel 3176:     foreach my $key (sort(keys(%env))) {
1.246     albertel 3177: 	if ($key !~ /^form\.(.*)$/) { next; }
                   3178: 	my $cleankey=$1;
                   3179: 	if ($cleankey eq 'command') { next; }
                   3180: 	$request->print('<input type="hidden" name="'.$cleankey.
1.257     albertel 3181: 			'"  value="'.$env{$key}.'" />'."\n");
1.246     albertel 3182:     }
                   3183:     # FIXME do a check for any duplicated user ids...
                   3184:     # FIXME do a check for any invalid user ids?...
                   3185:     $request->print("<hr /></form>\n");
                   3186:     $request->print(&show_grading_menu_form($symb,$url));
                   3187:     return '';
                   3188: }
                   3189: 
                   3190: sub get_fields {
                   3191:     my %fields;
1.257     albertel 3192:     my @keyfields = split(/\,/,$env{'form.keyfields'});
                   3193:     for (my $i=0; $i<=$env{'form.nfields'}; $i++) {
                   3194: 	if ($env{'form.upfile_associate'} eq 'reverse') {
                   3195: 	    if ($env{'form.f'.$i} ne 'none') {
                   3196: 		$fields{$keyfields[$i]}=$env{'form.f'.$i};
1.41      ng       3197: 	    }
                   3198: 	} else {
1.257     albertel 3199: 	    if ($env{'form.f'.$i} ne 'none') {
                   3200: 		$fields{$env{'form.f'.$i}}=$keyfields[$i];
1.41      ng       3201: 	    }
                   3202: 	}
1.27      albertel 3203:     }
1.246     albertel 3204:     return %fields;
                   3205: }
                   3206: 
                   3207: sub csvuploadassign {
                   3208:     my ($request)= @_;
                   3209:     my ($symb,$url)=&get_symb_and_url($request);
                   3210:     if (!$symb) {return '';}
                   3211:     &Apache::loncommon::load_tmp_file($request);
                   3212:     my @gradedata = &Apache::loncommon::upfile_record_sep();
1.257     albertel 3213:     if ($env{'form.noFirstLine'}) { shift(@gradedata); }
1.246     albertel 3214:     my %fields=&get_fields();
1.41      ng       3215:     $request->print('<h3>Assigning Grades</h3>');
1.257     albertel 3216:     my $courseid=$env{'request.course.id'};
1.97      albertel 3217:     my ($classlist) = &getclasslist('all',0);
1.106     albertel 3218:     my @notallowed;
1.41      ng       3219:     my @skipped;
                   3220:     my $countdone=0;
                   3221:     foreach my $grade (@gradedata) {
                   3222: 	my %entries=&Apache::loncommon::record_sep($grade);
1.246     albertel 3223: 	my $domain;
                   3224: 	if ($entries{$fields{'domain'}}) {
                   3225: 	    $domain=$entries{$fields{'domain'}};
                   3226: 	} else {
1.257     albertel 3227: 	    $domain=$env{'form.default_domain'};
1.246     albertel 3228: 	}
1.243     albertel 3229: 	$domain=~s/\s//g;
1.41      ng       3230: 	my $username=$entries{$fields{'username'}};
1.160     albertel 3231: 	$username=~s/\s//g;
1.243     albertel 3232: 	if (!$username) {
                   3233: 	    my $id=$entries{$fields{'ID'}};
1.247     albertel 3234: 	    $id=~s/\s//g;
1.243     albertel 3235: 	    my %ids=&Apache::lonnet::idget($domain,$id);
                   3236: 	    $username=$ids{$id};
                   3237: 	}
1.41      ng       3238: 	if (!exists($$classlist{"$username:$domain"})) {
1.247     albertel 3239: 	    my $id=$entries{$fields{'ID'}};
                   3240: 	    $id=~s/\s//g;
                   3241: 	    if ($id) {
                   3242: 		push(@skipped,"$id:$domain");
                   3243: 	    } else {
                   3244: 		push(@skipped,"$username:$domain");
                   3245: 	    }
1.41      ng       3246: 	    next;
                   3247: 	}
1.108     albertel 3248: 	my $usec=$classlist->{"$username:$domain"}[5];
1.106     albertel 3249: 	if (!&canmodify($usec)) {
                   3250: 	    push(@notallowed,"$username:$domain");
                   3251: 	    next;
                   3252: 	}
1.244     albertel 3253: 	my %points;
1.41      ng       3254: 	my %grades;
                   3255: 	foreach my $dest (keys(%fields)) {
1.244     albertel 3256: 	    if ($dest eq 'ID' || $dest eq 'username' ||
                   3257: 		$dest eq 'domain') { next; }
                   3258: 	    if ($entries{$fields{$dest}} =~ /^\s*$/) { next; }
                   3259: 	    if ($dest=~/stores_(.*)_points/) {
                   3260: 		my $part=$1;
                   3261: 		my $wgt =&Apache::lonnet::EXT('resource.'.$part.'.weight',
                   3262: 					      $symb,$domain,$username);
1.247     albertel 3263: 		$entries{$fields{$dest}}=~s/\s//g;
1.244     albertel 3264: 		my $pcr=$entries{$fields{$dest}} / $wgt;
                   3265: 		my $award='correct_by_override';
                   3266: 		$grades{"resource.$part.awarded"}=$pcr;
                   3267: 		$grades{"resource.$part.solved"}=$award;
                   3268: 		$points{$part}=1;
                   3269: 	    } else {
                   3270: 		if ($dest=~/stores_(.*)_awarded/) { if ($points{$1}) {next;} }
                   3271: 		if ($dest=~/stores_(.*)_solved/)  { if ($points{$1}) {next;} }
                   3272: 		my $store_key=$dest;
                   3273: 		$store_key=~s/^stores/resource/;
                   3274: 		$store_key=~s/_/\./g;
                   3275: 		$grades{$store_key}=$entries{$fields{$dest}};
                   3276: 	    }
1.41      ng       3277: 	}
1.244     albertel 3278: 	if (! %grades) { push(@skipped,"$username:$domain no data to store"); }
1.257     albertel 3279: 	$grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";
1.244     albertel 3280: #	&Apache::lonnet::logthis(" storing ".(join('-',%grades)));
1.257     albertel 3281: 	&Apache::lonnet::cstore(\%grades,$symb,$env{'request.course.id'},
1.41      ng       3282: 				$domain,$username);
                   3283: 	$request->print('.');
                   3284: 	$request->rflush();
                   3285: 	$countdone++;
                   3286:     }
                   3287:     $request->print("<br />Stored $countdone students\n");
                   3288:     if (@skipped) {
1.106     albertel 3289: 	$request->print('<p<font size="+1"><b>Skipped Students</b></font></p>');
                   3290: 	foreach my $student (@skipped) { $request->print("$student<br />\n"); }
                   3291:     }
                   3292:     if (@notallowed) {
                   3293: 	$request->print('<p><font size="+1" color="red"><b>Students Not Allowed to Modify</b></font></p>');
                   3294: 	foreach my $student (@notallowed) { $request->print("$student<br />\n"); }
1.41      ng       3295:     }
1.106     albertel 3296:     $request->print("<br />\n");
1.41      ng       3297:     $request->print(&show_grading_menu_form($symb,$url));
                   3298:     return '';
1.26      albertel 3299: }
1.44      ng       3300: #------------- end of section for handling csv file upload ---------
                   3301: #
                   3302: #-------------------------------------------------------------------
                   3303: #
1.122     ng       3304: #-------------- Next few routines handle grading by page/sequence
1.72      ng       3305: #
                   3306: #--- Select a page/sequence and a student to grade
1.68      ng       3307: sub pickStudentPage {
                   3308:     my ($request) = shift;
                   3309: 
                   3310:     $request->print(<<LISTJAVASCRIPT);
                   3311: <script type="text/javascript" language="javascript">
                   3312: 
                   3313: function checkPickOne(formname) {
1.76      ng       3314:     if (radioSelection(formname.student) == null) {
1.68      ng       3315: 	alert("Please select the student you wish to grade.");
                   3316: 	return;
                   3317:     }
1.125     ng       3318:     ptr = pullDownSelection(formname.selectpage);
                   3319:     formname.page.value = formname["page"+ptr].value;
                   3320:     formname.title.value = formname["title"+ptr].value;
1.68      ng       3321:     formname.submit();
                   3322: }
                   3323: 
                   3324: </script>
                   3325: LISTJAVASCRIPT
1.118     ng       3326:     &commonJSfunctions($request);
1.72      ng       3327:     my ($symb,$url) = &get_symb_and_url($request);
1.257     albertel 3328:     my $cdom      = $env{"course.$env{'request.course.id'}.domain"};
                   3329:     my $cnum      = $env{"course.$env{'request.course.id'}.num"};
                   3330:     my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
1.68      ng       3331: 
                   3332:     my $result='<h3><font color="#339933">&nbsp;'.
                   3333: 	'Manual Grading by Page or Sequence</font></h3>';
                   3334: 
1.80      ng       3335:     $result.='<form action="/adm/grades" method="post" name="displayPage">'."\n";
1.70      ng       3336:     $result.='&nbsp;<b>Problems from:</b> <select name="selectpage">'."\n";
1.74      albertel 3337:     my ($titles,$symbx) = &getSymbMap($request);
1.137     albertel 3338:     my ($curpage) =&Apache::lonnet::decode_symb($symb); 
                   3339: #    my ($curpage,$mapId) =&Apache::lonnet::decode_symb($symb); 
                   3340: #    my $type=($curpage =~ /\.(page|sequence)/);
1.70      ng       3341:     my $ctr=0;
1.68      ng       3342:     foreach (@$titles) {
                   3343: 	my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
1.70      ng       3344: 	$result.='<option value="'.$ctr.'" '.
1.71      ng       3345: 	    ($$symbx{$_} =~ /$curpage$/ ? 'selected="on"' : '').
                   3346: 	    '>'.$showtitle.'</option>'."\n";
1.70      ng       3347: 	$ctr++;
1.68      ng       3348:     }
                   3349:     $result.= '</select>'."<br>\n";
1.70      ng       3350:     $ctr=0;
                   3351:     foreach (@$titles) {
                   3352: 	my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
                   3353: 	$result.='<input type="hidden" name="page'.$ctr.'" value="'.$$symbx{$_}.'" />'."\n";
                   3354: 	$result.='<input type="hidden" name="title'.$ctr.'" value="'.$showtitle.'" />'."\n";
                   3355: 	$ctr++;
                   3356:     }
1.72      ng       3357:     $result.='<input type="hidden" name="page" />'."\n".
                   3358: 	'<input type="hidden" name="title" />'."\n";
1.68      ng       3359: 
1.144     albertel 3360:     $result.='&nbsp;<b>View Problems Text: </b><input type="radio" name="vProb" value="no" checked="on" /> no '."\n".
1.71      ng       3361: 	'<input type="radio" name="vProb" value="yes" /> yes '."<br>\n";
1.72      ng       3362: 
1.71      ng       3363:     $result.='&nbsp;<b>Submission Details: </b>'.
                   3364: 	'<input type="radio" name="lastSub" value="none" /> none'."\n".
1.122     ng       3365: 	'<input type="radio" name="lastSub" value="datesub" checked /> by dates and submissions'."\n".
1.71      ng       3366: 	'<input type="radio" name="lastSub" value="all" /> all details'."\n";
1.72      ng       3367: 
1.68      ng       3368:     $result.='<input type="hidden" name="section"     value="'.$getsec.'" />'."\n".
1.257     albertel 3369: 	'<input type="hidden" name="Status"  value="'.$env{'form.Status'}.'" />'."\n".
1.72      ng       3370: 	'<input type="hidden" name="command" value="displayPage" />'."\n".
                   3371: 	'<input type="hidden" name="url"     value="'.$url.'" />'."\n".
1.80      ng       3372: 	'<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
1.257     albertel 3373: 	'<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."<br />\n";
1.72      ng       3374: 
1.80      ng       3375:     $result.='&nbsp;<input type="button" '.
1.126     ng       3376: 	'onClick="javascript:checkPickOne(this.form);"value="Next->" /><br />'."\n";
1.72      ng       3377: 
1.68      ng       3378:     $request->print($result);
                   3379: 
1.126     ng       3380:     my $studentTable.='&nbsp;<b>Select a student you wish to grade and then click on the Next button.</b><br>'.
1.68      ng       3381: 	'<table border="0"><tr><td bgcolor="#777777">'.
                   3382: 	'<table border="0"><tr bgcolor="#e6ffff">'.
1.126     ng       3383: 	'<td align="right">&nbsp;<b>No.</b></td>'.
1.129     ng       3384: 	'<td>'.&nameUserString('header').'</td>'.
1.126     ng       3385: 	'<td align="right">&nbsp;<b>No.</b></td>'.
1.129     ng       3386: 	'<td>'.&nameUserString('header').'</td></tr>';
1.68      ng       3387:  
1.76      ng       3388:     my (undef,undef,$fullname) = &getclasslist($getsec,'1');
1.68      ng       3389:     my $ptr = 1;
                   3390:     foreach my $student (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
                   3391: 	my ($uname,$udom) = split(/:/,$student);
1.126     ng       3392: 	$studentTable.=($ptr%2 == 1 ? '<tr bgcolor="#ffffe6">' : '</td>');
                   3393: 	$studentTable.='<td align="right">'.$ptr.'&nbsp;</td>';
1.129     ng       3394: 	$studentTable.='<td>&nbsp;<input type="radio" name="student" value="'.$student.'" /> '
                   3395: 	    .&nameUserString(undef,$$fullname{$student},$uname,$udom)."\n";
1.126     ng       3396: 	$studentTable.=($ptr%2 == 0 ? '</td></tr>' : '');
1.68      ng       3397: 	$ptr++;
                   3398:     }
1.126     ng       3399:     $studentTable.='</td><td>&nbsp;</td><td>&nbsp;' if ($ptr%2 == 0);
1.68      ng       3400:     $studentTable.='</td></tr></table></td></tr></table>'."\n";
1.126     ng       3401:     $studentTable.='<input type="button" '.
                   3402: 	'onClick="javascript:checkPickOne(this.form);"value="Next->" /></form>'."\n";
1.68      ng       3403: 
                   3404:     $studentTable.=&show_grading_menu_form($symb,$url);
                   3405:     $request->print($studentTable);
                   3406: 
                   3407:     return '';
                   3408: }
                   3409: 
                   3410: sub getSymbMap {
1.74      albertel 3411:     my ($request) = @_;
1.132     bowersj2 3412:     my $navmap = Apache::lonnavmaps::navmap->new();
1.68      ng       3413: 
                   3414:     my %symbx = ();
                   3415:     my @titles = ();
1.117     bowersj2 3416:     my $minder = 0;
                   3417: 
                   3418:     # Gather every sequence that has problems.
1.240     albertel 3419:     my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); },
                   3420: 					       1,0,1);
1.117     bowersj2 3421:     for my $sequence ($navmap->getById('0.0'), @sequences) {
1.241     albertel 3422: 	if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 0) ) {
1.117     bowersj2 3423: 	    my $title = $minder.'.'.$sequence->compTitle();
                   3424: 	    push @titles, $title; # minder in case two titles are identical
                   3425: 	    $symbx{$title} = $sequence->symb();
                   3426: 	    $minder++;
1.241     albertel 3427: 	}
1.68      ng       3428:     }
                   3429:     return \@titles,\%symbx;
                   3430: }
                   3431: 
1.72      ng       3432: #
                   3433: #--- Displays a page/sequence w/wo problems, w/wo submissions
1.68      ng       3434: sub displayPage {
                   3435:     my ($request) = shift;
                   3436: 
1.72      ng       3437:     my ($symb,$url) = &get_symb_and_url($request);
1.257     albertel 3438:     my $cdom      = $env{"course.$env{'request.course.id'}.domain"};
                   3439:     my $cnum      = $env{"course.$env{'request.course.id'}.num"};
                   3440:     my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
                   3441:     my $pageTitle = $env{'form.page'};
1.103     albertel 3442:     my ($classlist,undef,$fullname) = &getclasslist($getsec,'1');
1.257     albertel 3443:     my ($uname,$udom) = split(/:/,$env{'form.student'});
                   3444:     my $usec=$classlist->{$env{'form.student'}}[5];
1.168     albertel 3445: 
                   3446:     #need to make sure we have the correct data for later EXT calls, 
                   3447:     #thus invalidate the cache
                   3448:     &Apache::lonnet::devalidatecourseresdata(
1.257     albertel 3449:                  $env{'course.'.$env{'request.course.id'}.'.num'},
                   3450:                  $env{'course.'.$env{'request.course.id'}.'.domain'});
1.168     albertel 3451:     &Apache::lonnet::clear_EXT_cache_status();
                   3452: 
1.103     albertel 3453:     if (!&canview($usec)) {
1.257     albertel 3454: 	$request->print('<font color="red">Unable to view requested student.('.$env{'form.student'}.')</font>');
1.103     albertel 3455: 	$request->print(&show_grading_menu_form($symb,$url));
                   3456: 	return;
                   3457:     }
1.257     albertel 3458:     my $result='<h3><font color="#339933">&nbsp;'.$env{'form.title'}.'</font></h3>';
                   3459:     $result.='<h3>&nbsp;Student: '.&nameUserString(undef,$$fullname{$env{'form.student'}},$uname,$udom).
1.129     ng       3460: 	'</h3>'."\n";
1.71      ng       3461:     &sub_page_js($request);
                   3462:     $request->print($result);
                   3463: 
1.132     bowersj2 3464:     my $navmap = Apache::lonnavmaps::navmap->new();
1.257     albertel 3465:     my ($mapUrl, $id, $resUrl)=&Apache::lonnet::decode_symb($env{'form.page'});
1.68      ng       3466:     my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
                   3467: 
                   3468:     my $iterator = $navmap->getIterator($map->map_start(),
                   3469: 					$map->map_finish());
                   3470: 
1.71      ng       3471:     my $studentTable='<form action="/adm/grades" method="post" name="gradePage">'."\n".
1.72      ng       3472: 	'<input type="hidden" name="command" value="gradeByPage" />'."\n".
1.257     albertel 3473: 	'<input type="hidden" name="fullname" value="'.$$fullname{$env{'form.student'}}.'" />'."\n".
                   3474: 	'<input type="hidden" name="student" value="'.$env{'form.student'}.'" />'."\n".
1.72      ng       3475: 	'<input type="hidden" name="page"    value="'.$pageTitle.'" />'."\n".
1.257     albertel 3476: 	'<input type="hidden" name="title"   value="'.$env{'form.title'}.'" />'."\n".
1.72      ng       3477: 	'<input type="hidden" name="url"     value="'.$url.'" />'."\n".
                   3478: 	'<input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
1.125     ng       3479: 	'<input type="hidden" name="overRideScore" value="no" />'."\n".
1.257     albertel 3480: 	'<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n";
1.71      ng       3481: 
                   3482:     my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').
                   3483: 	'/check.gif" height="16" border="0" />';
                   3484: 
1.118     ng       3485:     $studentTable.='&nbsp;<b>Note:</b> Problems graded correct by the computer are marked with a '.$checkIcon.
                   3486: 	' symbol.'."\n".
1.71      ng       3487: 	'<table border="0"><tr><td bgcolor="#777777">'.
                   3488: 	'<table border="0"><tr bgcolor="#e6ffff">'.
1.118     ng       3489: 	'<td align="center"><b>&nbsp;Prob.&nbsp;</b></td>'.
1.257     albertel 3490: 	'<td><b>&nbsp;'.($env{'form.vProb'} eq 'no' ? 'Title' : 'Problem Text').'/Grade</b></td></tr>';
1.71      ng       3491: 
1.196     albertel 3492:     my ($depth,$question,$prob) = (1,1,1);
1.68      ng       3493:     $iterator->next(); # skip the first BEGIN_MAP
                   3494:     my $curRes = $iterator->next(); # for "current resource"
1.101     albertel 3495:     while ($depth > 0) {
1.68      ng       3496:         if($curRes == $iterator->BEGIN_MAP) { $depth++; }
1.100     bowersj2 3497:         if($curRes == $iterator->END_MAP) { $depth--; }
1.68      ng       3498: 
1.235     albertel 3499:         if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) {
1.91      albertel 3500: 	    my $parts = $curRes->parts();
1.68      ng       3501:             my $title = $curRes->compTitle();
1.71      ng       3502: 	    my $symbx = $curRes->symb();
1.196     albertel 3503: 	    $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$prob.
1.71      ng       3504: 		(scalar(@{$parts}) == 1 ? '' : '<br>('.scalar(@{$parts}).'&nbsp;parts)').'</td>';
                   3505: 	    $studentTable.='<td valign="top">';
1.257     albertel 3506: 	    if ($env{'form.vProb'} eq 'yes' ) {
1.144     albertel 3507: 		$studentTable.=&show_problem($request,$symbx,$uname,$udom,1,
                   3508: 					     undef,'both');
1.71      ng       3509: 	    } else {
1.257     albertel 3510: 		my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'});
1.80      ng       3511: 		$companswer =~ s|<form(.*?)>||g;
                   3512: 		$companswer =~ s|</form>||g;
1.71      ng       3513: #		while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>
1.116     ng       3514: #		    $companswer =~ s/$1/ /ms;
                   3515: #		    $request->print('match='.$1."<br>\n");
1.71      ng       3516: #		}
1.116     ng       3517: #		$companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;
1.71      ng       3518: 		$studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br>&nbsp;<b>Correct answer:</b><br>'.$companswer;
                   3519: 	    }
                   3520: 
1.257     albertel 3521: 	    my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);
1.125     ng       3522: 
1.257     albertel 3523: 	    if ($env{'form.lastSub'} eq 'datesub') {
1.71      ng       3524: 		if ($record{'version'} eq '') {
                   3525: 		    $studentTable.='<br />&nbsp;<font color="red">No recorded submission for this problem</font><br />';
                   3526: 		} else {
1.116     ng       3527: 		    my %responseType = ();
                   3528: 		    foreach my $partid (@{$parts}) {
1.147     albertel 3529: 			my @responseIds =$curRes->responseIds($partid);
                   3530: 			my @responseType =$curRes->responseType($partid);
                   3531: 			my %responseIds;
                   3532: 			for (my $i=0;$i<=$#responseIds;$i++) {
                   3533: 			    $responseIds{$responseIds[$i]}=$responseType[$i];
                   3534: 			}
                   3535: 			$responseType{$partid} = \%responseIds;
1.116     ng       3536: 		    }
1.148     albertel 3537: 		    $studentTable.= &displaySubByDates($symbx,\%record,$parts,\%responseType,$checkIcon,$uname,$udom);
1.147     albertel 3538: 
1.71      ng       3539: 		}
1.257     albertel 3540: 	    } elsif ($env{'form.lastSub'} eq 'all') {
                   3541: 		my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');
1.71      ng       3542: 		$studentTable.=&Apache::loncommon::get_previous_attempt($symbx,$uname,$udom,
1.257     albertel 3543: 									$env{'request.course.id'},
1.71      ng       3544: 									'','.submission');
                   3545:  
                   3546: 	    }
1.103     albertel 3547: 	    if (&canmodify($usec)) {
                   3548: 		foreach my $partid (@{$parts}) {
                   3549: 		    $studentTable.=&gradeBox($request,$symbx,$uname,$udom,$question,$partid,\%record);
                   3550: 		    $studentTable.='<input type="hidden" name="q_'.$question.'" value="'.$partid.'" />'."\n";
                   3551: 		    $question++;
                   3552: 		}
1.196     albertel 3553: 		$prob++;
1.71      ng       3554: 	    }
                   3555: 	    $studentTable.='</td></tr>';
1.68      ng       3556: 
1.103     albertel 3557: 	}
1.68      ng       3558:         $curRes = $iterator->next();
                   3559:     }
                   3560: 
1.71      ng       3561:     $studentTable.='</td></tr></table></td></tr></table>'."\n".
1.125     ng       3562: 	'<input type="button" value="Save" '.
1.71      ng       3563: 	'onClick="javascript:checkSubmitPage(this.form,'.$question.');" TARGET=_self />'.
                   3564: 	'</form>'."\n";
                   3565:     $studentTable.=&show_grading_menu_form($symb,$url);
                   3566:     $request->print($studentTable);
                   3567: 
                   3568:     return '';
1.119     ng       3569: }
                   3570: 
                   3571: sub displaySubByDates {
1.148     albertel 3572:     my ($symb,$record,$parts,$responseType,$checkIcon,$uname,$udom) = @_;
1.224     albertel 3573:     my $isCODE=0;
                   3574:     if (exists($record->{'resource.CODE'})) { $isCODE=1; }
1.119     ng       3575:     my $studentTable='<table border="0" width="100%"><tr><td bgcolor="#777777">'.
                   3576: 	'<table border="0" width="100%"><tr bgcolor="#e6ffff">'.
                   3577: 	'<td><b>Date/Time</b></td>'.
1.224     albertel 3578: 	($isCODE?'<td><b>CODE</b></td>':'').
1.119     ng       3579: 	'<td><b>Submission</b></td>'.
                   3580: 	'<td><b>Status&nbsp;</b></td></tr>';
                   3581:     my ($version);
                   3582:     my %mark;
1.148     albertel 3583:     my %orders;
1.119     ng       3584:     $mark{'correct_by_student'} = $checkIcon;
1.147     albertel 3585:     if (!exists($$record{'1:timestamp'})) {
                   3586: 	return '<br />&nbsp;<font color="red">Nothing submitted - no attempts</font><br />';
                   3587:     }
1.119     ng       3588:     for ($version=1;$version<=$$record{'version'};$version++) {
                   3589: 	my $timestamp = scalar(localtime($$record{$version.':timestamp'}));
                   3590: 	$studentTable.='<tr bgcolor="#ffffff" valign="top"><td>'.$timestamp.'</td>';
1.224     albertel 3591: 	if ($isCODE) {
                   3592: 	    $studentTable.='<td>'.$record->{$version.':resource.CODE'}.'</td>';
                   3593: 	}
1.119     ng       3594: 	my @versionKeys = split(/\:/,$$record{$version.':keys'});
                   3595: 	my @displaySub = ();
                   3596: 	foreach my $partid (@{$parts}) {
1.147     albertel 3597: 	    my @matchKey = sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys);
1.122     ng       3598: #	    next if ($$record{"$version:resource.$partid.solved"} eq '');
1.207     albertel 3599: 	    my $display_part=&get_display_part($partid,undef,$symb);
1.147     albertel 3600: 	    foreach my $matchKey (@matchKey) {
1.198     albertel 3601: 		if (exists($$record{$version.':'.$matchKey}) &&
                   3602: 		    $$record{$version.':'.$matchKey} ne '') {
1.147     albertel 3603: 		    my ($responseId)=($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/);
1.207     albertel 3604: 		    $displaySub[0].='<b>Part:</b>&nbsp;'.$display_part.'&nbsp;';
1.147     albertel 3605: 		    $displaySub[0].='<font color="#999999">(ID&nbsp;'.
1.207     albertel 3606: 			$responseId.')</font>&nbsp;<b>';
1.147     albertel 3607: 		    if ($$record{"$version:resource.$partid.tries"} eq '') {
                   3608: 			$displaySub[0].='Trial&nbsp;not&nbsp;counted';
                   3609: 		    } else {
                   3610: 			$displaySub[0].='Trial&nbsp;'.
                   3611: 			    $$record{"$version:resource.$partid.tries"};
                   3612: 		    }
                   3613: 		    my $responseType=$responseType->{$partid}->{$responseId};
1.148     albertel 3614: 		    if (!exists($orders{$partid})) { $orders{$partid}={}; }
                   3615: 		    if (!exists($orders{$partid}->{$responseId})) {
                   3616: 			$orders{$partid}->{$responseId}=
                   3617: 			    &get_order($partid,$responseId,$symb,$uname,$udom);
                   3618: 		    }
1.147     albertel 3619: 		    $displaySub[0].='</b>&nbsp; '.
1.148     albertel 3620: 			&cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:").'<br />';
1.147     albertel 3621: 		}
                   3622: 	    }
                   3623: 	    if (exists $$record{"$version:resource.$partid.award"}) {
1.207     albertel 3624: 		$displaySub[1].='<b>Part:</b>&nbsp;'.$display_part.' &nbsp;'.
1.147     albertel 3625: 		    lc($$record{"$version:resource.$partid.award"}).' '.
                   3626: 		    $mark{$$record{"$version:resource.$partid.solved"}}.
                   3627: 		    '<br />';
                   3628: 	    }
                   3629: 	    if (exists $$record{"$version:resource.$partid.regrader"}) {
                   3630: 		$displaySub[2].=$$record{"$version:resource.$partid.regrader"}.
1.207     albertel 3631: 		    ' (<b>'.&mt('Part').':</b> '.$display_part.')';
1.147     albertel 3632: 	    }
                   3633: 	}
                   3634: 	# needed because old essay regrader has not parts info
                   3635: 	if (exists $$record{"$version:resource.regrader"}) {
                   3636: 	    $displaySub[2].=$$record{"$version:resource.regrader"};
                   3637: 	}
                   3638: 	$studentTable.='<td>'.$displaySub[0].'&nbsp;</td><td>'.$displaySub[1];
                   3639: 	if ($displaySub[2]) {
                   3640: 	    $studentTable.='Manually graded by '.$displaySub[2];
                   3641: 	}
                   3642: 	$studentTable.='&nbsp;</td></tr>';
                   3643:     
1.119     ng       3644:     }
                   3645:     $studentTable.='</table></td></tr></table>';
                   3646:     return $studentTable;
1.71      ng       3647: }
                   3648: 
                   3649: sub updateGradeByPage {
                   3650:     my ($request) = shift;
                   3651: 
1.257     albertel 3652:     my $cdom      = $env{"course.$env{'request.course.id'}.domain"};
                   3653:     my $cnum      = $env{"course.$env{'request.course.id'}.num"};
                   3654:     my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
                   3655:     my $pageTitle = $env{'form.page'};
1.103     albertel 3656:     my ($classlist,undef,$fullname) = &getclasslist($getsec,'1');
1.257     albertel 3657:     my ($uname,$udom) = split(/:/,$env{'form.student'});
                   3658:     my $usec=$classlist->{$env{'form.student'}}[5];
1.103     albertel 3659:     if (!&canmodify($usec)) {
1.257     albertel 3660: 	$request->print('<font color="red">Unable to modify requested student.('.$env{'form.student'}.'</font>');
                   3661: 	$request->print(&show_grading_menu_form($env{'form.symb'},$env{'form.url'}));
1.103     albertel 3662: 	return;
                   3663:     }
1.257     albertel 3664:     my $result='<h3><font color="#339933">&nbsp;'.$env{'form.title'}.'</font></h3>';
                   3665:     $result.='<h3>&nbsp;Student: '.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).
1.129     ng       3666: 	'</h3>'."\n";
1.70      ng       3667: 
1.68      ng       3668:     $request->print($result);
                   3669: 
1.132     bowersj2 3670:     my $navmap = Apache::lonnavmaps::navmap->new();
1.257     albertel 3671:     my ($mapUrl, $id, $resUrl) = &Apache::lonnet::decode_symb( $env{'form.page'});
1.71      ng       3672:     my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
                   3673: 
                   3674:     my $iterator = $navmap->getIterator($map->map_start(),
                   3675: 					$map->map_finish());
1.70      ng       3676: 
1.71      ng       3677:     my $studentTable='<table border="0"><tr><td bgcolor="#777777">'.
1.68      ng       3678: 	'<table border="0"><tr bgcolor="#e6ffff">'.
1.125     ng       3679: 	'<td align="center"><b>&nbsp;Prob.&nbsp;</b></td>'.
1.71      ng       3680: 	'<td><b>&nbsp;Title&nbsp;</b></td>'.
                   3681: 	'<td><b>&nbsp;Previous Score&nbsp;</b></td>'.
                   3682: 	'<td><b>&nbsp;New Score&nbsp;</b></td></tr>';
                   3683: 
                   3684:     $iterator->next(); # skip the first BEGIN_MAP
                   3685:     my $curRes = $iterator->next(); # for "current resource"
1.196     albertel 3686:     my ($depth,$question,$prob,$changeflag)= (1,1,1,0);
1.101     albertel 3687:     while ($depth > 0) {
1.71      ng       3688:         if($curRes == $iterator->BEGIN_MAP) { $depth++; }
1.100     bowersj2 3689:         if($curRes == $iterator->END_MAP) { $depth--; }
1.71      ng       3690: 
                   3691:         if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) {
1.91      albertel 3692: 	    my $parts = $curRes->parts();
1.71      ng       3693:             my $title = $curRes->compTitle();
                   3694: 	    my $symbx = $curRes->symb();
1.196     albertel 3695: 	    $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$prob.
1.71      ng       3696: 		(scalar(@{$parts}) == 1 ? '' : '<br>('.scalar(@{$parts}).'&nbsp;parts)').'</td>';
                   3697: 	    $studentTable.='<td valign="top">&nbsp;<b>'.$title.'</b>&nbsp;</td>';
                   3698: 
                   3699: 	    my %newrecord=();
                   3700: 	    my @displayPts=();
1.269     raeburn  3701:             my %aggregate = ();
                   3702:             my $aggregateflag = 0;
1.71      ng       3703: 	    foreach my $partid (@{$parts}) {
1.257     albertel 3704: 		my $newpts = $env{'form.GD_BOX'.$question.'_'.$partid};
                   3705: 		my $oldpts = $env{'form.oldpts'.$question.'_'.$partid};
1.71      ng       3706: 
1.257     albertel 3707: 		my $wgt = $env{'form.WGT'.$question.'_'.$partid} != 0 ? 
                   3708: 		    $env{'form.WGT'.$question.'_'.$partid} : 1;
1.71      ng       3709: 		my $partial = $newpts/$wgt;
                   3710: 		my $score;
                   3711: 		if ($partial > 0) {
                   3712: 		    $score = 'correct_by_override';
1.125     ng       3713: 		} elsif ($newpts ne '') { #empty is taken as 0
1.71      ng       3714: 		    $score = 'incorrect_by_override';
                   3715: 		}
1.257     albertel 3716: 		my $dropMenu = $env{'form.GD_SEL'.$question.'_'.$partid};
1.125     ng       3717: 		if ($dropMenu eq 'excused') {
1.71      ng       3718: 		    $partial = '';
                   3719: 		    $score = 'excused';
1.125     ng       3720: 		} elsif ($dropMenu eq 'reset status'
1.257     albertel 3721: 			 && $env{'form.solved'.$question.'_'.$partid} ne '') { #update only if previous record exists
1.125     ng       3722: 		    $newrecord{'resource.'.$partid.'.tries'} = 0;
                   3723: 		    $newrecord{'resource.'.$partid.'.solved'} = '';
                   3724: 		    $newrecord{'resource.'.$partid.'.award'} = '';
                   3725: 		    $newrecord{'resource.'.$partid.'.awarded'} = 0;
1.257     albertel 3726: 		    $newrecord{'resource.'.$partid.'.regrader'} = "$env{'user.name'}:$env{'user.domain'}";
1.125     ng       3727: 		    $changeflag++;
                   3728: 		    $newpts = '';
1.269     raeburn  3729:                     
                   3730:                     my $aggtries =  $env{'form.aggtries'.$question.'_'.$partid};
                   3731:                     my $totaltries = $env{'form.totaltries'.$question.'_'.$partid};
                   3732:                     my $solvedstatus = $env{'form.solved'.$question.'_'.$partid};
                   3733:                     if ($aggtries > 0) {
                   3734:                         &decrement_aggs($symbx,$partid,\%aggregate,$aggtries,$totaltries,$solvedstatus);
                   3735:                         $aggregateflag = 1;
                   3736:                     }
1.71      ng       3737: 		}
1.207     albertel 3738: 		my $display_part=&get_display_part($partid,undef,
                   3739: 						   $curRes->symb());
1.257     albertel 3740: 		my $oldstatus = $env{'form.solved'.$question.'_'.$partid};
1.207     albertel 3741: 		$displayPts[0].='&nbsp;<b>Part:</b> '.$display_part.' = '.
1.71      ng       3742: 		    (($oldstatus eq 'excused') ? 'excused' : $oldpts).
                   3743: 		    '&nbsp;<br>';
1.207     albertel 3744: 		$displayPts[1].='&nbsp;<b>Part:</b> '.$display_part.' = '.
1.125     ng       3745: 		     (($score eq 'excused') ? 'excused' : $newpts).
1.71      ng       3746: 		    '&nbsp;<br>';
                   3747: 
                   3748: 		$question++;
1.125     ng       3749: 		next if ($dropMenu eq 'reset status' || ($newpts == $oldpts && $score ne 'excused'));
                   3750: 
1.71      ng       3751: 		$newrecord{'resource.'.$partid.'.awarded'}  = $partial if $partial ne '';
1.125     ng       3752: 		$newrecord{'resource.'.$partid.'.solved'}   = $score if $score ne '';
1.257     albertel 3753: 		$newrecord{'resource.'.$partid.'.regrader'} = "$env{'user.name'}:$env{'user.domain'}"
1.125     ng       3754: 		    if (scalar(keys(%newrecord)) > 0);
1.71      ng       3755: 
                   3756: 		$changeflag++;
                   3757: 	    }
                   3758: 	    if (scalar(keys(%newrecord)) > 0) {
1.257     albertel 3759: 		&Apache::lonnet::cstore(\%newrecord,$symbx,$env{'request.course.id'},
1.71      ng       3760: 					$udom,$uname);
                   3761: 	    }
1.269     raeburn  3762:             if ($aggregateflag) {
                   3763:                 &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
                   3764:                       $env{'course.'.$env{'request.course.id'}.'.domain'},
                   3765:                       $env{'course.'.$env{'request.course.id'}.'.num'});
                   3766:             }
1.125     ng       3767: 
1.71      ng       3768: 	    $studentTable.='<td valign="top">'.$displayPts[0].'</td>'.
                   3769: 		'<td valign="top">'.$displayPts[1].'</td>'.
                   3770: 		'</tr>';
1.68      ng       3771: 
1.196     albertel 3772: 	    $prob++;
1.68      ng       3773: 	}
1.71      ng       3774:         $curRes = $iterator->next();
1.68      ng       3775:     }
1.98      albertel 3776: 
1.71      ng       3777:     $studentTable.='</td></tr></table></td></tr></table>';
1.257     albertel 3778:     $studentTable.=&show_grading_menu_form($env{'form.symb'},$env{'form.url'});
1.76      ng       3779:     my $grademsg=($changeflag == 0 ? 'No score was changed or updated.' :
                   3780: 		  'The scores were changed for '.
                   3781: 		  $changeflag.' problem'.($changeflag == 1 ? '.' : 's.'));
                   3782:     $request->print($grademsg.$studentTable);
1.68      ng       3783: 
1.70      ng       3784:     return '';
                   3785: }
                   3786: 
1.72      ng       3787: #-------- end of section for handling grading by page/sequence ---------
                   3788: #
                   3789: #-------------------------------------------------------------------
                   3790: 
1.75      albertel 3791: #--------------------Scantron Grading-----------------------------------
                   3792: #
                   3793: #------ start of section for handling grading by page/sequence ---------
                   3794: 
1.81      albertel 3795: sub defaultFormData {
                   3796:     my ($symb,$url)=@_;
                   3797:     return '
                   3798:       <input type="hidden" name="symb"    value="'.$symb.'" />'."\n".
                   3799:      '<input type="hidden" name="url"     value="'.$url.'" />'."\n".
1.257     albertel 3800:      '<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."\n".
                   3801:      '<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n";
1.81      albertel 3802: }
                   3803: 
1.75      albertel 3804: sub getSequenceDropDown {
                   3805:     my ($request,$symb)=@_;
                   3806:     my $result='<select name="selectpage">'."\n";
                   3807:     my ($titles,$symbx) = &getSymbMap($request);
1.137     albertel 3808:     my ($curpage)=&Apache::lonnet::decode_symb($symb); 
1.75      albertel 3809:     my $ctr=0;
                   3810:     foreach (@$titles) {
                   3811: 	my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
                   3812: 	$result.='<option value="'.$$symbx{$_}.'" '.
                   3813: 	    ($$symbx{$_} =~ /$curpage$/ ? 'selected="on"' : '').
                   3814: 	    '>'.$showtitle.'</option>'."\n";
                   3815: 	$ctr++;
                   3816:     }
                   3817:     $result.= '</select>';
                   3818:     return $result;
                   3819: }
                   3820: 
1.202     albertel 3821: sub scantron_filenames {
1.257     albertel 3822:     my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
                   3823:     my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
1.157     albertel 3824:     my @files=&Apache::lonnet::dirlist('userfiles',$cdom,$cname,
1.162     albertel 3825: 				    &Apache::loncommon::propath($cdom,$cname));
1.202     albertel 3826:     my @possiblenames;
1.201     albertel 3827:     foreach my $filename (sort(@files)) {
1.157     albertel 3828: 	($filename)=split(/&/,$filename);
                   3829: 	if ($filename!~/^scantron_orig_/) { next ; }
                   3830: 	$filename=~s/^scantron_orig_//;
1.202     albertel 3831: 	push(@possiblenames,$filename);
                   3832:     }
                   3833:     return @possiblenames;
                   3834: }
                   3835: 
                   3836: sub scantron_uploads {
1.209     ng       3837:     my ($file2grade) = @_;
1.202     albertel 3838:     my $result=	'<select name="scantron_selectfile">';
                   3839:     $result.="<option></option>";
                   3840:     foreach my $filename (sort(&scantron_filenames())) {
1.209     ng       3841: 	$result.="<option".($filename eq $file2grade ? ' selected="on"':'').">$filename</option>\n";
1.81      albertel 3842:     }
                   3843:     $result.="</select>";
                   3844:     return $result;
                   3845: }
                   3846: 
1.82      albertel 3847: sub scantron_scantab {
                   3848:     my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
                   3849:     my $result='<select name="scantron_format">'."\n";
1.191     albertel 3850:     $result.='<option></option>'."\n";
1.82      albertel 3851:     foreach my $line (<$fh>) {
                   3852: 	my ($name,$descrip)=split(/:/,$line);
                   3853: 	if ($name =~ /^\#/) { next; }
                   3854: 	$result.='<option value="'.$name.'">'.$descrip.'</option>'."\n";
                   3855:     }
                   3856:     $result.='</select>'."\n";
                   3857: 
                   3858:     return $result;
                   3859: }
                   3860: 
1.186     albertel 3861: sub scantron_CODElist {
1.257     albertel 3862:     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                   3863:     my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.186     albertel 3864:     my @names=&Apache::lonnet::getkeys('CODEs',$cdom,$cnum);
                   3865:     my $namechoice='<option></option>';
1.225     albertel 3866:     foreach my $name (sort {uc($a) cmp uc($b)} @names) {
1.191     albertel 3867: 	if ($name =~ /^error: 2 /) { next; }
1.186     albertel 3868: 	$namechoice.='<option value="'.$name.'">'.$name.'</option>';
                   3869:     }
                   3870:     $namechoice='<select name="scantron_CODElist">'.$namechoice.'</select>';
                   3871:     return $namechoice;
                   3872: }
                   3873: 
                   3874: sub scantron_CODEunique {
                   3875:     my $result='<nobr>
1.272   ! albertel 3876:                  <label><input type="radio" name="scantron_CODEunique"
        !          3877:                         value="Yes" checked="on" /> Yes </label>
1.186     albertel 3878:                 </nobr>
                   3879:                 <nobr>
1.272   ! albertel 3880:                  <label><input type="radio" name="scantron_CODEunique"
        !          3881:                         value="No" /> No </label>
1.186     albertel 3882:                 </nobr>';
                   3883:     return $result;
                   3884: }
                   3885: 
1.75      albertel 3886: sub scantron_selectphase {
1.209     ng       3887:     my ($r,$file2grade) = @_;
1.75      albertel 3888:     my ($symb,$url)=&get_symb_and_url($r);
                   3889:     if (!$symb) {return '';}
                   3890:     my $sequence_selector=&getSequenceDropDown($r,$symb);
1.81      albertel 3891:     my $default_form_data=&defaultFormData($symb,$url);
                   3892:     my $grading_menu_button=&show_grading_menu_form($symb,$url);
1.209     ng       3893:     my $file_selector=&scantron_uploads($file2grade);
1.82      albertel 3894:     my $format_selector=&scantron_scantab();
1.186     albertel 3895:     my $CODE_selector=&scantron_CODElist();
                   3896:     my $CODE_unique=&scantron_CODEunique();
1.75      albertel 3897:     my $result;
1.157     albertel 3898:     #FIXME allow instructor to be able to download the scantron file
                   3899:     # and to upload it,
1.75      albertel 3900:     $result.= <<SCANTRONFORM;
1.162     albertel 3901:     <table width="100%" border="0">
1.75      albertel 3902:     <tr>
1.226     albertel 3903:      <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantron_process">
1.75      albertel 3904:       <td bgcolor="#777777">
1.203     albertel 3905:        <input type="hidden" name="command" value="scantron_warning" />
1.162     albertel 3906:         $default_form_data
1.75      albertel 3907:         <table width="100%" border="0">
                   3908:           <tr bgcolor="#e6ffff">
1.174     albertel 3909:             <td colspan="2">
                   3910:               &nbsp;<b>Specify file and which Folder/Sequence to grade</b>
1.75      albertel 3911:             </td>
                   3912:           </tr>
                   3913:           <tr bgcolor="#ffffe6">
1.174     albertel 3914:             <td> Sequence to grade: </td><td> $sequence_selector </td>
1.75      albertel 3915:           </tr>
                   3916:           <tr bgcolor="#ffffe6">
1.174     albertel 3917:             <td> Filename of scoring office file: </td><td> $file_selector </td>
1.75      albertel 3918:           </tr>
1.82      albertel 3919:           <tr bgcolor="#ffffe6">
1.174     albertel 3920:             <td> Format of data file: </td><td> $format_selector </td>
1.82      albertel 3921:           </tr>
1.157     albertel 3922:           <tr bgcolor="#ffffe6">
1.186     albertel 3923:             <td> Saved CODEs to validate against: </td><td> $CODE_selector</td>
                   3924:           </tr>
                   3925:           <tr bgcolor="#ffffe6">
                   3926:             <td> Each CODE is only to be used once:</td><td> $CODE_unique </td>
                   3927:           </tr>
                   3928:           <tr bgcolor="#ffffe6">
1.187     albertel 3929: 	    <td> Options: </td>
                   3930:             <td>
1.272   ! albertel 3931: 	       <label><input type="checkbox" name="scantron_options_redo" value="redo_skipped"/> Do only previously skipped records</label> <br />
        !          3932:                <label><input type="checkbox" name="scantron_options_ignore" value="ignore_corrections"/> Remove all exisiting corrections</label>
1.187     albertel 3933: 	    </td>
                   3934:           </tr>
                   3935:           <tr bgcolor="#ffffe6">
1.174     albertel 3936:             <td colspan="2">
1.265     www      3937:               <input type="submit" value="Grading: Validate Scantron Records" />
1.162     albertel 3938:             </td>
                   3939:           </tr>
                   3940:         </table>
1.226     albertel 3941:        </td>
                   3942:      </form>
1.162     albertel 3943:     </tr>
                   3944: SCANTRONFORM
                   3945:    
                   3946:     $r->print($result);
                   3947: 
1.257     albertel 3948:     if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) ||
                   3949:         &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {
1.162     albertel 3950: 
                   3951:         $r->print(<<SCANTRONFORM);
                   3952:     <tr>
                   3953:       <td bgcolor="#777777">
                   3954:         <table width="100%" border="0">
                   3955:           <tr bgcolor="#e6ffff">
                   3956:             <td>
1.174     albertel 3957:               &nbsp;<b>Specify a Scantron data file to upload.</b>
1.162     albertel 3958:             </td>
                   3959:           </tr>
                   3960:           <tr bgcolor="#ffffe6">
                   3961:             <td>
                   3962: SCANTRONFORM
1.174     albertel 3963:     my $default_form_data=&defaultFormData(&get_symb_and_url($r,1));
1.257     albertel 3964:     my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
                   3965:     my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};
1.174     albertel 3966:     $r->print(<<UPLOAD);
                   3967:               <script type="text/javascript" language="javascript">
                   3968:     function checkUpload(formname) {
                   3969: 	if (formname.upfile.value == "") {
                   3970: 	    alert("Please use the browse button to select a file from your local directory.");
                   3971: 	    return false;
                   3972: 	}
                   3973: 	formname.submit();
                   3974:     }
                   3975:               </script>
                   3976: 
                   3977:               <form enctype='multipart/form-data' action='/adm/grades' name='rules' method='post'>
                   3978:                 $default_form_data
                   3979:                 <input name='courseid' type='hidden' value='$cnum' />
                   3980:                 <input name='domainid' type='hidden' value='$cdom' />
                   3981:                 <input name='command' value='scantronupload_save' type='hidden' />
                   3982:                 File to upload:<input type="file" name="upfile" size="50" />
                   3983:                 <br />
                   3984:                 <input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scantron Data" />
                   3985:               </form>
                   3986: UPLOAD
1.162     albertel 3987: 
                   3988:         $r->print(<<SCANTRONFORM);
                   3989:             </td>
                   3990:           </tr>
1.75      albertel 3991:         </table>
                   3992:       </td>
                   3993:     </tr>
1.162     albertel 3994: SCANTRONFORM
                   3995:     }
1.187     albertel 3996:     $r->print(<<SCANTRONFORM);
                   3997:     <tr>
1.226     albertel 3998:       <form action='/adm/grades' name='scantron_download'>
                   3999:         <td bgcolor="#777777">
1.187     albertel 4000:           <input type="hidden" name="command" value="scantron_download" />
                   4001:           <table width="100%" border="0">
                   4002:             <tr bgcolor="#e6ffff">
                   4003:               <td colspan="2">
                   4004:                 &nbsp;<b>Download a scoring office file</b>
                   4005:               </td>
                   4006:             </tr>
                   4007:             <tr bgcolor="#ffffe6">
                   4008:               <td> Filename of scoring office file: </td><td> $file_selector </td>
                   4009:             </tr>
                   4010:             <tr bgcolor="#ffffe6">
                   4011:               <td colspan="2">
1.202     albertel 4012:                 <input type="submit" value="Show List of Files" />
1.187     albertel 4013:               </td>
                   4014:             </tr>
                   4015:           </table>
1.226     albertel 4016:         </td>
                   4017:       </form>
1.187     albertel 4018:     </tr>
                   4019: SCANTRONFORM
1.162     albertel 4020: 
                   4021:     $r->print(<<SCANTRONFORM);
1.75      albertel 4022:   </table>
1.81      albertel 4023: $grading_menu_button
1.75      albertel 4024: SCANTRONFORM
                   4025: 
1.162     albertel 4026:     return
1.75      albertel 4027: }
                   4028: 
1.82      albertel 4029: sub get_scantron_config {
                   4030:     my ($which) = @_;
                   4031:     my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');
                   4032:     my %config;
1.157     albertel 4033:     #FIXME probably should move to XML it has already gotten a bit much now
1.82      albertel 4034:     foreach my $line (<$fh>) {
                   4035: 	my ($name,$descrip)=split(/:/,$line);
                   4036: 	if ($name ne $which ) { next; }
                   4037: 	chomp($line);
                   4038: 	my @config=split(/:/,$line);
                   4039: 	$config{'name'}=$config[0];
                   4040: 	$config{'description'}=$config[1];
                   4041: 	$config{'CODElocation'}=$config[2];
                   4042: 	$config{'CODEstart'}=$config[3];
                   4043: 	$config{'CODElength'}=$config[4];
                   4044: 	$config{'IDstart'}=$config[5];
                   4045: 	$config{'IDlength'}=$config[6];
                   4046: 	$config{'Qstart'}=$config[7];
                   4047: 	$config{'Qlength'}=$config[8];
                   4048: 	$config{'Qoff'}=$config[9];
                   4049: 	$config{'Qon'}=$config[10];
1.157     albertel 4050: 	$config{'PaperID'}=$config[11];
                   4051: 	$config{'PaperIDlength'}=$config[12];
                   4052: 	$config{'FirstName'}=$config[13];
                   4053: 	$config{'FirstNamelength'}=$config[14];
                   4054: 	$config{'LastName'}=$config[15];
                   4055: 	$config{'LastNamelength'}=$config[16];
1.82      albertel 4056: 	last;
                   4057:     }
                   4058:     return %config;
                   4059: }
                   4060: 
                   4061: sub username_to_idmap {
                   4062:     my ($classlist)= @_;
                   4063:     my %idmap;
                   4064:     foreach my $student (keys(%$classlist)) {
                   4065: 	$idmap{$classlist->{$student}->[&Apache::loncoursedata::CL_ID]}=
                   4066: 	    $student;
                   4067:     }
                   4068:     return %idmap;
                   4069: }
                   4070: 
1.157     albertel 4071: sub scantron_fixup_scanline {
                   4072:     my ($scantron_config,$scan_data,$line,$whichline,$field,$args)=@_;
                   4073:     if ($field eq 'ID') {
                   4074: 	if (length($args->{'newid'}) > $$scantron_config{'IDlength'}) {
1.186     albertel 4075: 	    return ($line,1,'New value too large');
1.157     albertel 4076: 	}
                   4077: 	if (length($args->{'newid'}) < $$scantron_config{'IDlength'}) {
                   4078: 	    $args->{'newid'}=sprintf('%-'.$$scantron_config{'IDlength'}.'s',
                   4079: 				     $args->{'newid'});
                   4080: 	}
                   4081: 	substr($line,$$scantron_config{'IDstart'}-1,
                   4082: 	       $$scantron_config{'IDlength'})=$args->{'newid'};
                   4083: 	if ($args->{'newid'}=~/^\s*$/) {
                   4084: 	    &scan_data($scan_data,"$whichline.user",
                   4085: 		       $args->{'username'}.':'.$args->{'domain'});
                   4086: 	}
1.186     albertel 4087:     } elsif ($field eq 'CODE') {
1.192     albertel 4088: 	if ($args->{'CODE_ignore_dup'}) {
                   4089: 	    &scan_data($scan_data,"$whichline.CODE_ignore_dup",'1');
                   4090: 	}
                   4091: 	&scan_data($scan_data,"$whichline.useCODE",'1');
                   4092: 	if ($args->{'CODE'} ne 'use_unfound') {
1.191     albertel 4093: 	    if (length($args->{'CODE'}) > $$scantron_config{'CODElength'}) {
                   4094: 		return ($line,1,'New CODE value too large');
                   4095: 	    }
                   4096: 	    if (length($args->{'CODE'}) < $$scantron_config{'CODElength'}) {
                   4097: 		$args->{'CODE'}=sprintf('%-'.$$scantron_config{'CODElength'}.'s',$args->{'CODE'});
                   4098: 	    }
                   4099: 	    substr($line,$$scantron_config{'CODEstart'}-1,
                   4100: 		   $$scantron_config{'CODElength'})=$args->{'CODE'};
1.186     albertel 4101: 	}
1.157     albertel 4102:     } elsif ($field eq 'answer') {
                   4103: 	my $length=$scantron_config->{'Qlength'};
                   4104: 	my $off=$scantron_config->{'Qoff'};
                   4105: 	my $on=$scantron_config->{'Qon'};
                   4106: 	my $answer=${off}x$length;
                   4107: 	if ($args->{'response'} eq 'none') {
                   4108: 	    &scan_data($scan_data,
                   4109: 		       "$whichline.no_bubble.".$args->{'question'},'1');
                   4110: 	} else {
                   4111: 	    substr($answer,$args->{'response'},1)=$on;
                   4112: 	    &scan_data($scan_data,
                   4113: 		       "$whichline.no_bubble.".$args->{'question'},undef,'1');
                   4114: 	}
                   4115: 	my $where=$length*($args->{'question'}-1)+$scantron_config->{'Qstart'};
                   4116: 	substr($line,$where-1,$length)=$answer;
                   4117:     }
                   4118:     return $line;
                   4119: }
                   4120: 
                   4121: sub scan_data {
                   4122:     my ($scan_data,$key,$value,$delete)=@_;
1.257     albertel 4123:     my $filename=$env{'form.scantron_selectfile'};
1.157     albertel 4124:     if (defined($value)) {
                   4125: 	$scan_data->{$filename.'_'.$key} = $value;
                   4126:     }
                   4127:     if ($delete) { delete($scan_data->{$filename.'_'.$key}); }
                   4128:     return $scan_data->{$filename.'_'.$key};
                   4129: }
                   4130: 
1.82      albertel 4131: sub scantron_parse_scanline {
1.194     albertel 4132:     my ($line,$whichline,$scantron_config,$scan_data,$justHeader)=@_;
1.82      albertel 4133:     my %record;
                   4134:     my $questions=substr($line,$$scantron_config{'Qstart'}-1);
                   4135:     my $data=substr($line,0,$$scantron_config{'Qstart'}-1);
                   4136:     if ($$scantron_config{'CODElocation'} ne 0) {
                   4137: 	if ($$scantron_config{'CODElocation'} < 0) {
1.191     albertel 4138: 	    $record{'scantron.CODE'}=substr($data,
                   4139: 					    $$scantron_config{'CODEstart'}-1,
1.83      albertel 4140: 					    $$scantron_config{'CODElength'});
1.191     albertel 4141: 	    if (&scan_data($scan_data,"$whichline.useCODE")) {
                   4142: 		$record{'scantron.useCODE'}=1;
                   4143: 	    }
1.192     albertel 4144: 	    if (&scan_data($scan_data,"$whichline.CODE_ignore_dup")) {
                   4145: 		$record{'scantron.CODE_ignore_dup'}=1;
                   4146: 	    }
1.82      albertel 4147: 	} else {
                   4148: 	    #FIXME interpret first N questions
                   4149: 	}
                   4150:     }
1.83      albertel 4151:     $record{'scantron.ID'}=substr($data,$$scantron_config{'IDstart'}-1,
                   4152: 				  $$scantron_config{'IDlength'});
1.157     albertel 4153:     $record{'scantron.PaperID'}=
                   4154: 	substr($data,$$scantron_config{'PaperID'}-1,
                   4155: 	       $$scantron_config{'PaperIDlength'});
                   4156:     $record{'scantron.FirstName'}=
                   4157: 	substr($data,$$scantron_config{'FirstName'}-1,
                   4158: 	       $$scantron_config{'FirstNamelength'});
                   4159:     $record{'scantron.LastName'}=
                   4160: 	substr($data,$$scantron_config{'LastName'}-1,
                   4161: 	       $$scantron_config{'LastNamelength'});
1.194     albertel 4162:     if ($justHeader) { return \%record; }
                   4163: 
1.82      albertel 4164:     my @alphabet=('A'..'Z');
                   4165:     my $questnum=0;
                   4166:     while ($questions) {
                   4167: 	$questnum++;
                   4168: 	my $currentquest=substr($questions,0,$$scantron_config{'Qlength'});
                   4169: 	substr($questions,0,$$scantron_config{'Qlength'})='';
1.83      albertel 4170: 	if (length($currentquest) < $$scantron_config{'Qlength'}) { next; }
1.239     albertel 4171: 	if ($$scantron_config{'Qon'} eq 'letter') {
                   4172: 	    if (!$currentquest || $currentquest eq $$scantron_config{'Qoff'} ||
                   4173: 		$currentquest !~ /^[A-Z]$/) {
                   4174: 		$record{"scantron.$questnum.answer"}='';
                   4175: 		if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) {
                   4176: 		    push(@{$record{"scantron.missingerror"}},$questnum);
                   4177: 		}
                   4178: 	    } else {
                   4179: 		$record{"scantron.$questnum.answer"}=$currentquest;
                   4180: 	    }
                   4181: 	} elsif ($$scantron_config{'Qon'} eq 'number') {
                   4182: 	    if (!$currentquest || $currentquest eq $$scantron_config{'Qoff'} ||
                   4183: 		$currentquest !~ /^\d$/) {
                   4184: 		$record{"scantron.$questnum.answer"}='';
                   4185: 		if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) {
                   4186: 		    push(@{$record{"scantron.missingerror"}},$questnum);
                   4187: 		}
                   4188: 	    } else {
                   4189: 		$record{"scantron.$questnum.answer"}=
                   4190: 		    $alphabet[$currentquest-1];
                   4191: 	    }
1.82      albertel 4192: 	} else {
1.239     albertel 4193: 	    my @array=split($$scantron_config{'Qon'},$currentquest,-1);
                   4194: 	    if (length($array[0]) eq $$scantron_config{'Qlength'}) {
                   4195: 		$record{"scantron.$questnum.answer"}='';
                   4196: 		if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) {
                   4197: 		    push(@{$record{"scantron.missingerror"}},$questnum);
                   4198: 		}
                   4199: 	    } else {
                   4200: 		$record{"scantron.$questnum.answer"}=
                   4201: 		    $alphabet[length($array[0])];
                   4202: 	    }
                   4203: 	    if (scalar(@array) gt 2) {
                   4204: 		push(@{$record{'scantron.doubleerror'}},$questnum);
                   4205: 		my @ans=@array;
                   4206: 		my $i=length($ans[0]);shift(@ans);
                   4207: 		while ($#ans) {
                   4208: 		    $i+=length($ans[0])+1;
                   4209: 		    $record{"scantron.$questnum.answer"}.=$alphabet[$i];
                   4210: 		    shift(@ans);
                   4211: 		}
                   4212: 	    }
1.82      albertel 4213: 	}
                   4214:     }
1.83      albertel 4215:     $record{'scantron.maxquest'}=$questnum;
                   4216:     return \%record;
1.82      albertel 4217: }
                   4218: 
                   4219: sub scantron_add_delay {
1.140     albertel 4220:     my ($delayqueue,$scanline,$errormessage,$errorcode)=@_;
                   4221:     push(@$delayqueue,
                   4222: 	 {'line' => $scanline, 'emsg' => $errormessage,
                   4223: 	  'ecode' => $errorcode }
                   4224: 	 );
1.82      albertel 4225: }
                   4226: 
                   4227: sub scantron_find_student {
1.157     albertel 4228:     my ($scantron_record,$scan_data,$idmap,$line)=@_;
1.83      albertel 4229:     my $scanID=$$scantron_record{'scantron.ID'};
1.157     albertel 4230:     if ($scanID =~ /^\s*$/) {
                   4231:  	return &scan_data($scan_data,"$line.user");
                   4232:     }
1.83      albertel 4233:     foreach my $id (keys(%$idmap)) {
1.157     albertel 4234:  	if (lc($id) eq lc($scanID)) {
                   4235:  	    return $$idmap{$id};
                   4236:  	}
1.83      albertel 4237:     }
                   4238:     return undef;
                   4239: }
                   4240: 
                   4241: sub scantron_filter {
                   4242:     my ($curres)=@_;
1.215     albertel 4243:                         # randomout is dysfunctional at best for this purpose
                   4244:     if (ref($curres) && $curres->is_problem()) { #&& !$curres->randomout) {
1.83      albertel 4245: 	return 1;
                   4246:     }
                   4247:     return 0;
1.82      albertel 4248: }
                   4249: 
1.157     albertel 4250: sub scantron_process_corrections {
                   4251:     my ($r) = @_;
1.257     albertel 4252:     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
1.157     albertel 4253:     my ($scanlines,$scan_data)=&scantron_getfile();
                   4254:     my $classlist=&Apache::loncoursedata::get_classlist();
1.257     albertel 4255:     my $which=$env{'form.scantron_line'};
1.200     albertel 4256:     my $line=&scantron_get_line($scanlines,$scan_data,$which);
1.157     albertel 4257:     my ($skip,$err,$errmsg);
1.257     albertel 4258:     if ($env{'form.scantron_skip_record'}) {
1.157     albertel 4259: 	$skip=1;
1.257     albertel 4260:     } elsif ($env{'form.scantron_corrections'} =~ /^(duplicate|incorrect)ID$/) {
                   4261: 	my $newstudent=$env{'form.scantron_username'}.':'.
                   4262: 	    $env{'form.scantron_domain'};
1.157     albertel 4263: 	my $newid=$classlist->{$newstudent}->[&Apache::loncoursedata::CL_ID];
                   4264: 	($line,$err,$errmsg)=
                   4265: 	    &scantron_fixup_scanline(\%scantron_config,$scan_data,$line,$which,
                   4266: 				     'ID',{'newid'=>$newid,
1.257     albertel 4267: 				    'username'=>$env{'form.scantron_username'},
                   4268: 				    'domain'=>$env{'form.scantron_domain'}});
                   4269:     } elsif ($env{'form.scantron_corrections'} =~ /^(duplicate|incorrect)CODE$/) {
                   4270: 	my $resolution=$env{'form.scantron_CODE_resolution'};
1.190     albertel 4271: 	my $newCODE;
1.192     albertel 4272: 	my %args;
1.190     albertel 4273: 	if      ($resolution eq 'use_unfound') {
1.191     albertel 4274: 	    $newCODE='use_unfound';
1.190     albertel 4275: 	} elsif ($resolution eq 'use_found') {
1.257     albertel 4276: 	    $newCODE=$env{'form.scantron_CODE_selectedvalue'};
1.190     albertel 4277: 	} elsif ($resolution eq 'use_typed') {
1.257     albertel 4278: 	    $newCODE=$env{'form.scantron_CODE_newvalue'};
1.194     albertel 4279: 	} elsif ($resolution =~ /^use_closest_(\d+)/) {
1.257     albertel 4280: 	    $newCODE=$env{"form.scantron_CODE_closest_$1"};
1.190     albertel 4281: 	}
1.257     albertel 4282: 	if ($env{'form.scantron_corrections'} eq 'duplicateCODE') {
1.192     albertel 4283: 	    $args{'CODE_ignore_dup'}=1;
                   4284: 	}
                   4285: 	$args{'CODE'}=$newCODE;
1.186     albertel 4286: 	($line,$err,$errmsg)=
                   4287: 	    &scantron_fixup_scanline(\%scantron_config,$scan_data,$line,$which,
1.192     albertel 4288: 				     'CODE',\%args);
1.257     albertel 4289:     } elsif ($env{'form.scantron_corrections'} =~ /^(missing|double)bubble$/) {
                   4290: 	foreach my $question (split(',',$env{'form.scantron_questions'})) {
1.157     albertel 4291: 	    ($line,$err,$errmsg)=
                   4292: 		&scantron_fixup_scanline(\%scantron_config,$scan_data,$line,
                   4293: 					 $which,'answer',
                   4294: 					 { 'question'=>$question,
1.257     albertel 4295: 		       'response'=>$env{"form.scantron_correct_Q_$question"}});
1.157     albertel 4296: 	    if ($err) { last; }
                   4297: 	}
                   4298:     }
                   4299:     if ($err) {
                   4300: 	$r->print("Unable to accept last correction, an error occurred :$errmsg:");
                   4301:     } else {
1.200     albertel 4302: 	&scantron_put_line($scanlines,$scan_data,$which,$line,$skip);
1.157     albertel 4303: 	&scantron_putfile($scanlines,$scan_data);
                   4304:     }
                   4305: }
                   4306: 
1.200     albertel 4307: sub reset_skipping_status {
                   4308:     my ($scanlines,$scan_data)=&scantron_getfile();
                   4309:     &scan_data($scan_data,'remember_skipping',undef,1);
                   4310:     &scantron_putfile(undef,$scan_data);
                   4311: }
                   4312: 
                   4313: sub allow_skipping {
                   4314:     my ($scan_data,$i)=@_;
                   4315:     my %remembered=split(':',&scan_data($scan_data,'remember_skipping'));
                   4316:     delete($remembered{$i});
                   4317:     &scan_data($scan_data,'remember_skipping',join(':',%remembered));
                   4318: }
                   4319: 
                   4320: sub should_be_skipped {
                   4321:     my ($scan_data,$i)=@_;
1.257     albertel 4322:     if ($env{'form.scantron_options_redo'} !~ /^redo_/) {
1.200     albertel 4323: 	# not redoing old skips
                   4324: 	return 0;
                   4325:     }
                   4326:     my %remembered=split(':',&scan_data($scan_data,'remember_skipping'));
                   4327:     if (exists($remembered{$i})) { return 0; }
                   4328:     return 1;
                   4329: }
                   4330: 
                   4331: sub remember_current_skipped {
                   4332:     my ($scanlines,$scan_data)=&scantron_getfile();
                   4333:     my %to_remember;
                   4334:     for (my $i=0;$i<=$scanlines->{'count'};$i++) {
                   4335: 	if ($scanlines->{'skipped'}[$i]) {
                   4336: 	    $to_remember{$i}=1;
                   4337: 	}
                   4338:     }
                   4339:     &Apache::lonnet::logthis('remembering '.join(':',%to_remember));
                   4340:     &scan_data($scan_data,'remember_skipping',join(':',%to_remember));
                   4341:     &scantron_putfile(undef,$scan_data);
                   4342: }
                   4343: 
                   4344: sub check_for_error {
                   4345:     my ($r,$result)=@_;
                   4346:     if ($result ne 'ok' && $result ne 'not_found' ) {
                   4347: 	$r->print("An error occured ($result) when trying to Remove the existing corrections.");
                   4348:     }
                   4349: }
1.157     albertel 4350: 
1.203     albertel 4351: sub scantron_warning_screen {
                   4352:     my ($button_text)=@_;
1.257     albertel 4353:     my $title=&Apache::lonnet::gettitle($env{'form.selectpage'});
1.203     albertel 4354:     return (<<STUFF);
                   4355: <p>
                   4356: <font color="red">Please double check the information
                   4357:                  below before clicking on '$button_text'</font>
                   4358: </p>
                   4359: <table>
                   4360: <tr><td><b>Sequence To be Graded:</b></td><td>$title</td></tr>
1.257     albertel 4361: <tr><td><b>Data File that will be used:</b></td><td><tt>$env{'form.scantron_selectfile'}</tt></td></tr>
1.203     albertel 4362: </table>
                   4363: </font>
                   4364: <br />
                   4365: <p> If this information is correct, please click on '$button_text'.</p>
                   4366: <p> If something is incorrect, please click the 'Grading Menu' button to start over.</p>
                   4367: 
                   4368: <br />
                   4369: STUFF
                   4370: }
                   4371: 
                   4372: sub scantron_do_warning {
                   4373:     my ($r)=@_;
                   4374:     my ($symb,$url)=&get_symb_and_url($r);
                   4375:     if (!$symb) {return '';}
                   4376:     my $default_form_data=&defaultFormData($symb,$url);
                   4377:     $r->print(&scantron_form_start().$default_form_data);
1.257     albertel 4378:     if ( $env{'form.selectpage'} eq '' ||
                   4379: 	 $env{'form.scantron_selectfile'} eq '' ||
                   4380: 	 $env{'form.scantron_format'} eq '' ) {
1.237     albertel 4381: 	$r->print("<p>You have forgetten to specify some information. Please go Back and try again.</p>");
1.257     albertel 4382: 	if ( $env{'form.selectpage'} eq '') {
1.237     albertel 4383: 	    $r->print('<p><font color="red">You have not selected a Sequence to grade</font></p>');
                   4384: 	} 
1.257     albertel 4385: 	if ( $env{'form.scantron_selectfile'} eq '') {
1.237     albertel 4386: 	    $r->print('<p><font color="red">You have not selected a file that contains the student\'s response data.</font></p>');
                   4387: 	} 
1.257     albertel 4388: 	if ( $env{'form.scantron_format'} eq '') {
1.237     albertel 4389: 	    $r->print('<p><font color="red">You have not selected a the format of the student\'s response data.</font></p>');
                   4390: 	} 
                   4391:     } else {
1.265     www      4392: 	my $warning=&scantron_warning_screen('Grading: Validate Records');
1.237     albertel 4393: 	$r->print(<<STUFF);
1.203     albertel 4394: $warning
1.265     www      4395: <input type="submit" name="submit" value="Grading: Validate Records" />
1.203     albertel 4396: <input type="hidden" name="command" value="scantron_validate" />
                   4397: STUFF
1.237     albertel 4398:     }
                   4399:     $r->print("</form><br />".&show_grading_menu_form($symb,$url)."</body></html>");
1.203     albertel 4400:     return '';
                   4401: }
                   4402: 
                   4403: sub scantron_form_start {
                   4404:     my ($max_bubble)=@_;
                   4405:     my $result= <<SCANTRONFORM;
                   4406: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload">
1.257     albertel 4407:   <input type="hidden" name="selectpage" value="$env{'form.selectpage'}" />
                   4408:   <input type="hidden" name="scantron_format" value="$env{'form.scantron_format'}" />
                   4409:   <input type="hidden" name="scantron_selectfile" value="$env{'form.scantron_selectfile'}" />
1.218     albertel 4410:   <input type="hidden" name="scantron_maxbubble" value="$max_bubble" />
1.257     albertel 4411:   <input type="hidden" name="scantron_CODElist" value="$env{'form.scantron_CODElist'}" />
                   4412:   <input type="hidden" name="scantron_CODEunique" value="$env{'form.scantron_CODEunique'}" />
                   4413:   <input type="hidden" name="scantron_options_redo" value="$env{'form.scantron_options_redo'}" />
                   4414:   <input type="hidden" name="scantron_options_ignore" value="$env{'form.scantron_options_ignore'}" />
1.203     albertel 4415: SCANTRONFORM
                   4416:     return $result;
                   4417: }
                   4418: 
1.157     albertel 4419: sub scantron_validate_file {
                   4420:     my ($r) = @_;
                   4421:     my ($symb,$url)=&get_symb_and_url($r);
                   4422:     if (!$symb) {return '';}
                   4423:     my $default_form_data=&defaultFormData($symb,$url);
1.200     albertel 4424:     
                   4425:     # do the detection of only doing skipped records first befroe we delete
                   4426:     # them  when doing the corrections reset
1.257     albertel 4427:     if ($env{'form.scantron_options_redo'} ne 'redo_skipped_ready') {
1.200     albertel 4428: 	&reset_skipping_status();
                   4429:     }
1.257     albertel 4430:     if ($env{'form.scantron_options_redo'} eq 'redo_skipped') {
1.200     albertel 4431: 	&remember_current_skipped();
                   4432: 	&scantron_remove_file('skipped');
1.257     albertel 4433: 	$env{'form.scantron_options_redo'}='redo_skipped_ready';
1.200     albertel 4434:     }
                   4435: 
1.257     albertel 4436:     if ($env{'form.scantron_options_ignore'} eq 'ignore_corrections') {
1.200     albertel 4437: 	&check_for_error($r,&scantron_remove_file('corrected'));
                   4438: 	&check_for_error($r,&scantron_remove_file('skipped'));
                   4439: 	&check_for_error($r,&scantron_remove_scan_data());
1.257     albertel 4440: 	$env{'form.scantron_options_ignore'}='done';
1.192     albertel 4441:     }
1.200     albertel 4442: 
1.257     albertel 4443:     if ($env{'form.scantron_corrections'}) {
1.157     albertel 4444: 	&scantron_process_corrections($r);
                   4445:     }
1.191     albertel 4446:     $r->print("<p>Gathering neccessary info.</p>");$r->rflush();
1.157     albertel 4447:     #get the student pick code ready
                   4448:     $r->print(&Apache::loncommon::studentbrowser_javascript());
1.203     albertel 4449:     my $max_bubble=&scantron_get_maxbubble($r);
                   4450:     my $result=&scantron_form_start($max_bubble).$default_form_data;
1.157     albertel 4451:     $r->print($result);
                   4452:     
                   4453:     my @validate_phases=( 'ID',
                   4454: 			  'CODE',
                   4455: 			  'doublebubble',
                   4456: 			  'missingbubbles');
1.257     albertel 4457:     if (!$env{'form.validatepass'}) {
                   4458: 	$env{'form.validatepass'} = 0;
1.157     albertel 4459:     }
1.257     albertel 4460:     my $currentphase=$env{'form.validatepass'};
1.157     albertel 4461: 
                   4462:     my $stop=0;
                   4463:     while (!$stop && $currentphase < scalar(@validate_phases)) {
                   4464: 	$r->print("<p> Validating ".$validate_phases[$currentphase]."</p>");
                   4465: 	$r->rflush();
                   4466: 	my $which="scantron_validate_".$validate_phases[$currentphase];
                   4467: 	{
                   4468: 	    no strict 'refs';
                   4469: 	    ($stop,$currentphase)=&$which($r,$currentphase);
                   4470: 	}
                   4471:     }
                   4472:     if (!$stop) {
1.203     albertel 4473: 	my $warning=&scantron_warning_screen('Start Grading');
                   4474: 	$r->print(<<STUFF);
                   4475: Validation process complete.<br />
                   4476: $warning
                   4477: <input type="submit" name="submit" value="Start Grading" />
                   4478: <input type="hidden" name="command" value="scantron_process" />
                   4479: STUFF
                   4480: 
1.157     albertel 4481:     } else {
                   4482: 	$r->print('<input type="hidden" name="command" value="scantron_validate" />');
                   4483: 	$r->print("<input type='hidden' name='validatepass' value='".$currentphase."' />");
                   4484:     }
                   4485:     if ($stop) {
                   4486: 	$r->print('<input type="submit" name="submit" value="Continue ->" />');
                   4487: 	$r->print(' using corrected info <br />');
                   4488: 	$r->print("<input type='submit' value='Skip' name='scantron_skip_record' />");
                   4489: 	$r->print(" this scanline saving it for later.");
                   4490:     }
                   4491:     $r->print(" </form><br />".&show_grading_menu_form($symb,$url).
                   4492: 	      "</body></html>");
                   4493:     return '';
                   4494: }
                   4495: 
1.200     albertel 4496: sub scantron_remove_file {
1.192     albertel 4497:     my ($which)=@_;
1.257     albertel 4498:     my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
                   4499:     my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
1.192     albertel 4500:     my $file='scantron_';
1.200     albertel 4501:     if ($which eq 'corrected' || $which eq 'skipped') {
                   4502: 	$file.=$which.'_';
1.192     albertel 4503:     } else {
                   4504: 	return 'refused';
                   4505:     }
1.257     albertel 4506:     $file.=$env{'form.scantron_selectfile'};
1.200     albertel 4507:     return &Apache::lonnet::removeuserfile($cname,$cdom,$file);
                   4508: }
                   4509: 
                   4510: sub scantron_remove_scan_data {
1.257     albertel 4511:     my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
                   4512:     my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
1.192     albertel 4513:     my @keys=&Apache::lonnet::getkeys('nohist_scantrondata',$cdom,$cname);
                   4514:     my @todelete;
1.257     albertel 4515:     my $filename=$env{'form.scantron_selectfile'};
1.192     albertel 4516:     foreach my $key (@keys) {
                   4517: 	if ($key=~/^\Q$filename\E_/) {
1.257     albertel 4518: 	    if ($env{'form.scantron_options_redo'} eq 'redo_skipped_ready' &&
1.200     albertel 4519: 		$key=~/remember_skipping/) {
                   4520: 		next;
                   4521: 	    }
1.192     albertel 4522: 	    push(@todelete,$key);
                   4523: 	}
                   4524:     }
1.200     albertel 4525:     my $result;
1.192     albertel 4526:     if (@todelete) {
1.200     albertel 4527: 	$result=&Apache::lonnet::del('nohist_scantrondata',\@todelete,$cdom,$cname);
1.192     albertel 4528:     }
                   4529:     return $result;
                   4530: }
                   4531: 
1.157     albertel 4532: sub scantron_getfile {
1.200     albertel 4533:     #FIXME really would prefer a scantron directory
1.257     albertel 4534:     my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
                   4535:     my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
1.157     albertel 4536:     my $lines;
                   4537:     $lines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.
1.257     albertel 4538: 		       'scantron_orig_'.$env{'form.scantron_selectfile'});
1.157     albertel 4539:     my %scanlines;
                   4540:     $scanlines{'orig'}=[(split("\n",$lines,-1))];
                   4541:     my $temp=$scanlines{'orig'};
                   4542:     $scanlines{'count'}=$#$temp;
                   4543: 
                   4544:     $lines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.
1.257     albertel 4545: 		       'scantron_corrected_'.$env{'form.scantron_selectfile'});
1.157     albertel 4546:     if ($lines eq '-1') {
                   4547: 	$scanlines{'corrected'}=[];
                   4548:     } else {
                   4549: 	$scanlines{'corrected'}=[(split("\n",$lines,-1))];
                   4550:     }
                   4551:     $lines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.
1.257     albertel 4552: 		       'scantron_skipped_'.$env{'form.scantron_selectfile'});
1.157     albertel 4553:     if ($lines eq '-1') {
                   4554: 	$scanlines{'skipped'}=[];
                   4555:     } else {
                   4556: 	$scanlines{'skipped'}=[(split("\n",$lines,-1))];
                   4557:     }
1.175     albertel 4558:     my @tmp=&Apache::lonnet::dump('nohist_scantrondata',$cdom,$cname);
1.157     albertel 4559:     if ($tmp[0] =~ /^(error:|no_such_host)/) { @tmp=(); }
                   4560:     my %scan_data = @tmp;
                   4561:     return (\%scanlines,\%scan_data);
                   4562: }
                   4563: 
                   4564: sub lonnet_putfile {
                   4565:     my ($contents,$filename)=@_;
1.257     albertel 4566:     my $docuname=$env{'course.'.$env{'request.course.id'}.'.num'};
                   4567:     my $docudom=$env{'course.'.$env{'request.course.id'}.'.domain'};
                   4568:     my $docuhome=$env{'course.'.$env{'request.course.id'}.'.home'};
                   4569:     $env{'form.sillywaytopassafilearound'}=$contents;
1.157     albertel 4570:     &Apache::lonnet::finishuserfileupload($docuname,$docudom,$docuhome,'sillywaytopassafilearound',$filename);
                   4571: 
                   4572: }
                   4573: 
                   4574: sub scantron_putfile {
                   4575:     my ($scanlines,$scan_data) = @_;
1.200     albertel 4576:     #FIXME really would prefer a scantron directory
1.257     albertel 4577:     my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
                   4578:     my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
1.200     albertel 4579:     if ($scanlines) {
                   4580: 	my $prefix='scantron_';
1.157     albertel 4581: # no need to update orig, shouldn't change
                   4582: #   &lonnet_putfile(join("\n",@{$scanlines->{'orig'}}),$prefix.'orig_'.
1.257     albertel 4583: #		    $env{'form.scantron_selectfile'});
1.200     albertel 4584: 	&lonnet_putfile(join("\n",@{$scanlines->{'corrected'}}),
                   4585: 			$prefix.'corrected_'.
1.257     albertel 4586: 			$env{'form.scantron_selectfile'});
1.200     albertel 4587: 	&lonnet_putfile(join("\n",@{$scanlines->{'skipped'}}),
                   4588: 			$prefix.'skipped_'.
1.257     albertel 4589: 			$env{'form.scantron_selectfile'});
1.200     albertel 4590:     }
1.175     albertel 4591:     &Apache::lonnet::put('nohist_scantrondata',$scan_data,$cdom,$cname);
1.157     albertel 4592: }
                   4593: 
                   4594: sub scantron_get_line {
1.200     albertel 4595:     my ($scanlines,$scan_data,$i)=@_;
                   4596:     if (&should_be_skipped($scan_data,$i)) { return undef; }
                   4597:     if ($scanlines->{'skipped'}[$i]) { return undef; }
1.157     albertel 4598:     if ($scanlines->{'corrected'}[$i]) {return $scanlines->{'corrected'}[$i];}
                   4599:     return $scanlines->{'orig'}[$i]; 
                   4600: }
                   4601: 
1.200     albertel 4602: sub get_todo_count {
                   4603:     my ($scanlines,$scan_data)=@_;
                   4604:     my $count=0;
                   4605:     for (my $i=0;$i<=$scanlines->{'count'};$i++) {
                   4606: 	my $line=&scantron_get_line($scanlines,$scan_data,$i);
                   4607: 	if ($line=~/^[\s\cz]*$/) { next; }
                   4608: 	$count++;
                   4609:     }
                   4610:     return $count;
                   4611: }
                   4612: 
1.157     albertel 4613: sub scantron_put_line {
1.200     albertel 4614:     my ($scanlines,$scan_data,$i,$newline,$skip)=@_;
1.157     albertel 4615:     if ($skip) {
                   4616: 	$scanlines->{'skipped'}[$i]=$newline;
1.200     albertel 4617: 	&allow_skipping($scan_data,$i);
1.157     albertel 4618: 	return;
                   4619:     }
                   4620:     $scanlines->{'corrected'}[$i]=$newline;
                   4621: }
                   4622: 
                   4623: sub scantron_validate_ID {
                   4624:     my ($r,$currentphase) = @_;
                   4625:     
                   4626:     #get student info
                   4627:     my $classlist=&Apache::loncoursedata::get_classlist();
                   4628:     my %idmap=&username_to_idmap($classlist);
                   4629: 
                   4630:     #get scantron line setup
1.257     albertel 4631:     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
1.157     albertel 4632:     my ($scanlines,$scan_data)=&scantron_getfile();
                   4633: 
                   4634:     my %found=('ids'=>{},'usernames'=>{});
                   4635:     for (my $i=0;$i<=$scanlines->{'count'};$i++) {
1.200     albertel 4636: 	my $line=&scantron_get_line($scanlines,$scan_data,$i);
1.157     albertel 4637: 	if ($line=~/^[\s\cz]*$/) { next; }
                   4638: 	my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
                   4639: 						 $scan_data);
                   4640: 	my $id=$$scan_record{'scantron.ID'};
                   4641: 	my $found;
                   4642: 	foreach my $checkid (keys(%idmap)) {
                   4643: 	    if (lc($checkid) eq lc($id)) { $found=$checkid;last; }
                   4644: 	}
                   4645: 	if ($found) {
                   4646: 	    my $username=$idmap{$found};
                   4647: 	    if ($found{'ids'}{$found}) {
                   4648: 		&scantron_get_correction($r,$i,$scan_record,\%scantron_config,
                   4649: 					 $line,'duplicateID',$found);
1.194     albertel 4650: 		return(1,$currentphase);
1.157     albertel 4651: 	    } elsif ($found{'usernames'}{$username}) {
                   4652: 		&scantron_get_correction($r,$i,$scan_record,\%scantron_config,
                   4653: 					 $line,'duplicateID',$username);
1.194     albertel 4654: 		return(1,$currentphase);
1.157     albertel 4655: 	    }
1.186     albertel 4656: 	    #FIXME store away line we previously saw the ID on to use above
1.157     albertel 4657: 	    $found{'ids'}{$found}++;
                   4658: 	    $found{'usernames'}{$username}++;
                   4659: 	} else {
                   4660: 	    if ($id =~ /^\s*$/) {
1.158     albertel 4661: 		my $username=&scan_data($scan_data,"$i.user");
1.157     albertel 4662: 		if (defined($username) && $found{'usernames'}{$username}) {
                   4663: 		    &scantron_get_correction($r,$i,$scan_record,
                   4664: 					     \%scantron_config,
                   4665: 					     $line,'duplicateID',$username);
1.194     albertel 4666: 		    return(1,$currentphase);
1.157     albertel 4667: 		} elsif (!defined($username)) {
                   4668: 		    &scantron_get_correction($r,$i,$scan_record,
                   4669: 					     \%scantron_config,
                   4670: 					     $line,'incorrectID');
1.194     albertel 4671: 		    return(1,$currentphase);
1.157     albertel 4672: 		}
                   4673: 		$found{'usernames'}{$username}++;
                   4674: 	    } else {
                   4675: 		&scantron_get_correction($r,$i,$scan_record,\%scantron_config,
                   4676: 					 $line,'incorrectID');
1.194     albertel 4677: 		return(1,$currentphase);
1.157     albertel 4678: 	    }
                   4679: 	}
                   4680:     }
                   4681: 
                   4682:     return (0,$currentphase+1);
                   4683: }
                   4684: 
                   4685: sub scantron_get_correction {
                   4686:     my ($r,$i,$scan_record,$scan_config,$line,$error,$arg)=@_;
                   4687: 
                   4688: #FIXME in the case of a duplicated ID the previous line, probaly need
                   4689: #to show both the current line and the previous one and allow skipping
                   4690: #the previous one or the current one
                   4691: 
1.161     albertel 4692:     $r->print("<p><b>An error was detected ($error)</b>");
1.157     albertel 4693:     if ( defined($$scan_record{'scantron.PaperID'}) ) {
                   4694: 	$r->print(" for PaperID <tt>".
                   4695: 		  $$scan_record{'scantron.PaperID'}."</tt> \n");
                   4696:     } else {
                   4697: 	$r->print(" in scanline $i <pre>".
                   4698: 		  $line."</pre> \n");
                   4699:     }
1.242     albertel 4700:     my $message="<p>The ID on the form is  <tt>".
                   4701: 	$$scan_record{'scantron.ID'}."</tt><br />\n".
                   4702: 	"The name on the paper is ".
                   4703: 	$$scan_record{'scantron.LastName'}.",".
                   4704: 	$$scan_record{'scantron.FirstName'}."</p>";
                   4705: 
1.157     albertel 4706:     $r->print('<input type="hidden" name="scantron_corrections" value="'.$error.'" />'."\n");
                   4707:     $r->print('<input type="hidden" name="scantron_line" value="'.$i.'" />'."\n");
                   4708:     if ($error =~ /ID$/) {
1.186     albertel 4709: 	if ($error eq 'incorrectID') {
1.157     albertel 4710: 	    $r->print("The encoded ID is not in the classlist</p>\n");
                   4711: 	} elsif ($error eq 'duplicateID') {
                   4712: 	    $r->print("The encoded ID has also been used by a previous paper $arg</p>\n");
                   4713: 	}
1.242     albertel 4714: 	$r->print($message);
1.157     albertel 4715: 	$r->print("<p>How should I handle this? <br /> \n");
                   4716: 	$r->print("\n<ul><li> ");
                   4717: 	#FIXME it would be nice if this sent back the user ID and
                   4718: 	#could do partial userID matches
                   4719: 	$r->print(&Apache::loncommon::selectstudent_link('scantronupload',
                   4720: 				       'scantron_username','scantron_domain'));
                   4721: 	$r->print(": <input type='text' name='scantron_username' value='' />");
                   4722: 	$r->print("\n@".
1.257     albertel 4723: 		 &Apache::loncommon::select_dom_form($env{'request.role.domain'},'scantron_domain'));
1.157     albertel 4724: 
                   4725: 	$r->print('</li>');
1.186     albertel 4726:     } elsif ($error =~ /CODE$/) {
                   4727: 	if ($error eq 'incorrectCODE') {
1.187     albertel 4728: 	    $r->print("</p><p>The encoded CODE is not in the list of possible CODEs</p>\n");
1.186     albertel 4729: 	} elsif ($error eq 'duplicateCODE') {
1.194     albertel 4730: 	    $r->print("</p><p>The encoded CODE has also been used by a previous paper ".join(', ',@{$arg}).", and CODEs are supposed to be unique</p>\n");
1.186     albertel 4731: 	}
1.224     albertel 4732: 	$r->print("<p>The CODE on the form is  <tt>'".
                   4733: 		  $$scan_record{'scantron.CODE'}."'</tt><br />\n");
1.242     albertel 4734: 	$r->print($message);
1.186     albertel 4735: 	$r->print("<p>How should I handle this? <br /> \n");
1.187     albertel 4736: 	$r->print("\n<br /> ");
1.194     albertel 4737: 	my $i=0;
                   4738: 	if ($error eq 'incorrectCODE') {
                   4739: 	    my ($max,$closest)=&scantron_get_closely_matching_CODEs($arg,$$scan_record{'scantron.CODE'});
                   4740: 	    foreach my $testcode (@{$closest}) {
                   4741: 		my $checked='';
                   4742: 		if (!$i) { $checked=' checked="on" '; }
1.272   ! albertel 4743: 		$r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_closest_$i' $checked /> Use the similar CODE <b><tt>".$testcode."</tt></b> instead.</label><input type='hidden' name='scantron_CODE_closest_$i' value='$testcode' />");
1.194     albertel 4744: 		$r->print("\n<br />");
                   4745: 		$i++;
                   4746: 	    }
                   4747: 	}
                   4748: 	my $checked; if (!$i) { $checked=' checked="on" '; }
1.272   ! albertel 4749: 	$r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_unfound' $checked /> Use the CODE <b><tt>".$$scan_record{'scantron.CODE'}."</tt></b> that is was on the paper, ignoring the error.</label>");
1.187     albertel 4750: 	$r->print("\n<br />");
1.194     albertel 4751: 
1.188     albertel 4752: 	$r->print(<<ENDSCRIPT);
                   4753: <script type="text/javascript">
                   4754: function change_radio(field) {
1.190     albertel 4755:     var slct=document.scantronupload.scantron_CODE_resolution;
1.188     albertel 4756:     var i;
                   4757:     for (i=0;i<slct.length;i++) {
                   4758:         if (slct[i].value==field) { slct[i].checked=true; }
                   4759:     }
                   4760: }
                   4761: </script>
                   4762: ENDSCRIPT
1.187     albertel 4763: 	my $href="/adm/pickcode?".
                   4764: 	   "form=".&Apache::lonnet::escape("scantronupload").
1.257     albertel 4765: 	   "&scantron_format=".&Apache::lonnet::escape($env{'form.scantron_format'}).
                   4766: 	   "&scantron_CODElist=".&Apache::lonnet::escape($env{'form.scantron_CODElist'}).
1.187     albertel 4767: 	   "&curCODE=".&Apache::lonnet::escape($$scan_record{'scantron.CODE'}).
1.257     albertel 4768: 	   "&scantron_selectfile=".&Apache::lonnet::escape($env{'form.scantron_selectfile'});
1.272   ! albertel 4769: 	$r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_found' /> <a target='_blank' href='$href'>Select</a> a CODE from the list of all CODEs and use it.</label> Selected CODE is <input readonly='true' type='text' size='8' name='scantron_CODE_selectedvalue' onfocus=\"javascript:change_radio('use_found')\" onchange=\"javascript:change_radio('use_found')\" />");
1.187     albertel 4770: 	$r->print("\n<br />");
1.272   ! albertel 4771: 	$r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_typed' /> Use </label><input type='text' size='8' name='scantron_CODE_newvalue' onfocus=\"javascript:change_radio('use_typed')\" onkeypress=\"javascript:change_radio('use_typed')\" /> as the CODE.");
1.187     albertel 4772: 	$r->print("\n<br /><br />");
1.157     albertel 4773:     } elsif ($error eq 'doublebubble') {
                   4774: 	$r->print("<p>There have been multiple bubbles scanned for a some question(s)</p>\n");
                   4775: 	$r->print('<input type="hidden" name="scantron_questions" value="'.
                   4776: 		  join(',',@{$arg}).'" />');
1.242     albertel 4777: 	$r->print($message);
1.157     albertel 4778: 	$r->print("<p>Please indicate which bubble should be used for grading</p>");
                   4779: 	foreach my $question (@{$arg}) {
                   4780: 	    my $selected=$$scan_record{"scantron.$question.answer"};
                   4781: 	    &scantron_bubble_selector($r,$scan_config,$question,split('',$selected));
                   4782: 	}
                   4783:     } elsif ($error eq 'missingbubble') {
                   4784: 	$r->print("<p>There have been <b>no</b> bubbles scanned for some question(s)</p>\n");
1.242     albertel 4785: 	$r->print($message);
1.157     albertel 4786: 	$r->print("<p>Please indicate which bubble should be used for grading</p>");
                   4787: 	$r->print("Some questions have no scanned bubbles\n");
                   4788: 	$r->print('<input type="hidden" name="scantron_questions" value="'.
                   4789: 		  join(',',@{$arg}).'" />');
                   4790: 	foreach my $question (@{$arg}) {
                   4791: 	    my $selected=$$scan_record{"scantron.$question.answer"};
                   4792: 	    &scantron_bubble_selector($r,$scan_config,$question);
                   4793: 	}
                   4794:     } else {
                   4795: 	$r->print("\n<ul>");
                   4796:     }
                   4797:     $r->print("\n</li></ul>");
                   4798: 
                   4799: }
                   4800: 
                   4801: sub scantron_bubble_selector {
                   4802:     my ($r,$scan_config,$quest,@selected)=@_;
                   4803:     my $max=$$scan_config{'Qlength'};
                   4804:     my @alphabet=('A'..'Z');
                   4805:     $r->print("<table border='1'><tr><td rowspan='2'>$quest</td>");
                   4806:     for (my $i=0;$i<$max+1;$i++) {
                   4807: 	$r->print('<td align="center">');
                   4808: 	if ($selected[0] eq $alphabet[$i]) { $r->print('X'); shift(@selected) }
                   4809: 	else { $r->print('&nbsp;'); }
                   4810: 	$r->print('</td>');
                   4811:     }
                   4812:     $r->print('<td></td></tr><tr>');
                   4813:     for (my $i=0;$i<$max;$i++) {
1.272   ! albertel 4814: 	$r->print('<td><label><input type="radio" name="scantron_correct_Q_'.
        !          4815: 		  $quest.'" value="'.$i.'" />'.$alphabet[$i]."</label></td>");
1.157     albertel 4816:     }
1.272   ! albertel 4817:     $r->print('<td><label><input type="radio" name="scantron_correct_Q_'.
        !          4818: 	      $quest.'" value="none" /> No bubble </label></td>');
1.157     albertel 4819:     $r->print('</tr></table>');
                   4820: }
                   4821: 
1.194     albertel 4822: sub num_matches {
                   4823:     my ($orig,$code) = @_;
                   4824:     my @code=split(//,$code);
                   4825:     my @orig=split(//,$orig);
                   4826:     my $same=0;
                   4827:     for (my $i=0;$i<scalar(@code);$i++) {
                   4828: 	if ($code[$i] eq $orig[$i]) { $same++; }
                   4829:     }
                   4830:     return $same;
                   4831: }
                   4832: 
                   4833: sub scantron_get_closely_matching_CODEs {
                   4834:     my ($allcodes,$CODE)=@_;
                   4835:     my @CODEs;
                   4836:     foreach my $testcode (sort(keys(%{$allcodes}))) {
                   4837: 	push(@{$CODEs[&num_matches($CODE,$testcode)]},$testcode);
                   4838:     }
                   4839: 
                   4840:     return ($#CODEs,$CODEs[-1]);
                   4841: }
                   4842: 
                   4843: sub get_codes {
1.257     albertel 4844:     my $old_name=$env{'form.scantron_CODElist'};
                   4845:     my $cdom =$env{'course.'.$env{'request.course.id'}.'.domain'};
                   4846:     my $cnum =$env{'course.'.$env{'request.course.id'}.'.num'};
1.194     albertel 4847:     my %result=&Apache::lonnet::get('CODEs',[$old_name],$cdom,$cnum);
                   4848:     my %allcodes=map {(&Apache::lonprintout::num_to_letters($_),1)} split(',',$result{$old_name});
                   4849:     return %allcodes;
                   4850: }
                   4851: 
1.157     albertel 4852: sub scantron_validate_CODE {
                   4853:     my ($r,$currentphase) = @_;
1.257     albertel 4854:     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
1.186     albertel 4855:     if ($scantron_config{'CODElocation'} &&
                   4856: 	$scantron_config{'CODEstart'} &&
                   4857: 	$scantron_config{'CODElength'}) {
1.257     albertel 4858: 	if (!defined($env{'form.scantron_CODElist'})) {
1.186     albertel 4859: 	    &FIXME_blow_up()
                   4860: 	}
                   4861:     } else {
                   4862: 	return (0,$currentphase+1);
                   4863:     }
                   4864:     
                   4865:     my %usedCODEs;
                   4866: 
1.194     albertel 4867:     my %allcodes=&get_codes();
1.186     albertel 4868: 
                   4869:     my ($scanlines,$scan_data)=&scantron_getfile();
                   4870:     for (my $i=0;$i<=$scanlines->{'count'};$i++) {
1.200     albertel 4871: 	my $line=&scantron_get_line($scanlines,$scan_data,$i);
1.186     albertel 4872: 	if ($line=~/^[\s\cz]*$/) { next; }
                   4873: 	my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
                   4874: 						 $scan_data);
                   4875: 	my $CODE=$$scan_record{'scantron.CODE'};
                   4876: 	my $error=0;
1.224     albertel 4877: 	if (!&Apache::lonnet::validCODE($CODE)) {
                   4878: 	    &scantron_get_correction($r,$i,$scan_record,
                   4879: 				     \%scantron_config,
                   4880: 				     $line,'incorrectCODE',\%allcodes);
                   4881: 	    return(1,$currentphase);
                   4882: 	}
1.221     albertel 4883: 	if (%allcodes && !exists($allcodes{$CODE}) 
                   4884: 	    && !$$scan_record{'scantron.useCODE'}) {
1.186     albertel 4885: 	    &scantron_get_correction($r,$i,$scan_record,
                   4886: 				     \%scantron_config,
1.194     albertel 4887: 				     $line,'incorrectCODE',\%allcodes);
                   4888: 	    return(1,$currentphase);
1.186     albertel 4889: 	}
1.214     albertel 4890: 	if (exists($usedCODEs{$CODE}) 
1.257     albertel 4891: 	    && $env{'form.scantron_CODEunique'} eq 'yes'
1.192     albertel 4892: 	    && !$$scan_record{'scantron.CODE_ignore_dup'}) {
1.186     albertel 4893: 	    &scantron_get_correction($r,$i,$scan_record,
                   4894: 				     \%scantron_config,
1.194     albertel 4895: 				     $line,'duplicateCODE',$usedCODEs{$CODE});
                   4896: 	    return(1,$currentphase);
1.186     albertel 4897: 	}
1.194     albertel 4898: 	push (@{$usedCODEs{$CODE}},$$scan_record{'scantron.PaperID'});
1.186     albertel 4899:     }
1.157     albertel 4900:     return (0,$currentphase+1);
                   4901: }
                   4902: 
                   4903: sub scantron_validate_doublebubble {
                   4904:     my ($r,$currentphase) = @_;
                   4905:     #get student info
                   4906:     my $classlist=&Apache::loncoursedata::get_classlist();
                   4907:     my %idmap=&username_to_idmap($classlist);
                   4908: 
                   4909:     #get scantron line setup
1.257     albertel 4910:     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
1.157     albertel 4911:     my ($scanlines,$scan_data)=&scantron_getfile();
                   4912:     for (my $i=0;$i<=$scanlines->{'count'};$i++) {
1.200     albertel 4913: 	my $line=&scantron_get_line($scanlines,$scan_data,$i);
1.157     albertel 4914: 	if ($line=~/^[\s\cz]*$/) { next; }
                   4915: 	my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
                   4916: 						 $scan_data);
                   4917: 	if (!defined($$scan_record{'scantron.doubleerror'})) { next; }
                   4918: 	&scantron_get_correction($r,$i,$scan_record,\%scantron_config,$line,
                   4919: 				 'doublebubble',
                   4920: 				 $$scan_record{'scantron.doubleerror'});
                   4921:     	return (1,$currentphase);
                   4922:     }
                   4923:     return (0,$currentphase+1);
                   4924: }
                   4925: 
1.191     albertel 4926: sub scantron_get_maxbubble {
                   4927:     my ($r)=@_;
1.257     albertel 4928:     if (defined($env{'form.scantron_maxbubble'}) &&
                   4929: 	$env{'form.scantron_maxbubble'}) {
                   4930: 	return $env{'form.scantron_maxbubble'};
1.191     albertel 4931:     }
                   4932:     my $navmap=Apache::lonnavmaps::navmap->new();
                   4933:     my (undef,undef,$sequence)=
1.257     albertel 4934: 	&Apache::lonnet::decode_symb($env{'form.selectpage'});
1.191     albertel 4935:     my $map=$navmap->getResourceByUrl($sequence);
                   4936:     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
                   4937:     &Apache::lonnet::delenv('form.counter');
                   4938:     foreach my $resource (@resources) {
1.234     albertel 4939: 	my $result=&Apache::lonnet::ssi($resource->src().'?symb='.&Apache::lonnet::escape($resource->symb()));
1.191     albertel 4940:     }
                   4941:     &Apache::lonnet::delenv('scantron\.');
1.257     albertel 4942:     my $envfile=$env{'user.environment'};
1.191     albertel 4943:     $envfile=~/\/([^\/]+)\.id$/;
                   4944:     $envfile=$1;
                   4945:     &Apache::lonnet::transfer_profile_to_env($r->dir_config('lonIDsDir'),
                   4946: 					     $envfile);
1.257     albertel 4947:     $env{'form.scantron_maxbubble'}=$env{'form.counter'}-1;
                   4948:     return $env{'form.scantron_maxbubble'};
1.191     albertel 4949: }
                   4950: 
1.157     albertel 4951: sub scantron_validate_missingbubbles {
                   4952:     my ($r,$currentphase) = @_;
                   4953:     #get student info
                   4954:     my $classlist=&Apache::loncoursedata::get_classlist();
                   4955:     my %idmap=&username_to_idmap($classlist);
                   4956: 
                   4957:     #get scantron line setup
1.257     albertel 4958:     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
1.157     albertel 4959:     my ($scanlines,$scan_data)=&scantron_getfile();
1.191     albertel 4960:     my $max_bubble=&scantron_get_maxbubble();
1.157     albertel 4961:     if (!$max_bubble) { $max_bubble=2**31; }
                   4962:     for (my $i=0;$i<=$scanlines->{'count'};$i++) {
1.200     albertel 4963: 	my $line=&scantron_get_line($scanlines,$scan_data,$i);
1.157     albertel 4964: 	if ($line=~/^[\s\cz]*$/) { next; }
                   4965: 	my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
                   4966: 						 $scan_data);
                   4967: 	if (!defined($$scan_record{'scantron.missingerror'})) { next; }
                   4968: 	my @to_correct;
                   4969: 	foreach my $missing (@{$$scan_record{'scantron.missingerror'}}) {
                   4970: 	    if ($missing > $max_bubble) { next; }
                   4971: 	    push(@to_correct,$missing);
                   4972: 	}
                   4973: 	if (@to_correct) {
                   4974: 	    &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
                   4975: 				     $line,'missingbubble',\@to_correct);
                   4976: 	    return (1,$currentphase);
                   4977: 	}
                   4978: 
                   4979:     }
                   4980:     return (0,$currentphase+1);
                   4981: }
                   4982: 
1.82      albertel 4983: sub scantron_process_students {
1.75      albertel 4984:     my ($r) = @_;
1.257     albertel 4985:     my (undef,undef,$sequence)=&Apache::lonnet::decode_symb($env{'form.selectpage'});
1.81      albertel 4986:     my ($symb,$url)=&get_symb_and_url($r);
                   4987:     if (!$symb) {return '';}
                   4988:     my $default_form_data=&defaultFormData($symb,$url);
1.82      albertel 4989: 
1.257     albertel 4990:     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
1.157     albertel 4991:     my ($scanlines,$scan_data)=&scantron_getfile();
1.82      albertel 4992:     my $classlist=&Apache::loncoursedata::get_classlist();
                   4993:     my %idmap=&username_to_idmap($classlist);
1.132     bowersj2 4994:     my $navmap=Apache::lonnavmaps::navmap->new();
1.83      albertel 4995:     my $map=$navmap->getResourceByUrl($sequence);
                   4996:     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
1.140     albertel 4997: #    $r->print("geto ".scalar(@resources)."<br />");
1.82      albertel 4998:     my $result= <<SCANTRONFORM;
1.81      albertel 4999: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload">
                   5000:   <input type="hidden" name="command" value="scantron_configphase" />
                   5001:   $default_form_data
                   5002: SCANTRONFORM
1.82      albertel 5003:     $r->print($result);
                   5004: 
                   5005:     my @delayqueue;
1.140     albertel 5006:     my %completedstudents;
                   5007:     
1.200     albertel 5008:     my $count=&get_todo_count($scanlines,$scan_data);
1.157     albertel 5009:     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron Status',
1.200     albertel 5010:  				    'Scantron Progress',$count,
1.195     albertel 5011: 				    'inline',undef,'scantronupload');
1.140     albertel 5012:     &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,
                   5013: 					  'Processing first student');
                   5014:     my $start=&Time::HiRes::time();
1.158     albertel 5015:     my $i=-1;
1.200     albertel 5016:     my ($uname,$udom,$started);
1.157     albertel 5017:     while ($i<$scanlines->{'count'}) {
                   5018:  	($uname,$udom)=('','');
                   5019:  	$i++;
1.200     albertel 5020:  	my $line=&scantron_get_line($scanlines,$scan_data,$i);
1.157     albertel 5021:  	if ($line=~/^[\s\cz]*$/) { next; }
1.200     albertel 5022: 	if ($started) {
                   5023: 	    &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
                   5024: 						     'last student');
                   5025: 	}
                   5026: 	$started=1;
1.157     albertel 5027:  	my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
                   5028:  						 $scan_data);
                   5029:  	unless ($uname=&scantron_find_student($scan_record,$scan_data,
                   5030:  					      \%idmap,$i)) {
                   5031:   	    &scantron_add_delay(\@delayqueue,$line,
                   5032:  				'Unable to find a student that matches',1);
                   5033:  	    next;
                   5034:   	}
                   5035:  	if (exists $completedstudents{$uname}) {
                   5036:  	    &scantron_add_delay(\@delayqueue,$line,
                   5037:  				'Student '.$uname.' has multiple sheets',2);
                   5038:  	    next;
                   5039:  	}
                   5040:   	($uname,$udom)=split(/:/,$uname);
                   5041:   	&Apache::lonnet::delenv('form.counter');
                   5042:   	&Apache::lonnet::appenv(%$scan_record);
1.161     albertel 5043: 	
                   5044: 	my $i=0;
1.83      albertel 5045: 	foreach my $resource (@resources) {
1.85      albertel 5046: 	    $i++;
1.193     albertel 5047: 	    my %form=('submitted'     =>'scantron',
                   5048: 		      'grade_target'  =>'grade',
                   5049: 		      'grade_username'=>$uname,
                   5050: 		      'grade_domain'  =>$udom,
1.257     albertel 5051: 		      'grade_courseid'=>$env{'request.course.id'},
1.193     albertel 5052: 		      'grade_symb'    =>$resource->symb());
                   5053: 	    if (exists($scan_record->{'scantron.CODE'}) &&
                   5054: 		$scan_record->{'scantron.CODE'}) {
                   5055: 		$form{'CODE'}=$scan_record->{'scantron.CODE'};
1.224     albertel 5056: 	    } else {
                   5057: 		$form{'CODE'}='';
1.193     albertel 5058: 	    }
                   5059: 	    my $result=&Apache::lonnet::ssi($resource->src(),%form);
1.227     albertel 5060: 	    if ($result ne '') {
                   5061: 		&Apache::lonnet::logthis("scantron grading error -> $result");
1.257     albertel 5062: 		&Apache::lonnet::logthis("scantron grading error info name $uname domain $udom course $env{'request.course.id'} url ".$resource->src());
1.227     albertel 5063: 	    }
1.213     albertel 5064: 	    if (&Apache::loncommon::connection_aborted($r)) { last; }
1.83      albertel 5065: 	}
1.140     albertel 5066: 	$completedstudents{$uname}={'line'=>$line};
1.213     albertel 5067: 	if (&Apache::loncommon::connection_aborted($r)) { last; }
1.140     albertel 5068:     } continue {
1.85      albertel 5069: 	&Apache::lonnet::delenv('form.counter');
1.83      albertel 5070: 	&Apache::lonnet::delenv('scantron\.');
1.82      albertel 5071:     }
1.140     albertel 5072:     &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
1.172     albertel 5073: #    my $lasttime = &Time::HiRes::time()-$start;
                   5074: #    $r->print("<p>took $lasttime</p>");
1.140     albertel 5075: 
1.200     albertel 5076:     $r->print("</form>");
1.157     albertel 5077:     $r->print(&show_grading_menu_form($symb,$url));
                   5078:     return '';
1.75      albertel 5079: }
1.157     albertel 5080: 
                   5081: sub scantron_upload_scantron_data {
                   5082:     my ($r)=@_;
1.257     albertel 5083:     $r->print(&Apache::loncommon::coursebrowser_javascript($env{'request.role.domain'}));
1.157     albertel 5084:     my $select_link=&Apache::loncommon::selectcourse_link('rules','courseid',
1.181     albertel 5085: 							  'domainid',
                   5086: 							  'coursename');
1.257     albertel 5087:     my $domsel=&Apache::loncommon::select_dom_form($env{'request.role.domain'},
1.157     albertel 5088: 						   'domainid');
1.173     albertel 5089:     my $default_form_data=&defaultFormData(&get_symb_and_url($r,1));
1.157     albertel 5090:     $r->print(<<UPLOAD);
                   5091: <script type="text/javascript" language="javascript">
                   5092:     function checkUpload(formname) {
                   5093: 	if (formname.upfile.value == "") {
                   5094: 	    alert("Please use the browse button to select a file from your local directory.");
                   5095: 	    return false;
                   5096: 	}
                   5097: 	formname.submit();
                   5098:     }
                   5099: </script>
                   5100: 
                   5101: <form enctype='multipart/form-data' action='/adm/grades' name='rules' method='post'>
1.162     albertel 5102: $default_form_data
1.181     albertel 5103: <table>
                   5104: <tr><td>$select_link </td></tr>
                   5105: <tr><td>Course ID:   </td><td><input name='courseid' type='text' />  </td></tr>
                   5106: <tr><td>Course Name: </td><td><input name='coursename' type='text' /></td></tr>
                   5107: <tr><td>Domain:      </td><td>$domsel                                </td></tr>
                   5108: <tr><td>File to upload:</td><td><input type="file" name="upfile" size="50" /></td></tr>
                   5109: </table>
1.157     albertel 5110: <input name='command' value='scantronupload_save' type='hidden' />
                   5111: <input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scantron Data" />
                   5112: </form>
                   5113: UPLOAD
                   5114:     return '';
                   5115: }
                   5116: 
                   5117: sub scantron_upload_scantron_data_save {
                   5118:     my($r)=@_;
1.182     albertel 5119:     my ($symb,$url)=&get_symb_and_url($r,1);
                   5120:     my $doanotherupload=
                   5121: 	'<br /><form action="/adm/grades" method="post">'."\n".
                   5122: 	'<input type="hidden" name="command" value="scantronupload" />'."\n".
                   5123: 	'<input type="submit" name="submit" value="Do Another Upload" />'."\n".
                   5124: 	'</form>'."\n";
1.257     albertel 5125:     if (!&Apache::lonnet::allowed('usc',$env{'form.domainid'}) &&
1.162     albertel 5126: 	!&Apache::lonnet::allowed('usc',
1.257     albertel 5127: 			    $env{'form.domainid'}.'_'.$env{'form.courseid'})) {
1.162     albertel 5128: 	$r->print("You are not allowed to upload Scantron data to the requested course.<br />");
1.182     albertel 5129: 	if ($symb) {
                   5130: 	    $r->print(&show_grading_menu_form($symb,$url));
                   5131: 	} else {
                   5132: 	    $r->print($doanotherupload);
                   5133: 	}
1.162     albertel 5134: 	return '';
                   5135:     }
1.257     albertel 5136:     my %coursedata=&Apache::lonnet::coursedescription($env{'form.domainid'}.'_'.$env{'form.courseid'});
1.211     ng       5137:     $r->print("Doing upload to ".$coursedata{'description'}." <br />");
1.257     albertel 5138:     my $home=&Apache::lonnet::homeserver($env{'form.courseid'},
                   5139: 					 $env{'form.domainid'});
                   5140:     my $fname=$env{'form.upfile.filename'};
1.157     albertel 5141:     #FIXME
                   5142:     #copied from lonnet::userfileupload()
                   5143:     #make that function able to target a specified course
                   5144:     # Replace Windows backslashes by forward slashes
                   5145:     $fname=~s/\\/\//g;
                   5146:     # Get rid of everything but the actual filename
                   5147:     $fname=~s/^.*\/([^\/]+)$/$1/;
                   5148:     # Replace spaces by underscores
                   5149:     $fname=~s/\s+/\_/g;
                   5150:     # Replace all other weird characters by nothing
                   5151:     $fname=~s/[^\w\.\-]//g;
                   5152:     # See if there is anything left
                   5153:     unless ($fname) { return 'error: no uploaded file'; }
1.209     ng       5154:     my $uploadedfile=$fname;
1.157     albertel 5155:     $fname='scantron_orig_'.$fname;
1.257     albertel 5156:     if (length($env{'form.upfile'}) < 2) {
                   5157: 	$r->print("<font color='red'>Error:</font> The file you attempted to upload, <tt>".&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</tt>, contained no information. Please check that you entered the correct filename.");
1.183     albertel 5158:     } else {
1.257     albertel 5159: 	my $result=&Apache::lonnet::finishuserfileupload($env{'form.courseid'},$env{'form.domainid'},$home,'upfile',$fname);
1.210     albertel 5160: 	if ($result =~ m|^/uploaded/|) {
1.257     albertel 5161: 	    $r->print("<font color='green'>Success:</font> Successfully uploaded ".(length($env{'form.upfile'})-1)." bytes of data into location <tt>".$result."</tt>");
1.210     albertel 5162: 	} else {
1.257     albertel 5163: 	    $r->print("<font color='red'>Error:</font> An error (".$result.") occurred when attempting to upload the file, <tt>".&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</tt>");
1.183     albertel 5164: 	}
                   5165:     }
1.174     albertel 5166:     if ($symb) {
1.209     ng       5167: 	$r->print(&scantron_selectphase($r,$uploadedfile));
1.174     albertel 5168:     } else {
1.182     albertel 5169: 	$r->print($doanotherupload);
1.174     albertel 5170:     }
1.157     albertel 5171:     return '';
                   5172: }
                   5173: 
1.202     albertel 5174: sub valid_file {
                   5175:     my ($requested_file)=@_;
                   5176:     foreach my $filename (sort(&scantron_filenames())) {
                   5177: 	&Apache::lonnet::logthis("$requested_file  $filename");
                   5178: 	if ($requested_file eq $filename) { return 1; }
                   5179:     }
                   5180:     return 0;
                   5181: }
                   5182: 
                   5183: sub scantron_download_scantron_data {
                   5184:     my ($r)=@_;
                   5185:     my $default_form_data=&defaultFormData(&get_symb_and_url($r,1));
1.257     albertel 5186:     my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
                   5187:     my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
                   5188:     my $file=$env{'form.scantron_selectfile'};
1.202     albertel 5189:     if (! &valid_file($file)) {
                   5190: 	$r->print(<<ERROR);
                   5191: 	<p>
                   5192: 	    The requested file name was invalid.
                   5193:         </p>
                   5194: ERROR
                   5195: 	$r->print(&show_grading_menu_form(&get_symb_and_url($r,1)));
                   5196: 	return;
                   5197:     }
                   5198:     my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file;
                   5199:     my $corrected='/uploaded/'.$cdom.'/'.$cname.'/scantron_corrected_'.$file;
                   5200:     my $skipped='/uploaded/'.$cdom.'/'.$cname.'/scantron_skipped_'.$file;
                   5201:     &Apache::lonnet::allowuploaded('/adm/grades',$orig);
                   5202:     &Apache::lonnet::allowuploaded('/adm/grades',$corrected);
                   5203:     &Apache::lonnet::allowuploaded('/adm/grades',$skipped);
                   5204:     $r->print(<<DOWNLOAD);
                   5205:     <p>
                   5206: 	<a href="$orig">Original</a> file as uploaded by the scantron office.
                   5207:     </p>
                   5208:     <p>
                   5209: 	<a href="$corrected">Corrections</a>, a file of corrected records that were used in grading.
                   5210:     </p>
                   5211:     <p>
                   5212: 	<a href="$skipped">Skipped</a>, a file of records that were skipped.
                   5213:     </p>
                   5214: DOWNLOAD
                   5215:     $r->print(&show_grading_menu_form(&get_symb_and_url($r,1)));
                   5216:     return '';
                   5217: }
1.157     albertel 5218: 
1.75      albertel 5219: #-------- end of section for handling grading scantron forms -------
                   5220: #
                   5221: #-------------------------------------------------------------------
                   5222: 
1.72      ng       5223: #-------------------------- Menu interface -------------------------
                   5224: #
                   5225: #--- Show a Grading Menu button - Calls the next routine ---
                   5226: sub show_grading_menu_form {
                   5227:     my ($symb,$url)=@_;
1.125     ng       5228:     my $result.='<br /><form action="/adm/grades" method="post">'."\n".
1.72      ng       5229: 	'<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
                   5230: 	'<input type="hidden" name="url" value="'.$url.'" />'."\n".
1.257     albertel 5231: 	'<input type="hidden" name="saveState"  value="'.$env{'form.saveState'}.'" />'."\n".
1.72      ng       5232: 	'<input type="hidden" name="command" value="gradingmenu" />'."\n".
                   5233: 	'<input type="submit" name="submit" value="Grading Menu" />'."\n".
                   5234: 	'</form>'."\n";
                   5235:     return $result;
                   5236: }
                   5237: 
1.77      ng       5238: # -- Retrieve choices for grading form
                   5239: sub savedState {
                   5240:     my %savedState = ();
1.257     albertel 5241:     if ($env{'form.saveState'}) {
                   5242: 	foreach (split(/:/,$env{'form.saveState'})) {
1.77      ng       5243: 	    my ($key,$value) = split(/=/,$_,2);
                   5244: 	    $savedState{$key} = $value;
                   5245: 	}
                   5246:     }
                   5247:     return \%savedState;
                   5248: }
1.76      ng       5249: 
1.72      ng       5250: #--- Displays the main menu page -------
                   5251: sub gradingmenu {
                   5252:     my ($request) = @_;
                   5253:     my ($symb,$url)=&get_symb_and_url($request);
                   5254:     if (!$symb) {return '';}
1.76      ng       5255:     my $probTitle = &Apache::lonnet::gettitle($symb);
1.72      ng       5256: 
                   5257:     $request->print(<<GRADINGMENUJS);
                   5258: <script type="text/javascript" language="javascript">
1.116     ng       5259:     function checkChoice(formname,val,cmdx) {
                   5260: 	if (val <= 2) {
                   5261: 	    var cmd = radioSelection(formname.radioChoice);
1.118     ng       5262: 	    var cmdsave = cmd;
1.116     ng       5263: 	} else {
                   5264: 	    cmd = cmdx;
1.118     ng       5265: 	    cmdsave = 'submission';
1.116     ng       5266: 	}
                   5267: 	formname.command.value = cmd;
1.118     ng       5268: 	formname.saveState.value = "saveCmd="+cmdsave+":saveSec="+pullDownSelection(formname.section)+
1.145     albertel 5269: 	    ":saveSub="+pullDownSelection(formname.submitonly)+":saveStatus="+pullDownSelection(formname.Status);
1.116     ng       5270: 	if (val < 5) formname.submit();
                   5271: 	if (val == 5) {
1.72      ng       5272: 	    if (!checkReceiptNo(formname,'notOK')) { return false;}
                   5273: 	    formname.submit();
                   5274: 	}
1.238     albertel 5275: 	if (val < 7) formname.submit();
1.72      ng       5276:     }
                   5277: 
                   5278:     function checkReceiptNo(formname,nospace) {
                   5279: 	var receiptNo = formname.receipt.value;
                   5280: 	var checkOpt = false;
                   5281: 	if (nospace == "OK" && isNaN(receiptNo)) {checkOpt = true;}
                   5282: 	if (nospace == "notOK" && (isNaN(receiptNo) || receiptNo == "")) {checkOpt = true;}
                   5283: 	if (checkOpt) {
                   5284: 	    alert("Please enter a receipt number given by a student in the receipt box.");
                   5285: 	    formname.receipt.value = "";
                   5286: 	    formname.receipt.focus();
                   5287: 	    return false;
                   5288: 	}
                   5289: 	return true;
                   5290:     }
                   5291: </script>
                   5292: GRADINGMENUJS
1.118     ng       5293:     &commonJSfunctions($request);
                   5294:     my $result='<h3>&nbsp;<font color="#339933">Manual Grading/View Submission</font></h3>';
1.122     ng       5295:     my ($table,undef,$hdgrade) = &showResourceInfo($url,$probTitle);
1.118     ng       5296:     $result.=$table;
1.76      ng       5297:     my (undef,$sections) = &getclasslist('all','0');
1.77      ng       5298:     my $savedState = &savedState();
1.118     ng       5299:     my $saveCmd = ($$savedState{'saveCmd'} eq '' ? 'submission' : $$savedState{'saveCmd'});
1.77      ng       5300:     my $saveSec = ($$savedState{'saveSec'} eq '' ? 'all' : $$savedState{'saveSec'});
1.118     ng       5301:     my $saveSub = ($$savedState{'saveSub'} eq '' ? 'all' : $$savedState{'saveSub'});
1.77      ng       5302:     my $saveStatus = ($$savedState{'saveStatus'} eq '' ? 'Active' : $$savedState{'saveStatus'});
1.72      ng       5303: 
                   5304:     $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
                   5305: 	'<input type="hidden" name="symb"        value="'.$symb.'" />'."\n".
                   5306: 	'<input type="hidden" name="url"         value="'.$url.'" />'."\n".
                   5307: 	'<input type="hidden" name="handgrade"   value="'.$hdgrade.'" />'."\n".
                   5308: 	'<input type="hidden" name="probTitle"   value="'.$probTitle.'" />'."\n".
1.116     ng       5309: 	'<input type="hidden" name="command"     value="" />'."\n".
1.77      ng       5310: 	'<input type="hidden" name="saveState"   value="" />'."\n".
1.124     ng       5311: 	'<input type="hidden" name="gradingMenu" value="1" />'."\n".
1.72      ng       5312: 	'<input type="hidden" name="showgrading" value="yes" />'."\n";
                   5313: 
1.116     ng       5314:     $result.='<table width="100%" border=0><tr><td bgcolor=#777777>'."\n".
                   5315: 	'<table width=100% border=0><tr bgcolor="#e6ffff"><td colspan="2">'."\n".
1.72      ng       5316: 	'&nbsp;<b>Select a Grading/Viewing Option</b></td></tr>'."\n".
1.116     ng       5317: 	'<tr bgcolor="#ffffe6" valign="top"><td>'."\n";
                   5318: 
                   5319:     $result.='<table width="100%" border=0>';
                   5320:     $result.='<tr bgcolor="#ffffe6" valign="top"><td>'."\n".
1.167     sakharuk 5321: 	'&nbsp;'.&mt('Select Section').': <select name="section">'."\n";
1.116     ng       5322:     if (ref($sections)) {
1.155     albertel 5323: 	foreach (sort (@$sections)) {
                   5324: 	    $result.='<option value="'.$_.'" '.
                   5325: 		($saveSec eq $_ ? 'selected="on"':'').'>'.$_.'</option>'."\n";
                   5326: 	}
1.116     ng       5327:     }
1.238     albertel 5328:     $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="on"' : ''). '>all</option></select> &nbsp; ';
1.116     ng       5329: 
1.167     sakharuk 5330:     $result.=&mt('Student Status').':</b>'.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,undef);
1.72      ng       5331: 
1.116     ng       5332:     $result.='</td></tr>';
                   5333: 
1.118     ng       5334:     $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.
                   5335: 	'<input type="radio" name="radioChoice" value="submission" '.
1.167     sakharuk 5336: 	($saveCmd eq 'submission' ? 'checked' : '').'> '.'<b>'.&mt('Current Resource').':</b> '.&mt('For one or more students').
                   5337: 	' <select name="submitonly">'.
1.145     albertel 5338: 	'<option value="yes" '.
                   5339: 	($saveSub eq 'yes' ? 'selected="on"' : '').'>with submissions</option>'.
                   5340: 	'<option value="graded" '.
                   5341: 	($saveSub eq 'graded' ? 'selected="on"' : '').'>with ungraded submissions</option>'.
1.156     albertel 5342: 	'<option value="incorrect" '.
                   5343: 	($saveSub eq 'incorrect' ? 'selected="on"' : '').'>with incorrect submissions</option>'.
1.145     albertel 5344: 	'<option value="all" '.
                   5345: 	($saveSub eq 'all' ? 'selected="on"' : '').'>with any status</option></select></td></tr>'."\n";
1.72      ng       5346: 
1.116     ng       5347:     $result.='<tr bgcolor="#ffffe6"valign="top"><td>'.
                   5348: 	'<input type="radio" name="radioChoice" value="viewgrades" '.
1.76      ng       5349: 	($saveCmd eq 'viewgrades' ? 'checked' : '').'> '.
1.118     ng       5350: 	'<b>Current Resource:</b> For all students in selected section or course</td></tr>'."\n";
1.72      ng       5351: 
1.118     ng       5352:     $result.='<tr bgcolor="#ffffe6" valign="top"><td>'.
                   5353: 	'<input type="radio" name="radioChoice" value="pickStudentPage" '.
                   5354: 	($saveCmd eq 'pickStudentPage' ? 'checked' : '').'> '.
                   5355: 	'The <b>complete</b> set/page/sequence: For one student</td></tr>'."\n";
1.46      ng       5356: 
1.116     ng       5357:     $result.='<tr bgcolor="#ffffe6"><td><br />'.
1.126     ng       5358: 	'<input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="Next->" />'.
1.116     ng       5359: 	'</td></tr></table>'."\n";
                   5360: 
                   5361:     $result.='</td><td valign="top">';
                   5362: 
                   5363:     $result.='<table width="100%" border=0>';
                   5364:     $result.='<tr bgcolor="#ffffe6"><td>'.
1.184     www      5365: 	'<input type="button" onClick="javascript:checkChoice(this.form,\'3\',\'csvform\');" value="'.&mt('Upload').'" />'.
                   5366: 	' '.&mt('scores from file').' </td></tr>'."\n";
1.72      ng       5367: 
1.75      albertel 5368:     $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.
1.116     ng       5369: 	'<input type="button" onClick="javascript:checkChoice(this.form,\'4\',\'scantron_selectphase\');'.
1.184     www      5370: 	'" value="'.&mt('Grade').'" /> scantron forms</td></tr>'."\n";
1.75      albertel 5371: 
1.257     albertel 5372:     if ((&Apache::lonnet::allowed('mgr',$env{'request.course.id'})) && ($symb)) {
1.72      ng       5373: 	$result.='<tr bgcolor="#ffffe6"valign="top"><td>'.
1.184     www      5374: 	    '<input type="button" onClick="javascript:checkChoice(this.form,\'5\',\'verify\');" value="'.&mt('Verify').'" />'.
                   5375: 	    ' '.&mt('receipt').': '.
1.257     albertel 5376: 	    &Apache::lonnet::recprefix($env{'request.course.id'}).
1.72      ng       5377: 	    '-<input type="text" name="receipt" size="4" onChange="javascript:checkReceiptNo(this.form,\'OK\')">'.
                   5378: 	    '</td></tr>'."\n";
                   5379:     } 
1.238     albertel 5380:     $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'.
                   5381: 	'<input type="button" onClick="javascript:this.form.action=\'/adm/helper/resettimes.helper\';this.form.submit();'.
                   5382: 	'" value="'.&mt('Manage').'" /> access times.</td></tr>'."\n";
1.44      ng       5383: 
1.116     ng       5384:     $result.='</form></td></tr></table>'."\n".
1.72      ng       5385: 	'</td></tr></table>'."\n".
                   5386: 	'</td></tr></table>'."\n";
1.44      ng       5387:     return $result;
1.2       albertel 5388: }
                   5389: 
1.1       albertel 5390: sub handler {
1.41      ng       5391:     my $request=$_[0];
1.102     albertel 5392: 
1.103     albertel 5393:     undef(%perm);
1.257     albertel 5394:     if ($env{'browser.mathml'}) {
1.141     www      5395: 	&Apache::loncommon::content_type($request,'text/xml');
1.41      ng       5396:     } else {
1.141     www      5397: 	&Apache::loncommon::content_type($request,'text/html');
1.41      ng       5398:     }
                   5399:     $request->send_http_header;
1.44      ng       5400:     return '' if $request->header_only;
1.41      ng       5401:     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});
1.257     albertel 5402:     my $url=$env{'form.url'};
                   5403:     my $symb=$env{'form.symb'};
1.160     albertel 5404:     my @commands=&Apache::loncommon::get_env_multiple('form.command');
                   5405:     my $command=$commands[0];
                   5406:     if ($#commands > 0) {
                   5407: 	&Apache::lonnet::logthis("grades got multiple commands ".join(':',@commands));
                   5408:     }
1.41      ng       5409:     if (!$url) {
                   5410: 	my ($temp1,$temp2);
1.257     albertel 5411: 	($temp1,$temp2,$env{'form.url'})=&Apache::lonnet::decode_symb($symb);
                   5412: 	$url = $env{'form.url'};
1.41      ng       5413:     }
                   5414:     &send_header($request);
1.157     albertel 5415:     if ($url eq '' && $symb eq '' && $command eq '') {
1.257     albertel 5416: 	if ($env{'user.adv'}) {
                   5417: 	    if (($env{'form.codeone'}) && ($env{'form.codetwo'}) &&
                   5418: 		($env{'form.codethree'})) {
                   5419: 		my $token=$env{'form.codeone'}.'*'.$env{'form.codetwo'}.'*'.
                   5420: 		    $env{'form.codethree'};
1.41      ng       5421: 		my ($tsymb,$tuname,$tudom,$tcrsid)=
                   5422: 		    &Apache::lonnet::checkin($token);
                   5423: 		if ($tsymb) {
1.137     albertel 5424: 		    my ($map,$id,$url)=&Apache::lonnet::decode_symb($tsymb);
1.41      ng       5425: 		    if (&Apache::lonnet::allowed('mgr',$tcrsid)) {
1.99      albertel 5426: 			$request->print(&Apache::lonnet::ssi_body('/res/'.$url,
                   5427: 					  ('grade_username' => $tuname,
                   5428: 					   'grade_domain' => $tudom,
                   5429: 					   'grade_courseid' => $tcrsid,
                   5430: 					   'grade_symb' => $tsymb)));
1.41      ng       5431: 		    } else {
1.45      ng       5432: 			$request->print('<h3>Not authorized: '.$token.'</h3>');
1.99      albertel 5433: 		    }
1.41      ng       5434: 		} else {
1.45      ng       5435: 		    $request->print('<h3>Not a valid DocID: '.$token.'</h3>');
1.41      ng       5436: 		}
1.14      www      5437: 	    } else {
1.41      ng       5438: 		$request->print(&Apache::lonxml::tokeninputfield());
                   5439: 	    }
                   5440: 	}
                   5441:     } else {
1.257     albertel 5442: 	if (!($perm{'vgr'}=&Apache::lonnet::allowed('vgr',$env{'request.course.id'}))) {
                   5443: 	    if ($perm{'vgr'}=&Apache::lonnet::allowed('vgr',$env{'request.course.id'}.'/'.$env{'request.course.sec'})) {
                   5444: 		$perm{'vgr_section'}=$env{'request.course.sec'};
1.102     albertel 5445: 	    } else {
1.103     albertel 5446: 		delete($perm{'vgr'});
1.102     albertel 5447: 	    }
                   5448: 	}
1.257     albertel 5449: 	if (!($perm{'mgr'}=&Apache::lonnet::allowed('mgr',$env{'request.course.id'}))) {
                   5450: 	    if ($perm{'mgr'}=&Apache::lonnet::allowed('mgr',$env{'request.course.id'}.'/'.$env{'request.course.sec'})) {
                   5451: 		$perm{'mgr_section'}=$env{'request.course.sec'};
1.102     albertel 5452: 	    } else {
1.103     albertel 5453: 		delete($perm{'mgr'});
1.102     albertel 5454: 	    }
                   5455: 	}
1.104     albertel 5456: 	if ($command eq 'submission' && $perm{'vgr'}) {
1.257     albertel 5457: 	    ($env{'form.student'} eq '' ? &listStudents($request) : &submission($request,0,0));
1.103     albertel 5458: 	} elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) {
1.68      ng       5459: 	    &pickStudentPage($request);
1.103     albertel 5460: 	} elsif ($command eq 'displayPage' && $perm{'vgr'}) {
1.68      ng       5461: 	    &displayPage($request);
1.104     albertel 5462: 	} elsif ($command eq 'gradeByPage' && $perm{'mgr'}) {
1.71      ng       5463: 	    &updateGradeByPage($request);
1.104     albertel 5464: 	} elsif ($command eq 'processGroup' && $perm{'vgr'}) {
1.41      ng       5465: 	    &processGroup($request);
1.104     albertel 5466: 	} elsif ($command eq 'gradingmenu' && $perm{'vgr'}) {
1.41      ng       5467: 	    $request->print(&gradingmenu($request));
1.104     albertel 5468: 	} elsif ($command eq 'viewgrades' && $perm{'vgr'}) {
1.41      ng       5469: 	    $request->print(&viewgrades($request));
1.104     albertel 5470: 	} elsif ($command eq 'handgrade' && $perm{'mgr'}) {
1.41      ng       5471: 	    $request->print(&processHandGrade($request));
1.106     albertel 5472: 	} elsif ($command eq 'editgrades' && $perm{'mgr'}) {
1.41      ng       5473: 	    $request->print(&editgrades($request));
1.106     albertel 5474: 	} elsif ($command eq 'verify' && $perm{'vgr'}) {
1.41      ng       5475: 	    $request->print(&verifyreceipt($request));
1.106     albertel 5476: 	} elsif ($command eq 'csvform' && $perm{'mgr'}) {
1.72      ng       5477: 	    $request->print(&upcsvScores_form($request));
1.106     albertel 5478: 	} elsif ($command eq 'csvupload' && $perm{'mgr'}) {
1.41      ng       5479: 	    $request->print(&csvupload($request));
1.106     albertel 5480: 	} elsif ($command eq 'csvuploadmap' && $perm{'mgr'} ) {
1.41      ng       5481: 	    $request->print(&csvuploadmap($request));
1.246     albertel 5482: 	} elsif ($command eq 'csvuploadoptions' && $perm{'mgr'}) {
1.257     albertel 5483: 	    if ($env{'form.associate'} ne 'Reverse Association') {
1.246     albertel 5484: 		$request->print(&csvuploadoptions($request));
1.41      ng       5485: 	    } else {
1.257     albertel 5486: 		if ( $env{'form.upfile_associate'} ne 'reverse' ) {
                   5487: 		    $env{'form.upfile_associate'} = 'reverse';
1.41      ng       5488: 		} else {
1.257     albertel 5489: 		    $env{'form.upfile_associate'} = 'forward';
1.41      ng       5490: 		}
                   5491: 		$request->print(&csvuploadmap($request));
                   5492: 	    }
1.246     albertel 5493: 	} elsif ($command eq 'csvuploadassign' && $perm{'mgr'} ) {
                   5494: 	    $request->print(&csvuploadassign($request));
1.106     albertel 5495: 	} elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {
1.75      albertel 5496: 	    $request->print(&scantron_selectphase($request));
1.203     albertel 5497:  	} elsif ($command eq 'scantron_warning' && $perm{'mgr'}) {
                   5498:  	    $request->print(&scantron_do_warning($request));
1.142     albertel 5499: 	} elsif ($command eq 'scantron_validate' && $perm{'mgr'}) {
                   5500: 	    $request->print(&scantron_validate_file($request));
1.106     albertel 5501: 	} elsif ($command eq 'scantron_process' && $perm{'mgr'}) {
1.82      albertel 5502: 	    $request->print(&scantron_process_students($request));
1.157     albertel 5503:  	} elsif ($command eq 'scantronupload' && 
1.257     albertel 5504:  		 (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||
                   5505: 		  &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {
1.162     albertel 5506:  	    $request->print(&scantron_upload_scantron_data($request)); 
1.157     albertel 5507:  	} elsif ($command eq 'scantronupload_save' &&
1.257     albertel 5508:  		 (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||
                   5509: 		  &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {
1.157     albertel 5510:  	    $request->print(&scantron_upload_scantron_data_save($request));
1.202     albertel 5511:  	} elsif ($command eq 'scantron_download' &&
1.257     albertel 5512: 		 &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {
1.162     albertel 5513:  	    $request->print(&scantron_download_scantron_data($request));
1.106     albertel 5514: 	} elsif ($command) {
1.157     albertel 5515: 	    $request->print("Access Denied ($command)");
1.26      albertel 5516: 	}
1.2       albertel 5517:     }
1.41      ng       5518:     &send_footer($request);
1.44      ng       5519:     return '';
                   5520: }
                   5521: 
                   5522: sub send_header {
                   5523:     my ($request)= @_;
                   5524:     $request->print(&Apache::lontexconvert::header());
                   5525: #  $request->print("
                   5526: #<script>
                   5527: #remotewindow=open('','homeworkremote');
                   5528: #remotewindow.close();
                   5529: #</script>"); 
1.47      www      5530:     $request->print(&Apache::loncommon::bodytag('Grading'));
1.157     albertel 5531:     $request->rflush();
1.44      ng       5532: }
                   5533: 
                   5534: sub send_footer {
                   5535:     my ($request)= @_;
1.212     albertel 5536:     $request->print('</body></html>');
1.1       albertel 5537: }
                   5538: 
                   5539: 1;
                   5540: 
1.13      albertel 5541: __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.