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

1.17      albertel    1: # The LearningOnline Network with CAPA
1.13      albertel    2: # The LON-CAPA Grading handler
1.17      albertel    3: #
1.34    ! ng          4: # $Id: grades.pm,v 1.33 2002/06/27 21:34:18 ng Exp $
1.17      albertel    5: #
                      6: # Copyright Michigan State University Board of Trustees
                      7: #
                      8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      9: #
                     10: # LON-CAPA is free software; you can redistribute it and/or modify
                     11: # it under the terms of the GNU General Public License as published by
                     12: # the Free Software Foundation; either version 2 of the License, or
                     13: # (at your option) any later version.
                     14: #
                     15: # LON-CAPA is distributed in the hope that it will be useful,
                     16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     18: # GNU General Public License for more details.
                     19: #
                     20: # You should have received a copy of the GNU General Public License
                     21: # along with LON-CAPA; if not, write to the Free Software
                     22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     23: #
                     24: # /home/httpd/html/adm/gpl.txt
                     25: #
                     26: # http://www.lon-capa.org/
                     27: #
1.13      albertel   28: # 2/9,2/13 Guy Albertelli
1.8       www        29: # 6/8 Gerd Kortemeyer
1.13      albertel   30: # 7/26 H.K. Ng
1.14      www        31: # 8/20 Gerd Kortemeyer
1.30      ng         32: # Year 2002
                     33: # June 2002 H.K. Ng
                     34: #
1.1       albertel   35: 
                     36: package Apache::grades;
                     37: use strict;
                     38: use Apache::style;
                     39: use Apache::lonxml;
                     40: use Apache::lonnet;
1.3       albertel   41: use Apache::loncommon;
1.1       albertel   42: use Apache::lonhomework;
                     43: use Apache::Constants qw(:common);
                     44: 
1.2       albertel   45: sub moreinfo {
1.13      albertel   46:   my ($request,$reason) = @_;
                     47:   $request->print("Unable to process request: $reason");
                     48:   if ( $Apache::grades::viewgrades eq 'F' ) {
                     49:     $request->print('<form action="/adm/grades" method="post">'."\n");
1.16      albertel   50:     if ($ENV{'form.url'}) {
                     51:       $request->print('<input type="hidden" name="url" value="'.$ENV{'form.url'}.'" />'."\n");
                     52:     }
                     53:     if ($ENV{'form.symb'}) {
                     54:       $request->print('<input type="hidden" name="symb" value="'.$ENV{'form.symb'}.'" />'."\n");
                     55:     }
                     56:     $request->print('<input type="hidden" name="command" value="'.$ENV{'form.command'}.'" />'."\n");
                     57:     $request->print("Student:".'<input type="text" name="student" value="'.$ENV{'form.student'}.'" />'."<br />\n");
                     58:     $request->print("Domain:".'<input type="text" name="domain" value="'.$ENV{'user.domain'}.'" />'."<br />\n");
                     59:     $request->print('<input type="submit" name="submit" value="ReSubmit" />'."<br />\n");
1.13      albertel   60:     $request->print('</form>');
                     61:   }
                     62:   return '';
1.2       albertel   63: }
                     64: 
1.23      www        65: sub verifyreceipt {
                     66:     my $request=shift;
                     67:     my $courseid=$ENV{'request.course.id'};
1.34    ! ng         68: #    my $cdom=$ENV{"course.$courseid.domain"};
        !            69: #    my $cnum=$ENV{"course.$courseid.num"};
1.23      www        70:     my $receipt=unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}).'-'.
                     71:                 $ENV{'form.receipt'};
                     72:     $receipt=~s/[^\-\d]//g;
                     73:     my $symb=$ENV{'form.symb'};
                     74:     unless ($symb) {
                     75: 	$symb=&Apache::lonnet::symbread($ENV{'form.url'});
                     76:     }
                     77:     if ((&Apache::lonnet::allowed('mgr',$courseid)) && ($symb)) {
                     78:         $request->print('<h1>Verifying Submission Receipt '.$receipt.'</h1>');
                     79:         my $matches=0;
1.34    ! ng         80:         my ($classlist) = &getclasslist('all','0');
        !            81:         foreach my $student ( sort(@{ $$classlist{'all'} }) ) {
1.23      www        82:             my ($uname,$udom)=split(/\:/,$student);
                     83:             if ($receipt eq 
                     84:              &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb)) {
                     85:                $request->print('Matching '.$student.'<br>');
                     86:                $matches++;
                     87: 	   }
                     88:         }
1.30      ng         89:         $request->printf('<p>'.$matches." match%s</p>",$matches <= 1 ? '' : 'es');
1.33      ng         90: # needs to print who is matched
1.23      www        91:     }
                     92:     return '';
                     93: }
1.13      albertel   94: 
1.32      ng         95: sub student_gradeStatus {
                     96:   my ($url,$udom,$uname) = @_;
                     97:   my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));
                     98:   my %record= &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
                     99:   foreach my $part (&getpartlist($url)) {
                    100:     my ($temp,$part,$type)=split(/_/,$part);
                    101:     if ($type eq 'solved') {
                    102:       my ($status,$foo)=split(/_/,$record{"resource.$part.$type"},2);
1.34    ! ng        103:       $status = 'partial' if ($foo =~ /^partially/);
1.32      ng        104:       $status = 'nothing' if ($status eq '');
                    105:       return $type,$status;
                    106:     }
                    107:   }
                    108:   return '';
                    109: }
                    110: 
1.34    ! ng        111: sub get_fullname {
        !           112:     my ($sname,$sdom) = @_;
        !           113:     my %name=&Apache::lonnet::get('environment', ['lastname','generation',
        !           114: 						  'firstname','middlename'],
        !           115: 				  $sdom,$sname);
        !           116:     my $fullname;
        !           117:     my ($tmp) = keys(%name);
        !           118:     if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
        !           119: 	$fullname=$name{'lastname'}.$name{'generation'};
        !           120: 	if ($fullname =~ /[^\s]+/) { $fullname.=', '; }
        !           121: 	$fullname.=$name{'firstname'}.' '.$name{'middlename'};
        !           122:     }
        !           123:     return $fullname;
        !           124: }
        !           125: 
1.30      ng        126: sub listStudents {
                    127:   my ($request) = shift;
1.34    ! ng        128:   my $cdom      =$ENV{"course.$ENV{'request.course.id'}.domain"};
        !           129:   my $cnum      =$ENV{"course.$ENV{'request.course.id'}.num"};
        !           130:   my $getsec    =$ENV{'form.section'};
        !           131:   my $submitonly=$ENV{'form.submitonly'};
1.30      ng        132: 
1.23      www       133:   $request->print(<<ENDTABLEST);
1.34    ! ng        134: <h2><font color="#339933">&nbsp;View Submissions for a Student or a Group of Students</font></h2>
        !           135: &nbsp;<font size=+1><b>Resource:</b> $ENV{'form.url'}<br /><br />
        !           136: <form action="/adm/grades" method="post">&nbsp;<b>View Options</b></font><br />
        !           137: &nbsp;<b>View Problem: </b><input type="radio" name="vProb" value="no" checked> no 
        !           138: <input type="radio" name="vProb" value="yes"> yes 
        !           139: &nbsp;&nbsp;&nbsp;<b>Submissions: </b><input type="radio" name="lastSub" value="last" checked> last 
        !           140: <input type="radio" name="lastSub" value="all"> all 
        !           141: <input type="hidden" name="section" value="$getsec">
        !           142: <input type="hidden" name="submitonly" value="$submitonly">
1.32      ng        143: <table border="0"><tr><td bgcolor="#777777">
1.34    ! ng        144: <table border="0"><tr bgcolor="#e6ffff">
        !           145: <td><b>&nbsp;Select&nbsp;</b></td><td><b>&nbsp;Username&nbsp;</b></td>
        !           146: <td><b>&nbsp;Fullname&nbsp;</b></td><td><b>&nbsp;Domain&nbsp;</b></td>
        !           147: <td><b>&nbsp;Grade Status&nbsp;</b></td></tr>
1.23      www       148: ENDTABLEST
1.34    ! ng        149:   if ($ENV{'form.url'}) {
        !           150:       $request->print('<input type="hidden" name="url" value="'.$ENV{'form.url'}.'" />'."\n");
        !           151:   }
        !           152:   if ($ENV{'form.symb'}) {
        !           153:       $request->print('<input type="hidden" name="symb" value="'.$ENV{'form.symb'}.'" />'."\n");
        !           154:   }
        !           155:   $request->print('<input type="hidden" name="command" value="processGroup" />'."\n");
        !           156: 
        !           157:   my ($classlist) = &getclasslist($getsec,'0');
        !           158:   foreach my $student ( sort(@{ $$classlist{$getsec} }) ) {
1.13      albertel  159:       my ($sname,$sdom) = split(/:/,$student);
1.34    ! ng        160:       my ($type,$status) = &student_gradeStatus($ENV{'form.url'},$cdom,$sname);
        !           161:       next if ($status eq 'nothing' && $submitonly eq 'yes');
1.13      albertel  162: 
1.34    ! ng        163:       my $fullname = &get_fullname($sname,$sdom);
1.13      albertel  164:       if ( $Apache::grades::viewgrades eq 'F' ) {
1.34    ! ng        165: 	  $request->print("\n".'<tr bgcolor="#ffffe6">'.
        !           166: 			  '<td align="center"><input type=checkbox name="stuinfo" value="'.
        !           167: 			  $student.':'.$fullname.'"></td>'."\n".
        !           168: 			  '<td>&nbsp;'.$sname.'&nbsp;</td>'."\n".
        !           169: 			  '<td>&nbsp;'.$fullname.'&nbsp;</td>'."\n".
        !           170: 			  '<td align="middle">&nbsp;'.$sdom.'&nbsp;</td>'."\n");
        !           171: 	  $request->print('<td align="middle">&nbsp;'.$status.'&nbsp;</td>'."\n");
        !           172: 
        !           173: 	  $request->print('</tr>');
1.13      albertel  174:       }
                    175:   }
1.28      ng        176:   $request->print('</table></td></tr></table>');
1.34    ! ng        177:   $request->print('<input type="submit" name="submit" value="View/Grade" /><form />');
1.10      ng        178: }
                    179: 
