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

1.12    ! harris41    1: # The LON-CAPA grading handler.
        !             2: #
        !             3: # Handles the viewing of grades.
        !             4: #
        !             5: # YEAR=2001
        !             6: # 2/7,2/9,2/13,4/16,4/17,5/1 Guy Albertelli
1.8       www         7: # 6/8 Gerd Kortemeyer
1.12    ! harris41    8: # 7/26 Guy Albertelli
        !             9: # 7/27 H.K. Ng
        !            10: # 7/30 Guy Albertelli
        !            11: # 8/6 Scott Harrison
1.1       albertel   12: 
                     13: package Apache::grades;
                     14: use strict;
                     15: use Apache::style;
                     16: use Apache::lonxml;
                     17: use Apache::lonnet;
1.3       albertel   18: use Apache::loncommon;
1.1       albertel   19: use Apache::lonhomework;
                     20: use Apache::Constants qw(:common);
                     21: 
1.12    ! harris41   22: # ======================================================== Get more information
1.2       albertel   23: sub moreinfo {
1.12    ! harris41   24:     my ($request,$reason) = @_;
        !            25:     $request->print("Unable to process request: $reason");
        !            26:     if ( $Apache::grades::viewgrades eq 'F' ) {
        !            27: 	$request->print('<form action="/adm/grades" method="post">'."\n");
        !            28: 	$request->print('<input type="hidden" name="url" value="'.
        !            29: 			$ENV{'form.url'}.'"></input>'."\n");
        !            30: 	$request->print('<input type="hidden" name="command" value="'.
        !            31: 			$ENV{'form.command'}.'"></input>'."\n");
        !            32: 	$request->print("Student:".
        !            33: 			'<input type="text" name="student" value="'.
        !            34: 			$ENV{'form.student'}.'"></input>'."<br />\n");
        !            35: 	$request->print("Domain:".
        !            36: 			'<input type="text" name="domain" value="'.
        !            37: 			$ENV{'user.domain'}.'"></input>'."<br />\n");
        !            38: 	$request->print('<input type="submit" name="submit" '.
        !            39: 			'value="ReSubmit"></input>'."<br />\n");
        !            40: 	$request->print('</form>');
        !            41:     }
        !            42:     return '';
1.2       albertel   43: }
                     44: 
1.12    ! harris41   45: # ========================================= Displays the class list of students
1.10      ng         46: sub listStudents {
1.12    ! harris41   47:     my ($request) = shift;
        !            48:     my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});
        !            49:     my $chome = $ENV{"course.$ENV{'request.course.id'}.home"};
        !            50:     $request->print ("Found $cdom:$cnum:$chome<br />");
        !            51:     my (%classlist) = &getclasslist($cdom,$cnum,$chome,'0');
        !            52:     foreach my $student ( sort(@{ $classlist{'allids'} }) ) {
        !            53: 	my ($sname,$sdom) = split(/:/,$student);
        !            54: 	my $reply = &Apache::lonnet::reply('get:'.$sdom.':'.$sname.
        !            55: 		    ':environment:lastname&generation&firstname&middle'.
        !            56: 		    'name',&Apache::lonnet::homeserver($sname,$sdom));
        !            57: 	print "reply=$reply<br>";
        !            58: 	my (@nameparts) = split /&/,$reply;
        !            59: #        my $sfullname = $Apache::lonnet::unescape($nameparts[0]);
        !            60: 	if ( $Apache::grades::viewgrades eq 'F' ) {
        !            61: 	    $request->print('<form action="/adm/grades" method="post">'."\n");
        !            62: 	    $request->print("$sname @nameparts\n");
        !            63: 	    $request->print('<input type="hidden" name="url" value="'.
        !            64: 			    $ENV{'form.url'}.'"></input>'."\n");
        !            65: 	    $request->print('<input type="hidden" name="command" value="'.
        !            66: 			    $ENV{'form.command'}.'"></input>'."\n");
        !            67: 	    $request->print('<input type="hidden" name="student" '.
        !            68: 			    'value="'.$sname.'"></input>'."\n");
        !            69: 	    $request->print('<input type="hidden" name="domain" '.
        !            70: 			    'value="'.$sdom.'"></input>'."\n");
        !            71: 	    $request->print('<input type="submit" name="submit" '.
        !            72: 			    'value="View"></input>'."<br />\n");
        !            73: 	    $request->print('</form>');
        !            74: 	}
        !            75:     }
