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

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