1.34    ! ng        180: sub processGroup {
        !           181:   my ($request)  = shift;
        !           182:   my $ctr        = 0;
        !           183:   my @stuchecked = (ref($ENV{'form.stuinfo'}) ? @{$ENV{'form.stuinfo'}}
        !           184:                            :  ($ENV{'form.stuinfo'}) );
        !           185:   my $total      = scalar(@stuchecked)-1;
        !           186:   foreach my $student (@stuchecked) {
        !           187:       my ($sname,$sdom,$fullname) = split(/:/,$student);
        !           188:       $ENV{'form.student'} = $sname;
        !           189:       $ENV{'form.fullname'} = $fullname;
        !           190:       &submission($request,$ctr,$total);
        !           191:       $ctr++;
        !           192:   }
        !           193: 
        !           194:   return 'The End';
        !           195: }
1.13      albertel  196: 
1.7       albertel  197: #FIXME - needs to handle multiple matches
1.2       albertel  198: sub finduser {
1.13      albertel  199:   my ($name) = @_;
                    200:   my $domain = '';
                    201: 
                    202:   if ( $Apache::grades::viewgrades eq 'F' ) {
                    203:     #get classlist
1.34    ! ng        204: #    my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});
1.24      albertel  205:     #print "Found $cdom:$cnum<br />";
1.34    ! ng        206:     my ($classlist) = &getclasslist('all','0');
        !           207:     foreach my $student ( sort(@{ $$classlist{'all'} }) ) {
1.13      albertel  208:       my ($posname,$posdomain) = split(/:/,$student);
                    209:       if ($posname =~ $name) { $name=$posname; $domain=$posdomain; last; }
1.7       albertel  210:     }
1.13      albertel  211:     return ($name,$domain);
                    212:   } else {
                    213:     return ($ENV{'user.name'},$ENV{'user.domain'});
                    214:   }
1.5       albertel  215: }
                    216: 
                    217: sub getclasslist {
1.34    ! ng        218:   my ($getsec,$hideexpired) = @_;
        !           219:   my ($coursedomain,$coursenum) = split(/_/,$ENV{'request.course.id'});
1.24      albertel  220:   my %classlist=&Apache::lonnet::dump('classlist',$coursedomain,$coursenum);
1.13      albertel  221:   my $now = time;
1.34    ! ng        222:   my (@holdsec,@sections);
1.24      albertel  223:   foreach my $student (keys(%classlist)) {
                    224:     my ($end,$start)=split(/:/,$classlist{$student});
1.13      albertel  225:     # still a student?
                    226:     if (($hideexpired) && ($end) && ($end < $now)) {
                    227:       next;
                    228:     }
1.34    ! ng        229:     my ($unam,$udom) = split(/:/,$student,2);
        !           230:     my $section = &Apache::lonnet::usection($udom,$unam,$ENV{'request.course.id'});
        !           231:     push @holdsec,$section;
        !           232:     push (@{ $classlist{$getsec} }, $student) if ($getsec eq 'all' or $getsec == $section);
        !           233:   }
        !           234:   my %seen = ();
        !           235:   foreach my $item (@holdsec) {
        !           236:       push (@sections, $item) unless $seen{$item}++;
1.13      albertel  237:   }
1.34    ! ng        238:   return (\%classlist,\@sections);
1.5       albertel  239: }
                    240: 
                    241: sub getpartlist {
1.13      albertel  242:   my ($url) = @_;
                    243:   my @parts =();
                    244:   my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));
                    245:   foreach my $key (@metakeys) {
1.30      ng        246:     if ( $key =~ m/stores_([0-9]+)_.*/) {
1.13      albertel  247:       push(@parts,$key);
1.6       albertel  248:     }
1.13      albertel  249:   }
                    250:   return @parts;
1.5       albertel  251: }
                    252: 
                    253: sub viewstudentgrade {
1.13      albertel  254:   my ($url,$symb,$courseid,$student,@parts) = @_;
                    255:   my $result ='';
                    256:   my $cellclr = '"#ffffdd"';
1.28      ng        257:   my ($username,$domain) = split(/:/,$student);
1.13      albertel  258: 
1.34    ! ng        259:   my $fullname = &get_fullname($username,$domain);
1.28      ng        260:   my %record=&Apache::lonnet::restore($symb,$courseid,$domain,$username);
1.32      ng        261: 
1.28      ng        262:   $result.="<tr bgcolor=$cellclr><td>$username</td><td>$fullname</td><td align=\"middle\">$domain</td>\n";
1.13      albertel  263:   foreach my $part (@parts) {
                    264:     my ($temp,$part,$type)=split(/_/,$part);
1.31      ng        265:     my $score=$record{"resource.$part.$type"};
                    266:     if ($type eq 'awarded' || $type eq 'tries') {
                    267:       $result.='<td align="middle"><input type="text" name="GRADE.'.$student.'.'.$part.'.'.$type.
                    268: 	  '" value="'.$score.'" size="4" /></td>'."\n";
1.13      albertel  269:     } elsif ($type eq 'solved') {
1.31      ng        270:       my ($status,$foo)=split(/_/,$score,2);
1.28      ng        271:       $result.="<td align=\"middle\"><select name=\"GRADE.$student.$part.$type\">\n";
1.31      ng        272:       my $optsel = '<option>correct</option><option>incorrect</option><option>excused</option>'.
1.33      ng        273: 	  '<option>ungraded</option><option>partial</option><option>nothing</option>'."\n";
1.31      ng        274:       $status = 'nothing' if ($status eq '');
                    275:       $optsel =~ s/<option>$status/<option selected="on">$status/;
                    276:       $result.=$optsel;
1.13      albertel  277:       $result.="</select></td>\n";
                    278:     }
                    279:   }
1.29      albertel  280:   $result.='<td></td></tr>';
1.13      albertel  281:   return $result;
1.5       albertel  282: }
1.31      ng        283: 
                    284: #FIXME need to look at the metadata <stores> spec on what type of data to accept and provide an