1.10      ng         76: }
                     77: 
1.12    ! harris41   78: # ========== Finds a user based on a name substring (returns a 2 element array)
1.7       albertel   79: #FIXME - needs to handle multiple matches
1.2       albertel   80: sub finduser {
1.12    ! harris41   81:     my ($name) = @_;
        !            82:     my $domain = '';
        !            83:     if ( $Apache::grades::viewgrades eq 'F' ) {
        !            84: 	#get classlist
        !            85: 	my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});
        !            86: 	my $chome = $ENV{"course.$ENV{'request.course.id'}.home"};
        !            87: 	#print "Found $cdom:$cnum:$chome<br />";
        !            88: 	my (%classlist) = &getclasslist($cdom,$cnum,$chome,'0');
        !            89: 	foreach my $student ( sort(@{ $classlist{'allids'} }) ) {
        !            90: 	    my ($posname,$posdomain) = split(/:/,$student);
        !            91: 	    if ($posname =~ $name) { 
        !            92: 		$name = $posname;
        !            93: 		$domain = $posdomain;
        !            94: 		last;
        !            95: 	    }
        !            96: 	}
        !            97: 	return ($name,$domain);
        !            98:     } else {
        !            99: 	return ($ENV{'user.name'},$ENV{'user.domain'});
1.7       albertel  100:     }
1.5       albertel  101: }
                    102: 
1.12    ! harris41  103: # ===================================== Gets a class list (returns a hash list)
1.5       albertel  104: sub getclasslist {
1.12    ! harris41  105:     my ($coursedomain,$coursenum,$coursehome,$hideexpired) = @_;
        !           106:     my $classlist = &Apache::lonnet::reply('dump:'.$coursedomain.':'.
        !           107: 					 $coursenum.':classlist',$coursehome);
        !           108:     my %classlist = ();
        !           109:     my $now = time;
        !           110:     foreach my $record (split /&/, $classlist) {
        !           111: 	my ($name,$value)=split(/=/,&Apache::lonnet::unescape($record));
        !           112: 	my ($end,$start)=split(/:/,$value);
        !           113: 	# still a student?
        !           114: 	if (($hideexpired) && ($end) && ($end < $now)) {
        !           115: 	    print "Skipping:$name:$end:$now<br />\n";
        !           116: 	    next;
        !           117: 	}
        !           118: 	print "record=$record<br>";
        !           119: 	push( @{ $classlist{'allids'} }, $name); 
        !           120:     }
        !           121:     return (%classlist);
1.5       albertel  122: }
                    123: 
1.12    ! harris41  124: # ============================== Get parts of a stored value (returns an array)
1.5       albertel  125: sub getpartlist {
1.12    ! harris41  126:     my ($url) = @_;
        !           127:     my @parts =();
        !           128:     my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));
        !           129:     foreach my $key (@metakeys) {
        !           130: 	if ( $key =~ m/stores_([0-9]+)_.*/ ) {
        !           131: 	    push(@parts,$key);
        !           132: 	}
1.6       albertel  133:     }
1.12    ! harris41  134:     return @parts;
1.5       albertel  135: }
                    136: 
