Annotation of loncom/homework/grades.pm, revision 1.7
1.1 albertel 1: # The LON-CAPA Grading handler
1.4 albertel 2: # 2/9,2/13 Guy Albertelli
1.1 albertel 3:
4: package Apache::grades;
5: use strict;
6: use Apache::style;
7: use Apache::lonxml;
8: use Apache::lonnet;
1.3 albertel 9: use Apache::loncommon;
1.1 albertel 10: use Apache::lonhomework;
11: use Apache::Constants qw(:common);
12:
1.2 albertel 13: sub moreinfo {
14: my ($request,$reason) = @_;
15: $request->print("Unable to process request: $reason");
1.5 albertel 16: if ( $Apache::grades::viewgrades eq 'F' ) {
17: $request->print('<form action="/adm/grades" method="post">'."\n");
18: $request->print('<input type="hidden" name="url" value="'.$ENV{'form.url'}.'"></input>'."\n");
19: $request->print('<input type="hidden" name="command" value="'.$ENV{'form.command'}.'"></input>'."\n");
20: $request->print("Student:".'<input type="text" name="student" value="'.$ENV{'form.student'}.'"></input>'."<br />\n");
21: $request->print("Domain:".'<input type="text" name="domain" value="'.$ENV{'user.domain'}.'"></input>'."<br />\n");
22: $request->print('<input type="submit" name="submit" value="ReSubmit"></input>'."<br />\n");
23: $request->print('</form>');
24: }
25: return '';
1.2 albertel 26: }
27:
28:
1.7 ! albertel 29: #FIXME - needs to handle multiple matches
1.2 albertel 30: sub finduser {
31: my ($name) = @_;
1.7 ! albertel 32: my $domain = '';
1.5 albertel 33:
34: if ( $Apache::grades::viewgrades eq 'F' ) {
1.7 ! albertel 35: #get classlist
! 36: my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});
! 37: my $chome=$ENV{"course.$ENV{'request.course.id'}.home"};
! 38: #print "Found $cdom:$cnum:$chome<br />";
! 39: my (%classlist) = &getclasslist($cdom,$cnum,$chome,'0');
! 40: foreach my $student ( sort(@{ $classlist{'allids'} }) ) {
! 41: my ($posname,$posdomain) = split(/:/,$student);
! 42: if ($posname =~ $name) { $name=$posname; $domain=$posdomain; last; }
! 43: }
! 44: return ($name,$domain);
1.5 albertel 45: } else {
46: return ($ENV{'user.name'},$ENV{'user.domain'});
47: }
48: }
49:
50: sub getclasslist {
51: my ($coursedomain,$coursenum,$coursehome,$hideexpired) = @_;
52: my $classlist=&Apache::lonnet::reply("dump:$coursedomain:$coursenum:classlist",$coursehome);
53: my %classlist=();
54: my $now = time;
55: foreach my $record (split /&/, $classlist) {
56: my ($name,$value)=split(/=/,&Apache::lonnet::unescape($record));
57: my ($end,$start)=split(/:/,$value);
58: # still a student?
59: if (($hideexpired) && ($end) && ($end < $now)) {
60: print "Skipping:$name:$end:$now<br />\n";
61: next;
62: }
63: push( @{ $classlist{'allids'} }, $name);
64: }
65: return (%classlist);
66: }
67:
68: sub getpartlist {
69: my ($url) = @_;
70: my @parts =();
71: my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));
72: foreach my $key (@metakeys) {
1.6 albertel 73: if ( $key =~ m/stores_([0-9]+)_.*/ ) {
74: push(@parts,$key);
75: }
1.5 albertel 76: }
77: return @parts;
78: }
79:
80: sub viewstudentgrade {
81: my ($url,$symb,$courseid,$student,@parts) = @_;
82: my $result ='';
83:
84: my ($stuname,$domain) = split(/:/,$student);
85:
86: my %record=&Apache::lonnet::restore($symb,$courseid,$domain,$stuname,
87: &Apache::lonnet::homeserver($stuname,$domain));
88:
89: $result.="<tr><td>$stuname</td><td>$domain</td>\n";
90: foreach my $part (@parts) {
91: my ($temp,$part,$type)=split(/_/,$part);
92: #print "resource.$part.$type = ".$record{"resource.$part.$type"}." <br />\n";
93: if ($type eq 'awarded') {
94: my $score=$record{"resource.$part.$type"};
1.6 albertel 95: $result.="<td><input type=\"text\" name=\"GRADE.$student.$part.$type\" value=\"$score\" size=\"4\" /></td>\n";
1.5 albertel 96: } elsif ($type eq 'tries') {
97: my $score=$record{"resource.$part.$type"};
1.6 albertel 98: $result.="<td><input type=\"text\" name=\"GRADE.$student.$part.$type\" value=\"$score\" size=\"4\" /></td>\n"
1.5 albertel 99: } elsif ($type eq 'solved') {
100: my $score=$record{"resource.$part.$type"};
1.6 albertel 101: $result.="<td><select name=\"GRADE.$student.$part.$type\">\n";
1.5 albertel 102: if ($score =~ /^correct/) {
1.6 albertel 103: $result.="<option selected=\"on\">correct</option>\n<option>incorrect</option>\n<option>excused</option>\n<option>ungraded</option>\n<option>nothing</option>\n";
1.5 albertel 104: } elsif ($score =~ /^incorrect/) {
1.6 albertel 105: $result.="<option>correct</option>\n<option selected=\"on\">incorrect</option>\n<option>excused</option>\n<option>ungraded</option>\n<option>nothing</option>\n";
1.5 albertel 106: } elsif ($score eq '') {
1.6 albertel 107: $result.="<option>correct</option>\n<option>incorrect</option>\n<option>excused</option>\n<option>ungraded</option>\n<option selected=\"on\">nothing</option>\n";
1.5 albertel 108: } elsif ($score =~ /^excused/) {
1.6 albertel 109: $result.="<option>correct</option>\n<option>incorrect</option>\n<option selected=\"on\">excused</option>\n<option>ungraded</option>\n<option>nothing</option>\n";
1.5 albertel 110: } elsif ($score =~ /^ungraded/) {
1.6 albertel 111: $result.="<option>correct</option>\n<option>incorrect</option>\n<option>excused</option>\n<option selected=\"on\">ungraded</option>\n<option>nothing</option>\n";
1.5 albertel 112: }
113: $result.="</select></td>\n";
114: }
115: }
116: $result.='</tr>';
117: return $result;
118: }
1.6 albertel 119: #FIXME need to look at the meatdata <stores> spec on what type of data to accept and provide an
120: #interface based on that, also do that to above function.
1.5 albertel 121: sub setstudentgrade {
122: my ($url,$symb,$courseid,$student,@parts) = @_;
123:
1.6 albertel 124: my $result ='';
1.5 albertel 125:
126: my ($stuname,$domain) = split(/:/,$student);
127:
128: my %record=&Apache::lonnet::restore($symb,$courseid,$domain,$stuname,
129: &Apache::lonnet::homeserver($stuname,$domain));
130: my %newrecord;
1.6 albertel 131:
1.5 albertel 132: foreach my $part (@parts) {
133: my ($temp,$part,$type)=split(/_/,$part);
134: my $oldscore=$record{"resource.$part.$type"};
1.6 albertel 135: my $newscore=$ENV{"form.GRADE.$student.$part.$type"};
136: if ($type eq 'solved') {
137: my $update=0;
138: if ($newscore eq 'nothing' ) {
139: if ($oldscore ne '') {
140: $update=1;
141: $newscore = '';
142: }
143: } elsif ($oldscore !~ m/^$newscore/) {
144: $update=1;
145: $result.="Updating $stuname to $newscore<br />\n";
146: if ($newscore eq 'correct') { $newscore = 'correct_by_override'; }
147: if ($newscore eq 'incorrect') { $newscore = 'incorrect_by_override'; }
148: if ($newscore eq 'excused') { $newscore = 'excused'; }
149: if ($newscore eq 'ungraded') { $newscore = 'ungraded_attempted'; }
150: } else {
151: #$result.="$stuname:$part:$type:unchanged $oldscore to $newscore:<br />\n";
152: }
153: if ($update) { $newrecord{"resource.$part.$type"}=$newscore; }
1.5 albertel 154: } else {
1.6 albertel 155: if ($oldscore ne $newscore) {
156: $newrecord{"resource.$part.$type"}=$newscore;
157: $result.="Updating $student"."'s status for $part.$type to $newscore<br />\n";
158: } else {
159: #$result.="$stuname:$part:$type:unchanged $oldscore to $newscore:<br />\n";
160: }
1.5 albertel 161: }
162: }
1.6 albertel 163: if ( scalar(keys(%newrecord)) > 0 ) {
164: $newrecord{"resource.regrader"}="$ENV{'user.name'}:$ENV{'user.domain'}";
165: &Apache::lonnet::cstore(\%newrecord,$symb,$courseid,$domain,$stuname,
166: &Apache::lonnet::homeserver($stuname,$domain));
167: $result.="Stored away ".scalar(keys(%newrecord))." elements.<br />\n";
168: }
1.5 albertel 169: return $result;
1.2 albertel 170: }
171:
172: sub submission {
173: my ($request) = @_;
1.3 albertel 174: my $url=$ENV{'form.url'};
175: $url=~s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
1.2 albertel 176: if ($ENV{'form.student'} eq '') { &moreinfo($request,"Need student login id"); return ''; }
177: my ($uname,$udom) = &finduser($ENV{'form.student'});
178: if ($uname eq '') { &moreinfo($request,"Unable to find student"); return ''; }
1.3 albertel 179: my $symb=&Apache::lonnet::symbread($url);
180: if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
181: my $home=&Apache::lonnet::homeserver($uname,$udom);
182: my $answer=&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,$home,
183: $ENV{'request.course.id'});
184: my $result="<h2> Submission Record </h2> $uname:$udom for $url".$answer;
185: return $result;
1.2 albertel 186: }
187:
1.5 albertel 188: sub viewgrades {
189: my ($request) = @_;
190: my $result='';
191:
192: #get resource reference
193: my $url=$ENV{'form.url'};
194: $url=~s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
195: my $symb=$ENV{'form.symb'};
196: if (!$symb) { $symb=&Apache::lonnet::symbread($url); }
197: if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
198:
199: #get classlist
200: my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});
201: my $chome=$ENV{"course.$ENV{'request.course.id'}.home"};
202: #print "Found $cdom:$cnum:$chome<br />";
203: my (%classlist) = &getclasslist($cdom,$cnum,$chome,'0');
204:
205:
206: #get list of parts for this problem
207: my (@parts) = &getpartlist($url);
208:
209: #start the form
210: $result = '<form action="/adm/grades" method="post">'."\n".
1.6 albertel 211: '<input type="hidden" name="symb" value="'.$symb.'"/>'."\n".
212: '<input type="hidden" name="url" value="'.$url.'"/>'."\n".
1.5 albertel 213: '<input type="hidden" name="command" value="editgrades" />'."\n".
214: '<input type="submit" name="submit" value="Submit Changes" />'."\n".
215: '<table>'."\n".
1.6 albertel 216: '<tr><td>UserId</td><td>Domain</td>'."\n";
217: foreach my $part (@parts) {
218: my $display=&Apache::lonnet::metadata($url,$part.'.display');
219: if (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
220: $result.="<td>$display</td>\n";
221: }
222: $result.="</tr>";
1.5 albertel 223: #get info for each student
224: foreach my $student ( sort(@{ $classlist{'allids'} }) ) {
225: $result.=&viewstudentgrade($url,$symb,$ENV{'request.course.id'},$student,@parts);
226: }
227: $result.='</table><input type="submit" name="submit" value="Submit Changes" /></form>';
228:
229: return $result;
230: }
231:
232: sub editgrades {
233: my ($request) = @_;
234: my $result='';
235:
236: my $symb=$ENV{'form.symb'};
237: if ($symb eq '') { $request->print("Unable to handle ambiguous references:$symb:$ENV{'form.url'}"); return ''; }
238: my $url=$ENV{'form.url'};
239: #get classlist
240: my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});
241: my $chome=$ENV{"course.$ENV{'request.course.id'}.home"};
242: #print "Found $cdom:$cnum:$chome<br />";
243: my (%classlist) = &getclasslist($cdom,$cnum,$chome,'0');
244:
245: #get list of parts for this problem
246: my (@parts) = &getpartlist($url);
247:
248: $result.='<form action="/adm/grades" method="post">'."\n".
249: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
250: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
251: '<input type="hidden" name="command" value="viewgrades" />'."\n".
1.6 albertel 252: '<input type="submit" name="submit" value="See Grades" /> <br />'."\n";
1.5 albertel 253:
254: foreach my $student ( sort(@{ $classlist{'allids'} }) ) {
255: $result.=&setstudentgrade($url,$symb,$ENV{'request.course.id'},$student,@parts);
256: }
257:
258: $result.='<input type="submit" name="submit" value="See Grades" /></table></form>';
259: return $result;
260: }
261:
1.2 albertel 262: sub send_header {
263: my ($request)= @_;
264: $request->print(&Apache::lontexconvert::header());
1.6 albertel 265: # $request->print("
266: #<script>
267: #remotewindow=open('','homeworkremote');
268: #remotewindow.close();
269: #</script>");
1.2 albertel 270: $request->print('<body bgcolor="#FFFFFF">');
271: }
272:
273: sub send_footer {
274: my ($request)= @_;
275: $request->print('</body>');
276: $request->print(&Apache::lontexconvert::footer());
277: }
278:
1.1 albertel 279: sub handler {
280: my $request=$_[0];
281:
282: if ( $ENV{'user.name'} eq 'albertel' ) {$Apache::lonxml::debug=1;} else {$Apache::lonxml::debug=0;}
283:
284: if ($ENV{'browser.mathml'}) {
285: $request->content_type('text/xml');
286: } else {
287: $request->content_type('text/html');
288: }
289: $request->send_http_header;
290: return OK if $request->header_only;
291: my $url=$ENV{'form.url'};
1.5 albertel 292: my $symb=$ENV{'form.symb'};
1.2 albertel 293: my $command=$ENV{'form.command'};
294:
295: &send_header($request);
1.5 albertel 296: if ($url eq '' && $symb eq '') {
1.2 albertel 297: $request->print("Non-Contextual Access Unsupported:$command:$url:");
1.1 albertel 298: } else {
1.5 albertel 299: $Apache::grades::viewgrades=&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'});
1.2 albertel 300: if ($command eq 'submission') {
1.3 albertel 301: $request->print(&submission($request));
1.5 albertel 302: } elsif ($command eq 'viewgrades') {
303: $request->print(&viewgrades($request));
304: } elsif ($command eq 'editgrades') {
305: $request->print(&editgrades($request));
1.2 albertel 306: } else {
307: $request->print("Unknown action:$command:");
308: }
1.1 albertel 309: }
1.2 albertel 310: &send_footer($request);
1.1 albertel 311: return OK;
312: }
313:
314: 1;
315:
316: __END__;
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>