1.6       albertel  285: #interface based on that, also do that to above function.
1.5       albertel  286: sub setstudentgrade {
1.13      albertel  287:   my ($url,$symb,$courseid,$student,@parts) = @_;
1.34    ! ng        288:   print "set student grade parts=@parts<br>";
1.13      albertel  289:   my $result ='';
                    290:   my ($stuname,$domain) = split(/:/,$student);
                    291:   my %record=&Apache::lonnet::restore($symb,$courseid,$domain,$stuname);
                    292:   my %newrecord;
                    293: 
                    294:   foreach my $part (@parts) {
                    295:     my ($temp,$part,$type)=split(/_/,$part);
                    296:     my $oldscore=$record{"resource.$part.$type"};
                    297:     my $newscore=$ENV{"form.GRADE.$student.$part.$type"};
1.32      ng        298:     print "old=$oldscore:new=$newscore:<br>";
1.13      albertel  299:     if ($type eq 'solved') {
                    300:       my $update=0;
                    301:       if ($newscore eq 'nothing' ) {
                    302: 	if ($oldscore ne '') {
                    303: 	  $update=1;
                    304: 	  $newscore = '';
1.6       albertel  305: 	}
1.13      albertel  306:       } elsif ($oldscore !~ m/^$newscore/) {
                    307: 	$update=1;
                    308: 	$result.="Updating $stuname to $newscore<br />\n";
1.34    ! ng        309: 	if ($newscore eq 'correct')   { $newscore = 'correct_by_override'; }
1.13      albertel  310: 	if ($newscore eq 'incorrect') { $newscore = 'incorrect_by_override'; }
1.34    ! ng        311: 	if ($newscore eq 'excused')   { $newscore = 'excused'; }
        !           312: 	if ($newscore eq 'ungraded')  { $newscore = 'ungraded_attempted'; }
        !           313: 	if ($newscore eq 'partial')   { $newscore = 'correct_partially_by_override'; }
1.13      albertel  314:       } else {
                    315: 	#$result.="$stuname:$part:$type:unchanged  $oldscore to $newscore:<br />\n";
                    316:       }
                    317:       if ($update) { $newrecord{"resource.$part.$type"}=$newscore; }
                    318:     } else {
                    319:       if ($oldscore ne $newscore) {
                    320: 	$newrecord{"resource.$part.$type"}=$newscore;
                    321: 	$result.="Updating $student"."'s status for $part.$type to $newscore<br />\n";
                    322:       } else {
                    323: 	#$result.="$stuname:$part:$type:unchanged  $oldscore to $newscore:<br />\n";
                    324:       }
                    325:     }
                    326:   }
                    327:   if ( scalar(keys(%newrecord)) > 0 ) {
1.32      ng        328:     $newrecord{'resource.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
                    329:     print "grader=$newrecord{'resource.regrader'}:<br>records<br>";
                    330:     while (my ($k,$v) = each %newrecord) {
                    331: 	print "k=$k:v=$v:<br>\n";
                    332:     }
1.34    ! ng        333: #    &Apache::lonnet::cstore(\%newrecord,$symb,$courseid,$domain,$stuname);
1.13      albertel  334: 
                    335:     $result.="Stored away ".scalar(keys(%newrecord))." elements.<br />\n";
                    336:   }
                    337:   return $result;
1.2       albertel  338: }
                    339: 
1.33      ng        340: #
1.32      ng        341: # --------------------------- show submissions of a student, option to grade --------
1.2       albertel  342: sub submission {
1.34    ! ng        343:   my ($request,$counter,$total) = @_;
1.33      ng        344: 
1.34    ! ng        345:   if ($counter == 0) {
        !           346:       $request->print(<<JAVASCRIPT);
1.32      ng        347: <script type="text/javascript" language="javascript">
1.33      ng        348:   function updateRadio(radioButton,formtextbox,formsel,wgt) {
                    349:      var pts = formtextbox.value;
                    350:      var resetbox =false;
                    351:      if (isNaN(pts) || pts < 0) {
                    352: 	alert("A number equal or greater than 0 is expected. Entered value = "+pts);
                    353: 	for (var i=0; i<radioButton.length; i++) {
                    354: 	   if (radioButton[i].checked) {
                    355: 	      formtextbox.value = i;
                    356: 	      resetbox = true;
                    357: 	   }
1.32      ng        358: 	}
1.33      ng        359: 	if (!resetbox) {
                    360: 	   formtextbox.value = "";
                    361: 	}
                    362: 	return;
                    363:     }
1.32      ng        364: 
1.33      ng        365:     for (var i=0; i<radioButton.length; i++) {
                    366: 	radioButton[i].checked=false;
                    367: 	if (pts == i) {
                    368: 	   radioButton[i].checked=true;
1.32      ng        369: 	}
1.31      ng        370:     }
1.33      ng        371:     updateSelect(formsel,pts,wgt);
                    372:   }
                    373: 
                    374:   function writeBox(formrad,formsel,pts,wgt) {
                    375:     formrad.value = pts;
                    376:     updateSelect(formsel,pts,wgt);
                    377:     return;
                    378:   }
                    379: 
                    380:   function updateSelect(formsel,pts,wgt) {
                    381:     if (pts == 0) {
                    382:       formsel[1].selected = true;
                    383:     }
                    384:     if (pts > 0 && pts < wgt) {
                    385:       formsel[4].selected = true;
                    386:     }
                    387:     if (pts == wgt) {
                    388:       formsel[0].selected = true;
                    389:     }
                    390:     return;
                    391:   }
1.32      ng        392: 
1.31      ng        393: </script>
                    394: JAVASCRIPT
1.34    ! ng        395: }
1.33      ng        396:   (my $url=$ENV{'form.url'})=~s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
1.13      albertel  397:   if ($ENV{'form.student'} eq '') { &moreinfo($request,"Need student login id"); return ''; }
                    398:   my ($uname,$udom) = &finduser($ENV{'form.student'});
                    399:   if ($uname eq '') { &moreinfo($request,"Unable to find student"); return ''; }
1.32      ng        400: 
                    401:   my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));
1.13      albertel  402:   if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
1.34    ! ng        403:   my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : '');
1.31      ng        404: #
                    405: # header info
1.34    ! ng        406:   if ($counter == 0) {
        !           407:       $request->print('<h2><font color="#339933">Submission Record</font></h2>');
        !           408:   }
        !           409: 
1.31      ng        410: #
1.34    ! ng        411: # option to display problem, only once else it cause problems with the form later since the problem has a form.
        !           412:   if ($ENV{'form.vProb'} eq 'yes' && $counter == 0) {
1.30      ng        413:       my $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom,
1.17      albertel  414: 						   $ENV{'request.course.id'});
1.30      ng        415:       my $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom,
                    416: 							 $ENV{'request.course.id'});
1.34    ! ng        417:       my $result.='<table border="0" width="100%"><tr><td bgcolor="#777777">';
1.33      ng        418:       $result.='<table border="0" width="100%"><tr><td bgcolor="#e6ffff">';
1.31      ng        419:       $result.='<b>Student\'s view of the problem</b></td></tr><tr><td bgcolor="#ffffff">'.$rendered.'<br />';
1.30      ng        420:       $result.='<b>Correct answer:</b><br />'.$companswer;
                    421:       $result.='</td></tr></table>';
                    422:       $result.='</td></tr></table><br />';
1.34    ! ng        423:       $request->print($result);
        !           424:   }
        !           425: #
        !           426: # beginning of form
        !           427:   if ($counter == 0) {
        !           428:       $request->print('<form action="/adm/grades" method="post" name="SCORE">'."\n".
        !           429: 		      '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
        !           430: 		      '<input type="hidden" name="url" value="'.$url.'" />'."\n".
        !           431: 		      '<input type="hidden" name="vProb" value="'.$ENV{'form.vProb'}.'" />'."\n".
        !           432: 		      '<input type="hidden" name="lastSub" value="'.$last.'" />'."\n".
        !           433: 		      '<input type="hidden" name="section" value="'.$ENV{'form.section'}.'">'."\n".
        !           434: 		      '<input type="hidden" name="submitonly" value="'.$ENV{'form.submitonly'}.'">'."\n".
        !           435: 		      '<input type="hidden" name="command" value="handgrade" />'."\n".
        !           436: 		      '<input type="hidden" name="NCT"'.
        !           437: 		      ' value="'.($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : '1').'" />'."\n");
1.30      ng        438:   }
1.34    ! ng        439: #
        !           440: # Student info
        !           441:   $request->print(($counter == 0 ? '' : '<br /><hr><br />'));
        !           442:   my $result.='<table border="0"><tr><td><b>Username: </b>'.$uname.
        !           443:       '</td><td><b>Fullname: </b>'.
        !           444: 	  ($ENV{'form.fullname'} ne '' ? $ENV{'form.fullname'} : &get_fullname($uname,$udom)).
        !           445: 	      '</td><td><b>Domain: </b>'.$udom.'</td></tr>';
        !           446:   $result.='<tr><td colspan=3><b>Resource: </b>'.$url.'</td></tr></table>';
        !           447:   $request->print($result);
        !           448: #
        !           449: # print student answer