1.12    ! harris41  137: # =================================== Displays student grade (returns a string)
1.5       albertel  138: sub viewstudentgrade {
1.12    ! harris41  139:     my ($url,$symb,$courseid,$student,@parts) = @_;
        !           140:     my $result = '';
        !           141:     my $cellclr = '"#ffffdd"';
        !           142:     my ($stuname,$domain) = split(/:/,$student);
        !           143:     my %record = &Apache::lonnet::restore($symb,$courseid,$domain,$stuname);
        !           144:     $result .= "<tr><td bgcolor=$cellclr>$stuname</td><td bgcolor=".
        !           145: 	       "\"$cellclr\" align=\"middle\">$domain</td>\n";
        !           146:     foreach my $part (@parts) {
        !           147: 	my ($temp,$part,$type)=split(/_/,$part);
        !           148: 	#print "resource.$part.$type = ".$record{"resource.$part.$type"}." <br />\n";
        !           149: 	if ($type eq 'awarded') {
        !           150: 	    my $score = $record{"resource.$part.$type"};
        !           151: 	    $result .= "<td bgcolor=$cellclr align=\"middle\">".
        !           152: 		       "<input type=\"text\" name=".
        !           153: 		       "\"GRADE.$student.$part.$type\" ".
        !           154: 		       "value=\"$score\" size=\"4\" /></td>\n";
        !           155: 	} elsif ($type eq 'tries') {
        !           156: 	    my $score = $record{"resource.$part.$type"};
        !           157: 	    $result .= "<td bgcolor=$cellclr align=\"middle\"><input ".
        !           158: 		       "type=\"text\" name=\"GRADE.$student.$part.".
        !           159:  		       "$type\" value=\"$score\" size=\"4\" /></td>\n";
        !           160: 	} elsif ($type eq 'solved') {
        !           161: 	    my $score = $record{"resource.$part.$type"};
        !           162: 	    $result .= "<td bgcolor=\"$cellclr\" align=\"middle\">".
        !           163: 		       "<select name=\"GRADE.$student.$part.$type\">\n";
        !           164: 	    if ($score =~ /^correct/) {
        !           165: 		$result .= "<option selected=\"on\">correct</option>".
        !           166: 		           "\n<option>incorrect</option>\n<option>".
        !           167: 			   "excused</option>\n<option>ungraded".
        !           168: 			   "</option>\n<option>nothing</option>\n";
        !           169: 	    } elsif ($score =~ /^incorrect/) {
        !           170: 		$result .= "<option>correct</option>\n<option ".
        !           171: 		           "selected=\"on\">incorrect</option>\n".
        !           172: 			   "<option>excused</option>\n<option>".
        !           173: 			   "ungraded</option>\n<option>nothing</option>\n";
        !           174: 	    } elsif ($score eq '') {
        !           175: 		$result .= "<option>correct</option>\n<option> ".
        !           176: 		           "incorrect</option>\n<option>excused".
        !           177:                            "</option>\n<option>ungraded</option>\n".
        !           178: 			   "<option selected=\"on\">nothing</option>\n";
        !           179: 	    } elsif ($score =~ /^excused/) {
        !           180: 		$result .= "<option>correct</option>\n<option>".
        !           181:                            "incorrect</option>\n<option selected=".
        !           182:                            "\"on\">excused</option>\n<option>".
        !           183: 			   "ungraded</option>\n<option>nothing</option>\n";
        !           184: 	    } elsif ($score =~ /^ungraded/) {
        !           185: 		$result .= "<option>correct</option>\n<option>".
        !           186: 		           "incorrect</option>\n<option>excused".
        !           187:                            "</option>\n<option selected=\"on\">".
        !           188: 			   "ungraded</option>\n<option>nothing</option>\n";
        !           189: 	    }
        !           190: 	    $result .= "</select></td>\n";
        !           191: 	}
        !           192:     }
        !           193:     $result .= '</tr>';
        !           194:     return $result;
1.5       albertel  195: }
1.12    ! harris41  196: 
        !           197: # ========================================================== Sets student grade
        !           198: #FIXME need to look at the meatdata <stores> spec
        !           199: # on what type of data to accept and provide an
1.6       albertel  200: #interface based on that, also do that to above function.
1.5       albertel  201: sub setstudentgrade {
1.12    ! harris41  202:     my ($url,$symb,$courseid,$student,@parts) = @_;
        !           203:     my $result ='';
        !           204:     my ($stuname,$domain) = split(/:/,$student);
        !           205:     my %record=&Apache::lonnet::restore($symb,$courseid,$domain,$stuname);
        !           206:     my %newrecord;
        !           207:     foreach my $part (@parts) {
        !           208: 	my ($temp,$part,$type) = split(/_/,$part);
        !           209: 	my $oldscore = $record{"resource.$part.$type"};
        !           210: 	my $newscore = $ENV{"form.GRADE.$student.$part.$type"};
        !           211: 	if ($type eq 'solved') {
        !           212: 	    my $update = 0;
        !           213: 	    if ($newscore eq 'nothing' ) {
        !           214: 		if ($oldscore ne '') {
        !           215: 		    $update = 1;
        !           216: 		    $newscore = '';
        !           217: 		}
        !           218: 	    } elsif ($oldscore !~ m/^$newscore/) {
        !           219: 		$update=1;
        !           220: 		$result.="Updating $stuname to $newscore<br />\n";
        !           221: 		if ($newscore eq 'correct') { 
        !           222: 		    $newscore = 'correct_by_override'; }
        !           223: 		if ($newscore eq 'incorrect') {
        !           224: 		    $newscore = 'incorrect_by_override'; }
        !           225: 		if ($newscore eq 'excused') {
        !           226: 		    $newscore = 'excused'; }
        !           227: 		if ($newscore eq 'ungraded') {
        !           228: 		    $newscore = 'ungraded_attempted'; }
        !           229: 	    } else {
        !           230: 		#$result.="$stuname:$part:$type:unchanged  $oldscore to $newscore:<br />\n";
        !           231: 	    }
        !           232: 	    if ($update) { $newrecord{"resource.$part.$type"}=$newscore; }
        !           233: 	} else {
        !           234: 	    if ($oldscore ne $newscore) {
        !           235: 		$newrecord{"resource.$part.$type"}=$newscore;
        !           236: 		$result .= "Updating $student"."'s status for $part.$type to ".
        !           237: 		           "$newscore<br />\n";
        !           238: 	    } else {
        !           239: 		#$result.="$stuname:$part:$type:unchanged  $oldscore to $newscore:<br />\n";
        !           240: 	    }
1.6       albertel  241: 	}
1.12    ! harris41  242:     }
        !           243:     if ( scalar(keys(%newrecord)) > 0 ) {
        !           244: 	$newrecord{"resource.regrader"} = $ENV{'user.name'}.':'.
        !           245: 	                                  $ENV{'user.domain'};
        !           246: 	&Apache::lonnet::cstore(\%newrecord,$symb,$courseid,$domain,$stuname);
        !           247: 	
        !           248: 	$result .= 'Stored away '.scalar(keys(%newrecord)).
        !           249:   	           " elements.<br />\n";
        !           250:     }
        !           251:     return $result;
1.2       albertel  252: }
                    253: 
1.12    ! harris41  254: 
        !           255: # ========================================================== Attempt submission
1.2       albertel  256: sub submission {
1.12    ! harris41  257:     my ($request) = @_;
        !           258:     my $url = $ENV{'form.url'};
        !           259:     $url =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
        !           260:     if ($ENV{'form.student'} eq '') { 
        !           261: 	&moreinfo($request,"Need student login id"); return ''; }
1.10      ng        262: #  if ($ENV{'form.student'} eq '') { &listStudents($request); return ''; }
1.12    ! harris41  263:     my ($uname,$udom) = &finduser($ENV{'form.student'});
        !           264:     if ($uname eq '') {
        !           265: 	&moreinfo($request,"Unable to find student"); return ''; }
        !           266:     my $symb=&Apache::lonnet::symbread($url);
        !           267:     if ($symb eq '') {
        !           268: 	$request->print("Unable to handle ambiguous references:$url:.");
        !           269: 	return ''; }
        !           270:     my $answer = &Apache::loncommon::get_previous_attempt($symb,$uname,$udom,
        !           271: 		 $ENV{'request.course.id'});
        !           272:     my $result = "<h2> Submission Record </h2>  $uname:$udom for $url <br />".
        !           273: 	         $answer;
        !           274:     return $result;
1.2       albertel  275: }
                    276: 