1.30      ng        450:   my $answer=&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,
1.34    ! ng        451: 							   $ENV{'request.course.id'},$last);
        !           452:   $request->print($answer);
        !           453:   $answer =~ m/.*<\/tr><tr.*?<td>.*?<td>(.*?)<td>(.*?)<\/td>/;
        !           454:   print "Submitted=$1:<br>$2;";
        !           455: #
        !           456:   my $wgt    = &Apache::lonnet::EXT('resource.partid.weight',$symb,$udom,$uname);
        !           457:   my $wgtmsg = ($wgt > 0 ? '(problem weight)' : '<font color="red">problem weight assigned by computer</font>');
        !           458:   $wgt       = ($wgt > 0 ? $wgt : '1');
        !           459:   my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
        !           460:   my $score  = ($record{'resource.0.awarded'} eq '' ? '' : $record{'resource.0.awarded'}*$wgt);
1.18      albertel  461: 
1.34    ! ng        462: #
        !           463: # display grading options
        !           464:   $result='<input type="hidden" name="WGT'.$counter.'" value="'.$wgt.'" />'.
1.33      ng        465:       '<input type="hidden" name="unamedom'.$counter.'" value="'.$uname.':'.$udom.'" />'."\n";
1.32      ng        466:   $result.='<table border="0"><tr><td><b>Points</b></td><td>';
1.31      ng        467:   my $ctr = 0;
1.34    ! ng        468: 
        !           469:   $result.='<table border="0"><tr>';  # display radio buttons in a nice table with 10 across
1.32      ng        470:   while ($ctr<=$wgt) {
1.34    ! ng        471:       $result.= '<td><input type="radio" name="RADVAL'.$counter.'" '.
1.33      ng        472: 	  'onclick="javascript:writeBox(this.form.GRADE_BOX'.$counter.
1.34    ! ng        473: 	    ',this.form.GRADE_SEL'.$counter.','.$ctr.','.$wgt.')" '.
        !           474: 		($score eq $ctr ? 'checked':'').' /> '.$ctr."</td>\n";
        !           475:       $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
1.31      ng        476:       $ctr++;
                    477:   }
1.34    ! ng        478:   $result.='</tr></table>';
        !           479: 
1.31      ng        480:   $result.='</td><td>&nbsp;<b>or</b>&nbsp;</td>';
1.33      ng        481:   $result.='<td><input type="text" name="GRADE_BOX'.$counter.'"'.
1.32      ng        482:       ($score ne ''? ' value = "'.$score.'"':'').' size="4" '.
1.33      ng        483:        'onChange="javascript:updateRadio(this.form.RADVAL'.$counter.
                    484:         ',this.form.GRADE_BOX'.$counter.
                    485: 	   ',this.form.GRADE_SEL'.$counter.',\''.$wgt.'\')" /></td>'."\n";
1.34    ! ng        486:   $result.='<td>/'.$wgt.' '.$wgtmsg.' </td><td>';
1.33      ng        487: 
                    488:   foreach my $part (&getpartlist($url)) {
                    489:     my ($temp,$part,$type)=split(/_/,$part);
                    490:     if ($type eq 'solved') {
                    491:       my ($status,$foo)=split(/_/,$record{"resource.$part.$type"},2);
1.34    ! ng        492:       $status = 'partial' if ($foo =~ /partially/);
        !           493:       $status = 'nothing' if ($status eq '');
1.33      ng        494:       $result.='<select name="GRADE_SEL'.$counter.'">'."\n";
                    495:       my $optsel = '<option>correct</option><option>incorrect</option>'.
                    496: 	  '<option>excused</option><option>ungraded</option>'.
                    497: 	      '<option>partial</option><option>nothing</option>'."\n";
                    498:       $optsel =~ s/<option>$status/<option selected="on">$status/;
                    499:       $result.=$optsel;
                    500:       $result.="</select></td></tr>\n";
                    501:     }
                    502:   }
1.34    ! ng        503:   $result.='</table>';
        !           504:   $request->print($result);
        !           505: #
        !           506: # print end of form
        !           507:   if ($counter == $total) {
        !           508:       my $endform.='<table border="0"><tr><td><input type="submit" name="gradeOpt" value="Save & Next" />';
        !           509:       my $ntstu ='<select name="NTSTU">'.
        !           510: 	  '<option>1</option><option>2</option>'.
        !           511: 	      '<option>3</option><option>5</option>'.
        !           512: 		  '<option>7</option><option>10</option></select>'."\n";
        !           513:       my $nsel = ($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : '1');
        !           514:       $ntstu =~ s/<option>$nsel/<option selected="on">$nsel/;
        !           515:       $endform.=$ntstu.'student(s) &nbsp;&nbsp;';
        !           516:       $endform.='<input type="submit" name="gradeOpt" value="Next" />&nbsp';
        !           517:       $endform.='<input type="submit" name="gradeOpt" value="Previous" />&nbsp';
        !           518:       $endform.='(Next and Previous do not save the scores.)';
        !           519:       $endform.='</td><tr></table></form>';
        !           520:       $request->print($endform);
        !           521:   }
        !           522:   return '';