1.12    ! harris41  277: # ================================================================= View grades
1.5       albertel  278: sub viewgrades {
1.12    ! harris41  279:     my ($request) = @_;
        !           280:     my $result = '';
        !           281:     
        !           282:     #get resource reference
        !           283:     my $url = $ENV{'form.url'};
        !           284:     $url =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
        !           285:     my $symb=$ENV{'form.symb'};
        !           286:     if (!$symb) { $symb = &Apache::lonnet::symbread($url); }
        !           287:     if ($symb eq '') { 
        !           288: 	$request->print("Unable to handle ambiguous references:$url:.");
        !           289: 	return '';
        !           290:     }
        !           291:     
        !           292:     #get classlist
        !           293:     my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});
        !           294:     my $chome = $ENV{"course.$ENV{'request.course.id'}.home"};
        !           295:     #print "Found $cdom:$cnum:$chome<br />";
        !           296:     my (%classlist) = &getclasslist($cdom,$cnum,$chome,'0');
        !           297:     my $headerclr = '"#ccffff"';
        !           298:     my $cellclr = '"#ffffcc"';
1.5       albertel  299: 
1.12    ! harris41  300:     #get list of parts for this problem
        !           301:     my (@parts) = &getpartlist($url);
1.5       albertel  302: 
1.12    ! harris41  303:     $request->print("<h2><font color=\"#339966\">Manual Grading</font></h2>");
        !           304:     
        !           305:     #start the form
        !           306:     $result = '<form action="/adm/grades" method="post">'."\n".
        !           307: 	      '<input type="hidden" name="symb" value="'.$symb.'"/>'."\n".
        !           308: 	      '<input type="hidden" name="url" value="'.$url.'"/>'."\n".
        !           309: 	      '<input type="hidden" name="command" value="editgrades" />'."\n".
        !           310: 	      '<input type="submit" name="submit" value="Submit Changes" />'.
        !           311: 	      "\n".'<table border=0><tr><td bgcolor="#999999">'."\n".
        !           312: 	      '<table border=0>'."\n".
        !           313: 	      '<tr><td bgcolor='.$headerclr.'>UserId</td><td bgcolor='.
        !           314:               $headerclr.'>Domain</td>'."\n";
        !           315:     foreach my $part (@parts) {
        !           316: 	my $display = &Apache::lonnet::metadata($url,$part.'.display');
        !           317: 	if  (!$display) { 
        !           318: 	    $display = &Apache::lonnet::metadata($url,$part.'.name'); }
        !           319: 	$result .= '<td bgcolor='.$headerclr.'>'.$display.'</td>'."\n";
        !           320:     }
        !           321:     $result .= "</tr>";
        !           322: 
        !           323:     #get info for each student
        !           324:     foreach my $student ( sort(@{ $classlist{'allids'} }) ) {
        !           325: 	$result .= &viewstudentgrade($url,$symb,$ENV{'request.course.id'},
        !           326: 				     $student,@parts);
        !           327:     }
        !           328:     $result .= '</table></td></tr></table><input type="submit" name="submit"'.
        !           329: 	       'value="Submit Changes" /></form>';
        !           330:     return $result;
1.5       albertel  331: }
                    332: 
1.12    ! harris41  333: # ================================================================= Edit grades
1.5       albertel  334: sub editgrades {
1.12    ! harris41  335:     my ($request) = @_;
        !           336:     my $result='';
        !           337: 
        !           338:     my $symb = $ENV{'form.symb'};
        !           339:     if ($symb eq '') {
        !           340: 	$request->print("Unable to handle ambiguous references:$symb:".
        !           341: 			$ENV{'form.url'});
        !           342: 	return '';
        !           343:     }
        !           344:     my $url = $ENV{'form.url'};
        !           345:     #get classlist
        !           346:     my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});
        !           347:     my $chome = $ENV{"course.$ENV{'request.course.id'}.home"};
        !           348:     #print "Found $cdom:$cnum:$chome<br />";
        !           349:     my (%classlist) = &getclasslist($cdom,$cnum,$chome,'0');
        !           350:     
        !           351:     #get list of parts for this problem
        !           352:     my (@parts) = &getpartlist($url);
        !           353: 
        !           354:     $result .= '<form action="/adm/grades" method="post">'."\n".
        !           355:                '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
        !           356:                '<input type="hidden" name="url" value="'.$url.'" />'."\n".
        !           357: 	       '<input type="hidden" name="command" value="viewgrades" />'.
        !           358:                "\n".'<input type="submit" name="submit" value="See Grades" />'.
        !           359:                '<br />'."\n";