1.32      ng        523: }
1.30      ng        524: 
1.32      ng        525: sub processHandGrade {
1.34    ! ng        526:   my ($request) = shift;
1.33      ng        527:   my $url    = $ENV{'form.url'};
                    528:   my $symb   = $ENV{'form.symb'};
                    529:   my $button = $ENV{'form.gradeOpt'};
                    530:   my $ngrade = $ENV{'form.NCT'};
                    531:   my $ntstu  = $ENV{'form.NTSTU'};
1.34    ! ng        532: #  my $vProb  = $ENV{'form.vProb'};
        !           533: #  my $lastSub= $ENV{'form.lastSub'};
1.33      ng        534: 
                    535:   my (@parts) = sort(&getpartlist($url));
1.34    ! ng        536: 
1.33      ng        537:   if ($button eq 'Save & Next') {
                    538:       my $ctr = 0;
                    539:       while ($ctr < $ENV{'form.NCT'}) {
1.34    ! ng        540: #	  my $pts    = ($ENV{'form.GRADE_BOX'.$ctr} ne '' ? $ENV{'form.GRADE_BOX'.$ctr} : $ENV{'form.RADVAL'.$ctr});
        !           541: #	  my $wgt    = $ENV{'form.WGT'.$ctr};
        !           542: #	  my $sel    = $ENV{'form.GRADE_SEL'.$ctr};
        !           543: #	  my $score  = $pts/$wgt if ($wgt != 0);
1.33      ng        544: 	  my ($uname,$udom) = split(/:/,$ENV{'form.unamedom'.$ctr});
1.34    ! ng        545: 	  &saveHandGrade($url,$symb,$uname,$udom,$ctr,@parts);
        !           546: #	  &saveHandGrade($url,$symb,$uname,$udom,$score,@parts);
1.33      ng        547: 	  $ctr++;
                    548:       }
                    549:   }
                    550:   my $firststu = $ENV{'form.unamedom0'};
                    551:   my $laststu  = $ENV{'form.unamedom'.($ngrade-1)};
                    552: 
                    553:   #get classlist
1.34    ! ng        554: #  my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});
        !           555:   my ($classlist) = &getclasslist('all','0');
1.33      ng        556: 
                    557:   my (@nextlist,@prevlist);
1.34    ! ng        558:   my ($nextflg,$prevflg,$ctr,$ctprev) = (0,0,0,0);
        !           559:   foreach my $student ( sort(@{ $$classlist{'all'} }) ) {
1.33      ng        560:       my ($uname,$udom) = split(/:/,$student);
                    561:       if ($nextflg == 1 && $button =~ /Next$/) {
1.34    ! ng        562: 	  push @nextlist,$uname if ($ctr < $ntstu);
1.33      ng        563: 	  $ctr++;
                    564:       }
                    565:       $nextflg = 1 if ($student eq $laststu);
                    566:       $prevflg = 1 if ($student eq $firststu);
1.34    ! ng        567:       if ($prevflg == 0 && $button eq 'Previous') {
        !           568: 	  push @prevlist,$uname;
        !           569: 	  $ctprev++;
        !           570:       }
1.33      ng        571:   }
1.34    ! ng        572:   if ($button eq 'Previous') {
        !           573:       if ($ctprev <= $ntstu) {
        !           574: 	  @nextlist = @prevlist;
        !           575:       } else {
        !           576: 	  my $idx = 0;
        !           577: 	  my $start = $ctprev - $ntstu;
        !           578: 	  while ($idx < $ntstu) {
        !           579: 	      $nextlist[$idx] = $prevlist[$start+$idx];
        !           580: 	      $idx++;
        !           581: 	  }
        !           582:       }
        !           583:   }
        !           584:   $ctr = 0;
        !           585:   my $total = scalar(@nextlist)-1;
1.33      ng        586:   foreach my $student (@nextlist) {
                    587:       $ENV{'form.student'} = $student;
1.34    ! ng        588:       &submission($request,$ctr,$total);
        !           589:       $ctr++;
1.33      ng        590:   }
                    591: 
                    592:   return 'The End';
                    593: }
                    594: 
                    595: sub saveHandGrade {
1.34    ! ng        596:   my ($url,$symb,$stuname,$domain,$newflg,@parts) = @_;
1.33      ng        597: #  my ($stuname,$domain) = split(/:/,$student);
                    598:   my %record=&Apache::lonnet::restore($symb,$ENV{'request.course.id'},$domain,$stuname);
1.32      ng        599:   my %newrecord;
                    600: 
                    601:   foreach my $part (@parts) {
1.31      ng        602:     my ($temp,$part,$type)=split(/_/,$part);
1.32      ng        603:     my $oldscore=$record{"resource.$part.$type"};
1.34    ! ng        604:     my $newscore;
        !           605:     if ($type eq 'awarded' && $newflg >= 0) {
        !           606: 	my $pts    = ($ENV{'form.GRADE_BOX'.$newflg} ne '' ? $ENV{'form.GRADE_BOX'.$newflg} : $ENV{'form.RADVAL'.$newflg});
        !           607: 	my $wgt    = $ENV{'form.WGT'.$newflg};
        !           608: #	my $sel    = $ENV{'form.GRADE_SEL'.$newflg};
        !           609: 	$newscore  = $pts/$wgt if ($wgt != 0);
        !           610:     }
1.31      ng        611:     if ($type eq 'solved') {
1.34    ! ng        612:       $newscore = $ENV{'form.GRADE_SEL'.$newflg} if ($newflg >= 0);
1.32      ng        613:       my $update=0;
                    614:       if ($newscore eq 'nothing' ) {
                    615: 	if ($oldscore ne '') {
                    616: 	  $update=1;
                    617: 	  $newscore = '';
                    618: 	}
                    619:       } elsif ($oldscore !~ m/^$newscore/) {
                    620: 	$update=1;
                    621: 	if ($newscore eq 'correct')   { $newscore = 'correct_by_override'; }
                    622: 	if ($newscore eq 'incorrect') { $newscore = 'incorrect_by_override'; }
                    623: 	if ($newscore eq 'excused')   { $newscore = 'excused'; }
                    624: 	if ($newscore eq 'ungraded')  { $newscore = 'ungraded_attempted'; }
1.34    ! ng        625: 	if ($newscore eq 'partial')   { $newscore = 'correct_partially_by_override'; }
1.32      ng        626:       }
                    627:       if ($update) { $newrecord{"resource.$part.$type"}=$newscore; }
                    628:     } else {
                    629:       if ($oldscore ne $newscore) {
                    630: 	$newrecord{"resource.$part.$type"}=$newscore;
1.33      ng        631:       }
1.32      ng        632:     }
1.34    ! ng        633:   }
        !           634:   if ( scalar(keys(%newrecord)) > 0 ) {
1.33      ng        635:       $newrecord{'resource.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
1.34    ! ng        636:       while (my ($k,$v) = each %newrecord) {
        !           637: 	 print "k=$k:v=$v:<br>\n";
        !           638:       }
        !           639:       print "symb=$symb,courseid=$ENV{'request.course.id'},dom=$domain,name=$stuname<br>";
        !           640: #     &Apache::lonnet::cstore(\%newrecord,$symb,$ENV{'request.course.id'},$domain,$stuname);
1.31      ng        641:   }
1.34    ! ng        642:   return '';
1.2       albertel  643: }
                    644: 
1.26      albertel  645: sub get_symb_and_url {
1.34    ! ng        646:   my ($request) = @_;
        !           647:   (my $url=$ENV{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
1.32      ng        648:   my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));
1.13      albertel  649:   if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
1.34    ! ng        650:   return ($symb,$url);
1.29      albertel  651: }
                    652: 
                    653: sub show_grading_menu_form {
                    654:   my ($symb,$url)=@_;
                    655:   my $result.='<form action="/adm/grades" method="post">'."\n".
                    656:     '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
                    657:       '<input type="hidden" name="url" value="'.$url.'" />'."\n".
                    658: 	'<input type="hidden" name="command" value="gradingmenu" />'."\n".
                    659: 	  '<input type="submit" name="submit" value="Grading Menu" />'."\n".
                    660: 	    '</form>'."\n";
                    661:   return $result;
                    662: }
                    663: 
1.26      albertel  664: sub gradingmenu {
                    665:   my ($request) = @_;
                    666:   my ($symb,$url)=&get_symb_and_url($request);
                    667:   if (!$symb) {return '';}
1.28      ng        668: 
1.34    ! ng        669:   my $result='<h2>&nbsp;<font color="#339933">Select a Grading Method</font></h2>';
        !           670:   $result.='&nbsp;<font size=+1><b>Resource: </b>'.$url.'</font><br /><br />';
        !           671: 
        !           672:   $result.=&view_edit_entire_class_form($symb,$url).'<br />';
        !           673:   $result.=&upcsvScores_form($symb,$url).'<br />';
        !           674:   $result.=&viewGradeaStu_form($symb,$url).'<br />';
        !           675:   $result.=&verifyReceipt_form($symb,$url);
        !           676:   return $result;
        !           677: }
        !           678: 
        !           679: sub view_edit_entire_class_form {
        !           680:   my ($symb,$url)=@_;
        !           681:   my $result.='<table width=100% border=0><tr><td bgcolor=#777777>'."\n";
        !           682:   $result.='<table width=100% border=0><tr bgcolor="#e6ffff"><td>'."\n";
        !           683:   $result.='&nbsp;<b>View/Grade Entire Class</b></td></tr>'."\n";
1.28      ng        684:   $result.='<tr bgcolor=#ffffe6><td>'."\n";
1.26      albertel  685:   $result.='<form action="/adm/grades" method="post">'."\n".
1.34    ! ng        686:     '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
1.26      albertel  687:       '<input type="hidden" name="url" value="'.$url.'" />'."\n".
1.34    ! ng        688: 	  '<input type="hidden" name="command" value="viewgrades" />'."\n";
        !           689:   $result.='&nbsp;<b>Display students who has: </b>'.
        !           690:       '<input type="radio" name="submitonly" value="yes" checked> submitted'.
        !           691: 	  '<input type="radio" name="submitonly" value="all"> everybody <br /><br />';
        !           692:   $result.='&nbsp;<input type="submit" name="submit" value="View/Grade" /></form>'."\n";
        !           693:   $result.='</td></tr></table>'."\n";
        !           694:   $result.='</td></tr></table>'."\n";
        !           695:   return $result;
        !           696: }
        !           697: 
        !           698: sub upcsvScores_form {
        !           699:   my ($symb,$url) = @_;
        !           700:   if (!$symb) {return '';}
        !           701:   my $result.='<table width=100% border=0><tr><td bgcolor=#777777>'."\n";
        !           702:   $result.='<table width=100% border=0><tr bgcolor="#e6ffff"><td>'."\n";
        !           703:   $result.='&nbsp;<b>Specify a file containing the class scores for above resource</b></td></tr>'."\n";
        !           704:   $result.='<tr bgcolor=#ffffe6><td>'."\n";
        !           705:   my $upfile_select=&Apache::loncommon::upfile_select_html();
        !           706:   $result.=<<ENDUPFORM;
        !           707: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
        !           708: <input type="hidden" name="symb" value="$symb" />
        !           709: <input type="hidden" name="url" value="$url" />
        !           710: <input type="hidden" name="command" value="csvuploadmap" />
        !           711: $upfile_select
        !           712: <br />&nbsp;<input type="submit" name="submit" value="Upload Grades" />
        !           713: </form>
        !           714: ENDUPFORM
        !           715:   $result.='</td></tr></table>'."\n";
        !           716:   $result.='</td></tr></table>'."\n";
        !           717:   return $result;
        !           718: }
        !           719: 
        !           720: sub viewGradeaStu_form {
        !           721:   my ($symb,$url) = @_;
        !           722:   my ($classlist,$sections) = &getclasslist('all','0');
        !           723:   my $result.='<table width=100% border=0><tr><td bgcolor=#777777>'."\n";
        !           724:   $result.='<table width=100% border=0><tr bgcolor="#e6ffff"><td>'."\n";
        !           725:   $result.='&nbsp;<b>View/Grade an Individual Student\'s Submission</b></td></tr>'."\n";
        !           726:   $result.='<tr bgcolor=#ffffe6><td>'."\n";
1.26      albertel  727:   $result.='<form action="/adm/grades" method="post">'."\n".
                    728:      '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
                    729:       '<input type="hidden" name="url" value="'.$url.'" />'."\n".
1.34    ! ng        730: 	  '<input type="hidden" name="command" value="submission" />'."\n";
        !           731: 
        !           732:   $result.='&nbsp;<b>Select section:</b> <select name="section">'."\n";
        !           733:   foreach my $section (sort (@$sections)) {
        !           734:       $result.= '<option>'.$section.'</option>'."\n";
        !           735:   }
        !           736:   $result.= '<option selected="on">all</select>'."\n";
        !           737:   $result.='&nbsp;&nbsp;<b>Display students who has: </b>'.
        !           738:       '<input type="radio" name="submitonly" value="yes" checked> submitted'.
        !           739: 	  '<input type="radio" name="submitonly" value="all"> everybody <br />';
        !           740:   $result.='&nbsp;(Section -1 implies the students were not assigned a section.)<br />' if (grep /-1/,@$sections);
        !           741: 
        !           742:   $result.='<br />&nbsp;<input type="submit" name="submit" value="View/Grade" />'."\n".
        !           743:       '</form>'."\n";
        !           744:   $result.='</td></tr></table>'."\n";
        !           745:   $result.='</td></tr></table>'."\n";
        !           746:   return $result;
        !           747: }
        !           748: 
        !           749: sub verifyReceipt_form {
        !           750:   my ($symb,$url) = @_;
        !           751:   my $cdom=$ENV{"course.$ENV{'request.course.id'}.domain"};
        !           752:   my $cnum=$ENV{"course.$ENV{'request.course.id'}.num"};
        !           753:   my $hostver=unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'});
        !           754: 
        !           755:   my $result.='<table width=100% border=0><tr><td bgcolor=#777777>'."\n";
        !           756:   $result.='<table width=100% border=0><tr><td bgcolor=#e6ffff>'."\n";
        !           757:   $result.='&nbsp;<b>Verify a Submission Receipt Issued by this Server</td></tr>'."\n";
        !           758:   $result.='<tr bgcolor=#ffffe6><td>'."\n";
        !           759:   $result.='<form action="/adm/grades" method="post">'."\n";
        !           760:   $result.='&nbsp;<tt>'.$hostver.'-<input type="text" name="receipt" size="4"></tt><br />'."\n";
        !           761:   $result.='&nbsp;<input type="submit" name="submit" value="Verify Receipt">'."\n";
        !           762:   $result.='<input type="hidden" name="command" value="verify">'."\n";
        !           763:   if ($ENV{'form.url'}) {
        !           764:       $result.='<input type="hidden" name="url" value="'.$ENV{'form.url'}.'" />';
        !           765:   }
        !           766:   if ($ENV{'form.symb'}) {
        !           767:       $result.='<input type="hidden" name="symb" value="'.$ENV{'form.symb'}.'" />';
        !           768:   }
        !           769:   $result.='</form>';
1.28      ng        770:   $result.='</td></tr></table>'."\n";
                    771:   $result.='</td></tr></table>'."\n";
1.26      albertel  772:   return $result;
                    773: }
                    774: 
                    775: sub viewgrades {
                    776:   my ($request) = @_;
                    777:   my $result='';
                    778: 
                    779:   #get resource reference
                    780:   my ($symb,$url)=&get_symb_and_url($request);
                    781:   if (!$symb) {return '';}
1.13      albertel  782:   #get classlist
                    783:   my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});
1.24      albertel  784:   #print "Found $cdom:$cnum<br />";
1.34    ! ng        785:   my ($classlist) = &getclasslist('all','0');
1.13      albertel  786:   my $headerclr = '"#ccffff"';
                    787:   my $cellclr = '"#ffffcc"';
                    788: 
                    789:   #get list of parts for this problem
1.29      albertel  790:   my (@parts) = sort(&getpartlist($url));
1.13      albertel  791: 
1.28      ng        792:   $request->print ("<h2><font color=\"#339933\">Manual Grading</font></h2>");
1.13      albertel  793: 
                    794:   #start the form
                    795:   $result = '<form action="/adm/grades" method="post">'."\n".
1.16      albertel  796:     '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
                    797:       '<input type="hidden" name="url" value="'.$url.'" />'."\n".
1.13      albertel  798: 	'<input type="hidden" name="command" value="editgrades" />'."\n".
                    799: 	  '<input type="submit" name="submit" value="Submit Changes" />'."\n".
1.32      ng        800: 	    '<table border=0><tr><td bgcolor="#777777">'."\n".
1.13      albertel  801: 	     '<table border=0>'."\n".
1.28      ng        802: 	      '<tr bgcolor='.$headerclr.'><td><b>Username</b></td><td><b>Name</b></td><td><b>Domain</b></td>'."\n";
1.29      albertel  803:   foreach my $part (@parts) {
1.13      albertel  804:      my $display=&Apache::lonnet::metadata($url,$part.'.display');
                    805:      if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
1.28      ng        806:      $result.='<td><b>'.$display.'</b></td>'."\n";
1.30      ng        807:   }
1.28      ng        808:   $result.='</tr>';
1.13      albertel  809:   #get info for each student
1.34    ! ng        810:   foreach my $student ( sort(@{ $$classlist{'all'} }) ) {
1.31      ng        811:     my $display=&viewstudentgrade($url,$symb,$ENV{'request.course.id'},$student,@parts);
                    812: #      print "ID=$ENV{'request.course.id'}:STU=$student:DIS=$display:<br>\n";
1.13      albertel  813:     $result.=&viewstudentgrade($url,$symb,$ENV{'request.course.id'},$student,@parts);
                    814:   }
1.31      ng        815:   $result.='</table></td></tr></table>';
                    816:   $result.='<input type="submit" name="submit" value="Submit Changes" /></form>';
1.29      albertel  817:   $result.=&show_grading_menu_form($symb,$url);
1.13      albertel  818:   return $result;
1.5       albertel  819: }
                    820: 
                    821: sub editgrades {
1.13      albertel  822:   my ($request) = @_;
                    823:   my $result='';
1.5       albertel  824: 
1.13      albertel  825:   my $symb=$ENV{'form.symb'};
                    826:   if ($symb eq '') { $request->print("Unable to handle ambiguous references:$symb:$ENV{'form.url'}"); return ''; }
                    827:   my $url=$ENV{'form.url'};
                    828:   #get classlist
1.34    ! ng        829: #  my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});
1.24      albertel  830:   #print "Found $cdom:$cnum<br />";
1.34    ! ng        831:   my ($classlist) = &getclasslist('all','0');
1.13      albertel  832: 
                    833:   #get list of parts for this problem
                    834:   my (@parts) = &getpartlist($url);
                    835: 
                    836:   $result.='<form action="/adm/grades" method="post">'."\n".
                    837:     '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
                    838:       '<input type="hidden" name="url" value="'.$url.'" />'."\n".
                    839: 	'<input type="hidden" name="command" value="viewgrades" />'."\n".
                    840: 	  '<input type="submit" name="submit" value="See Grades" /> <br />'."\n";
                    841: 
1.34    ! ng        842:   foreach my $student ( sort(@{ $$classlist{'all   '} }) ) {
1.13      albertel  843:     $result.=&setstudentgrade($url,$symb,$ENV{'request.course.id'},$student,@parts);
                    844:   }
1.5       albertel  845: 
1.13      albertel  846:   $result.='<input type="submit" name="submit" value="See Grades" /></table></form>';
                    847:   return $result;
1.5       albertel  848: }
                    849: 