1.5       albertel  360: 
1.12    ! harris41  361:     foreach my $student ( sort(@{ $classlist{'allids'} }) ) {
        !           362:         $result .= &setstudentgrade($url,$symb,$ENV{'request.course.id'},
        !           363: 				    $student,@parts);
        !           364:     }
1.5       albertel  365: 
1.12    ! harris41  366:     $result .= '<input type="submit" name="submit" value="See Grades" />'.
        !           367: 	       '</table></form>';
        !           368:     return $result;
1.5       albertel  369: }
                    370: 
1.12    ! harris41  371: # ================================================================= Send header
1.2       albertel  372: sub send_header {
1.12    ! harris41  373:     my ($request) = @_;
        !           374:     $request->print(&Apache::lontexconvert::header());
1.6       albertel  375: #  $request->print("
                    376: #<script>
                    377: #remotewindow=open('','homeworkremote');
                    378: #remotewindow.close();
                    379: #</script>"); 
1.12    ! harris41  380:     $request->print('<body bgcolor="#FFFFFF">');
1.2       albertel  381: }
                    382: 
1.12    ! harris41  383: # ================================================================= Send footer
1.2       albertel  384: sub send_footer {
1.12    ! harris41  385:   my ($request) = @_;
1.2       albertel  386:   $request->print('</body>');
                    387:   $request->print(&Apache::lontexconvert::footer());
                    388: }
                    389: 
1.12    ! harris41  390: # ===================================================================== Handler
1.1       albertel  391: sub handler {
1.12    ! harris41  392:     my $request = $_[0];
        !           393:     if ( $ENV{'user.name'} eq 'albertel' ) {$Apache::lonxml::debug=1;}
        !           394:     else {$Apache::lonxml::debug=0;}
        !           395:     if ($ENV{'browser.mathml'}) {
        !           396: 	$request->content_type('text/xml');
        !           397:     } else {
        !           398: 	$request->content_type('text/html');
        !           399:     }
        !           400:     $request->send_http_header;
        !           401:     return OK if $request->header_only;
        !           402:     my $url = $ENV{'form.url'};
        !           403:     my $symb = $ENV{'form.symb'};
        !           404:     my $command = $ENV{'form.command'};
        !           405:     
        !           406:     &send_header($request);
        !           407:     if ($url eq '' && $symb eq '') {
        !           408: 	$request->print("Non-Contextual Access Unsupported:$command:$url:");
1.2       albertel  409:     } else {
1.12    ! harris41  410: 	$Apache::grades::viewgrades=&Apache::lonnet::allowed('vgr',
        !           411: 				    $ENV{'request.course.id'});
        !           412: 	if ($command eq 'submission') {
        !           413: 	    $request->print(&listStudents($request))
        !           414: 		if ($ENV{'form.student'} eq '');
        !           415: 	    $request->print(&submission($request))
        !           416: 		if ($ENV{'form.student'} ne '');
        !           417: 	} elsif ($command eq 'viewgrades') {
        !           418: 	    $request->print(&viewgrades($request));
        !           419: 	} elsif ($command eq 'editgrades') {
        !           420: 	    $request->print(&editgrades($request));
        !           421: 	} else {
        !           422: 	    $request->print("Unknown action:$command:");
        !           423: 	}
1.2       albertel  424:     }
1.12    ! harris41  425:     &send_footer($request);
        !           426:     return OK;
1.1       albertel  427: }
                    428: 
                    429: 1;
                    430: 
1.12    ! harris41  431: __END__

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