1.27      albertel  850: sub csvupload_javascript_reverse_associate {
                    851:   return(<<ENDPICK);
                    852:   function verify(vf) {
                    853:     var foundsomething=0;
                    854:     var founduname=0;
                    855:     var founddomain=0;
                    856:     for (i=0;i<=vf.nfields.value;i++) {
                    857:       tw=eval('vf.f'+i+'.selectedIndex');
                    858:       if (i==0 && tw!=0) { founduname=1; }
                    859:       if (i==1 && tw!=0) { founddomain=1; }
                    860:       if (i!=0 && i!=1 && tw!=0) { foundsomething=1; }
                    861:     }
                    862:     if (founduname==0 || founddomain==0) {
                    863:       alert('You need to specify at both the username and domain');
                    864:       return;
                    865:     }
                    866:     if (foundsomething==0) {
                    867:       alert('You need to specify at least one grading field');
                    868:       return;
                    869:     }
                    870:     vf.submit();
                    871:   }
                    872:   function flip(vf,tf) {
                    873:     var nw=eval('vf.f'+tf+'.selectedIndex');
                    874:     var i;
                    875:     for (i=0;i<=vf.nfields.value;i++) {
                    876:       //can not pick the same destination field for both name and domain
                    877:       if (((i ==0)||(i ==1)) && 
                    878:           ((tf==0)||(tf==1)) && 
                    879:           (i!=tf) &&
                    880:           (eval('vf.f'+i+'.selectedIndex')==nw)) {
                    881:         eval('vf.f'+i+'.selectedIndex=0;')
                    882:       }
                    883:     }
                    884:   }
                    885: ENDPICK
                    886: }
                    887: 
                    888: sub csvupload_javascript_forward_associate {
                    889:   return(<<ENDPICK);
                    890:   function verify(vf) {
                    891:     var foundsomething=0;
                    892:     var founduname=0;
                    893:     var founddomain=0;
                    894:     for (i=0;i<=vf.nfields.value;i++) {
                    895:       tw=eval('vf.f'+i+'.selectedIndex');
                    896:       if (tw==1) { founduname=1; }
                    897:       if (tw==2) { founddomain=1; }
                    898:       if (tw>2) { foundsomething=1; }
                    899:     }
                    900:     if (founduname==0 || founddomain==0) {
                    901:       alert('You need to specify at both the username and domain');
                    902:       return;
                    903:     }
                    904:     if (foundsomething==0) {
                    905:       alert('You need to specify at least one grading field');
                    906:       return;
                    907:     }
                    908:     vf.submit();
                    909:   }
                    910:   function flip(vf,tf) {
                    911:     var nw=eval('vf.f'+tf+'.selectedIndex');
                    912:     var i;
                    913:     //can not pick the same destination field twice
                    914:     for (i=0;i<=vf.nfields.value;i++) {
                    915:       if ((i!=tf) && (eval('vf.f'+i+'.selectedIndex')==nw)) {
                    916:         eval('vf.f'+i+'.selectedIndex=0;')
                    917:       }
                    918:     }
                    919:   }
                    920: ENDPICK
                    921: }
                    922: 
1.26      albertel  923: sub csvuploadmap_header {
                    924:   my ($request,$symb,$url,$datatoken,$distotal)= @_;
                    925:   my $result;
                    926:   my $javascript;
                    927:   if ($ENV{'form.upfile_associate'} eq 'reverse') {
1.27      albertel  928:     $javascript=&csvupload_javascript_reverse_associate();
1.26      albertel  929:   } else {
1.27      albertel  930:     $javascript=&csvupload_javascript_forward_associate();
1.26      albertel  931:   }
                    932:   $request->print(<<ENDPICK);
                    933: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
                    934: <h3>Uploading Class Grades for resource $url</h3>
                    935: <hr>
                    936: <h3>Identify fields</h3>
                    937: Total number of records found in file: $distotal <hr />
                    938: Enter as many fields as you can. The system will inform you and bring you back
                    939: to this page if the data selected is insufficient to run your class.<hr />
                    940: <input type="button" value="Reverse Association" onClick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" />
                    941: <input type="hidden" name="associate"  value="" />
                    942: <input type="hidden" name="phase"      value="three" />
                    943: <input type="hidden" name="datatoken"  value="$datatoken" />
                    944: <input type="hidden" name="fileupload" value="$ENV{'form.fileupload'}" />
                    945: <input type="hidden" name="upfiletype" value="$ENV{'form.upfiletype'}" />
                    946: <input type="hidden" name="upfile_associate" 
                    947:                                        value="$ENV{'form.upfile_associate'}" />
                    948: <input type="hidden" name="symb"       value="$symb" />
                    949: <input type="hidden" name="url"        value="$url" />
                    950: <input type="hidden" name="command"    value="csvuploadassign" />
                    951: <hr />
                    952: <script type="text/javascript" language="Javascript">
                    953: $javascript
                    954: </script>
                    955: ENDPICK
                    956:   return '';
                    957: 
                    958: }
                    959: 
                    960: sub csvupload_fields {
                    961:   my ($url) = @_;
                    962:   my (@parts) = &getpartlist($url);
1.27      albertel  963:   my @fields=(['username','Student Username'],['domain','Student Domain']);
                    964:   foreach my $part (sort(@parts)) {
1.26      albertel  965:     my @datum;
                    966:     my $display=&Apache::lonnet::metadata($url,$part.'.display');
1.27      albertel  967:     my $name=$part;
1.26      albertel  968:     if  (!$display) { $display = $name; }
                    969:     @datum=($name,$display);
                    970:     push(@fields,\@datum);
                    971:   }
                    972:   return (@fields);
                    973: }
                    974: 
                    975: sub csvuploadmap_footer {
                    976:   my ($request,$i,$keyfields) =@_;
                    977:   $request->print(<<ENDPICK);
                    978: </table>
                    979: <input type="hidden" name="nfields" value="$i" />
                    980: <input type="hidden" name="keyfields" value="$keyfields" />
                    981: <input type="button" onClick="javascript:verify(this.form)" value="Assign Grades" /><br />
                    982: </form>
                    983: ENDPICK
                    984: }
                    985: 
                    986: sub csvuploadmap {
                    987:   my ($request)= @_;
                    988:   my ($symb,$url)=&get_symb_and_url($request);
                    989:   if (!$symb) {return '';}
                    990:   my $datatoken;
                    991:   if (!$ENV{'form.datatoken'}) {
                    992:     $datatoken=&Apache::loncommon::upfile_store($request);
                    993:   } else {
                    994:     $datatoken=$ENV{'form.datatoken'};
                    995:     &Apache::loncommon::load_tmp_file($request);
                    996:   }
                    997:   my @records=&Apache::loncommon::upfile_record_sep();
                    998:   &csvuploadmap_header($request,$symb,$url,$datatoken,$#records+1);
                    999:   my $i;
                   1000:   my $keyfields;
                   1001:   if (@records) {
                   1002:     my @fields=&csvupload_fields($url);
                   1003:     if ($ENV{'form.upfile_associate'} eq 'reverse') {	
                   1004:       &Apache::loncommon::csv_print_samples($request,\@records);
                   1005:       $i=&Apache::loncommon::csv_print_select_table($request,\@records,
                   1006: 						    \@fields);
                   1007:       foreach (@fields) { $keyfields.=$_->[0].','; }
                   1008:       chop($keyfields);
                   1009:     } else {
                   1010:       unshift(@fields,['none','']);
                   1011:       $i=&Apache::loncommon::csv_samples_select_table($request,\@records,
                   1012: 						      \@fields);
                   1013:       my %sone=&Apache::loncommon::record_sep($records[0]);
                   1014:       $keyfields=join(',',sort(keys(%sone)));
                   1015:     }
                   1016:   }
                   1017:   &csvuploadmap_footer($request,$i,$keyfields);
                   1018:   return '';
1.27      albertel 1019: }
                   1020: 
                   1021: sub csvuploadassign {
                   1022:   my ($request)= @_;
                   1023:   my ($symb,$url)=&get_symb_and_url($request);
                   1024:   if (!$symb) {return '';}
                   1025:   &Apache::loncommon::load_tmp_file($request);
                   1026:   my @gradedata=&Apache::loncommon::upfile_record_sep();
                   1027:   my @keyfields = split(/\,/,$ENV{'form.keyfields'});
                   1028:   my %fields=();
                   1029:   for (my $i=0; $i<=$ENV{'form.nfields'}; $i++) {
                   1030:     if ($ENV{'form.upfile_associate'} eq 'reverse') {
                   1031:       if ($ENV{'form.f'.$i} ne 'none') {
                   1032: 	$fields{$keyfields[$i]}=$ENV{'form.f'.$i};
                   1033:       }
                   1034:     } else {
                   1035:       if ($ENV{'form.f'.$i} ne 'none') {
                   1036: 	$fields{$ENV{'form.f'.$i}}=$keyfields[$i];
                   1037:       }
                   1038:     }
                   1039:   }
                   1040:   $request->print('<h3>Assigning Grades</h3>');
                   1041:   my $courseid=$ENV{'request.course.id'};
1.34    ! ng       1042: #  my $cdom=$ENV{"course.$courseid.domain"};
        !          1043: #  my $cnum=$ENV{"course.$courseid.num"};
        !          1044:   my ($classlist) = &getclasslist('all','1');
1.29      albertel 1045:   my @skipped;
                   1046:   my $countdone=0;
                   1047:   foreach my $grade (@gradedata) {
                   1048:     my %entries=&Apache::loncommon::record_sep($grade);
                   1049:     my $username=$entries{$fields{'username'}};
                   1050:     my $domain=$entries{$fields{'domain'}};
1.34    ! ng       1051:     if (!exists($$classlist{"$username:$domain"})) {
1.29      albertel 1052:       push(@skipped,"$username:$domain");
                   1053:       next;
1.27      albertel 1054:     }
1.29      albertel 1055:     my %grades;
                   1056:     foreach my $dest (keys(%fields)) {
                   1057:       if ($dest eq 'username' || $dest eq 'domain') { next; }
                   1058:       if ($entries{$fields{$dest}} eq '') { next; }
                   1059:       my $store_key=$dest;
                   1060:       $store_key=~s/^stores/resource/;
                   1061:       $store_key=~s/_/\./g;
                   1062:       $grades{$store_key}=$entries{$fields{$dest}};
                   1063:     }
                   1064:     $grades{"resource.regrader"}="$ENV{'user.name'}:$ENV{'user.domain'}";
                   1065:     &Apache::lonnet::cstore(\%grades,$symb,$ENV{'request.course.id'},
                   1066: 			    $domain,$username);
                   1067:     $request->print('.');
                   1068:     $request->rflush();
                   1069:     $countdone++;
                   1070:   }
                   1071:   $request->print("<br />Stored $countdone students\n");
                   1072:   if (@skipped) {
                   1073:     $request->print('<br /><font size="+1"><b>Skipped Students</b></font><br />');
                   1074:     foreach my $student (@skipped) { $request->print("<br />$student"); }
1.27      albertel 1075:   }
1.29      albertel 1076:   $request->print(&view_edit_entire_class_form($symb,$url));
                   1077:   $request->print(&show_grading_menu_form($symb,$url));
                   1078:   return '';
1.26      albertel 1079: }
                   1080: 
1.2       albertel 1081: sub send_header {
1.13      albertel 1082:   my ($request)= @_;
                   1083:   $request->print(&Apache::lontexconvert::header());
1.6       albertel 1084: #  $request->print("
                   1085: #<script>
                   1086: #remotewindow=open('','homeworkremote');
                   1087: #remotewindow.close();
                   1088: #</script>"); 
1.13      albertel 1089:   $request->print('<body bgcolor="#FFFFFF">');
1.2       albertel 1090: }
                   1091: 
                   1092: sub send_footer {
1.13      albertel 1093:   my ($request)= @_;
1.2       albertel 1094:   $request->print('</body>');
                   1095:   $request->print(&Apache::lontexconvert::footer());
                   1096: }
                   1097: 
1.1       albertel 1098: sub handler {
1.13      albertel 1099:   my $request=$_[0];
                   1100: 
                   1101:   if ($ENV{'browser.mathml'}) {
                   1102:     $request->content_type('text/xml');
                   1103:   } else {
                   1104:     $request->content_type('text/html');
                   1105:   }
                   1106:   $request->send_http_header;
                   1107:   return OK if $request->header_only;
1.16      albertel 1108:   &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});
1.13      albertel 1109:   my $url=$ENV{'form.url'};
                   1110:   my $symb=$ENV{'form.symb'};
                   1111:   my $command=$ENV{'form.command'};
1.16      albertel 1112:   if (!$url) {
                   1113:     my ($temp1,$temp2);
                   1114:     ($temp1,$temp2,$ENV{'form.url'})=split(/___/,$symb);
                   1115:     $url = $ENV{'form.url'};
                   1116:   }
1.13      albertel 1117:   &send_header($request);
                   1118:   if ($url eq '' && $symb eq '') {
1.14      www      1119:      if ($ENV{'user.adv'}) {
                   1120:          if (($ENV{'form.codeone'}) && ($ENV{'form.codetwo'}) &&
                   1121:              ($ENV{'form.codethree'})) {
                   1122:              my $token=$ENV{'form.codeone'}.'*'.$ENV{'form.codetwo'}.'*'.
                   1123: 		        $ENV{'form.codethree'};
                   1124:              my ($tsymb,$tuname,$tudom,$tcrsid)=
                   1125: 		 &Apache::lonnet::checkin($token);
                   1126:              if ($tsymb) {
                   1127:                 my ($map,$id,$url)=split(/\_\_\_/,$tsymb);
                   1128:                 if (&Apache::lonnet::allowed('mgr',$tcrsid)) {
                   1129:                    $request->print(
                   1130:                      &Apache::lonnet::ssi('/res/'.$url,
                   1131:                         ('grade_username' => $tuname,
                   1132:                          'grade_domain' => $tudom,
                   1133:                          'grade_courseid' => $tcrsid,
                   1134:                          'grade_symb' => $tsymb)));
                   1135:                 } else {
                   1136:                    $request->print('<h1>Not authorized: '.$token.'</h1>');
                   1137:                 }           
                   1138: 	    } else {
                   1139:                 $request->print('<h1>Not a valid DocID: '.$token.'</h1>');
                   1140:             }
                   1141: 	 } else {
                   1142:              $request->print(&Apache::lonxml::tokeninputfield());
                   1143:          }
                   1144:      }
1.13      albertel 1145:   } else {
1.29      albertel 1146:     #&Apache::lonhomework::showhashsubset(\%ENV,'^form');
1.13      albertel 1147:     $Apache::grades::viewgrades=&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'});
                   1148:     if ($command eq 'submission') {
1.20      albertel 1149:       &listStudents($request) if ($ENV{'form.student'} eq '');
1.34    ! ng       1150:       &submission($request,0,0) if ($ENV{'form.student'} ne '');
        !          1151:     } elsif ($command eq 'processGroup') {
        !          1152:       &processGroup($request);
1.26      albertel 1153:     } elsif ($command eq 'gradingmenu') {
                   1154:       $request->print(&gradingmenu($request));
1.13      albertel 1155:     } elsif ($command eq 'viewgrades') {
                   1156:       $request->print(&viewgrades($request));
1.33      ng       1157:     } elsif ($command eq 'handgrade') {
                   1158:       $request->print(&processHandGrade($request));
1.13      albertel 1159:     } elsif ($command eq 'editgrades') {
                   1160:       $request->print(&editgrades($request));
1.23      www      1161:     } elsif ($command eq 'verify') {
                   1162:       $request->print(&verifyreceipt($request));
1.26      albertel 1163:     } elsif ($command eq 'csvupload') {
                   1164:       $request->print(&csvupload($request));
                   1165:     } elsif ($command eq 'csvuploadmap') {
                   1166:       $request->print(&csvuploadmap($request));
1.34    ! ng       1167: #    } elsif ($command eq 'receiptInput') {
        !          1168: #      &receiptInput($request);
1.26      albertel 1169:     } elsif ($command eq 'csvuploadassign') {
                   1170:       if ($ENV{'form.associate'} ne 'Reverse Association') {
                   1171: 	$request->print(&csvuploadassign($request));
                   1172:       } else {
                   1173: 	if ( $ENV{'form.upfile_associate'} ne 'reverse' ) {
                   1174: 	  $ENV{'form.upfile_associate'} = 'reverse';
                   1175: 	} else {
                   1176: 	  $ENV{'form.upfile_associate'} = 'forward';
                   1177: 	}
                   1178: 	$request->print(&csvuploadmap($request));
                   1179:       }
1.12      harris41 1180:     } else {
1.23      www      1181:       $request->print("Unknown action: $command:");
1.2       albertel 1182:     }
1.13      albertel 1183:   }
                   1184:   &send_footer($request);
                   1185:   return OK;
1.1       albertel 1186: }
                   1187: 
                   1188: 1;
                   1189: 
1.13      albertel 1190: __END__;

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