Annotation of loncom/homework/grades.pm, revision 1.31
1.17 albertel 1: # The LearningOnline Network with CAPA
1.13 albertel 2: # The LON-CAPA Grading handler
1.17 albertel 3: #
1.31 ! ng 4: # $Id: grades.pm,v 1.30 2002/06/24 21:05:12 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'};
68: my $cdom=$ENV{"course.$courseid.domain"};
69: my $cnum=$ENV{"course.$courseid.num"};
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.24 albertel 80: my (%classlist) = &getclasslist($cdom,$cnum,'0');
1.23 www 81: foreach my $student ( sort(@{ $classlist{'allids'} }) ) {
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.23 www 90: }
91: return '';
92: }
1.13 albertel 93:
1.30 ng 94: sub receiptInput {
1.13 albertel 95: my ($request) = shift;
1.23 www 96: my $cdom=$ENV{"course.$ENV{'request.course.id'}.domain"};
97: my $cnum=$ENV{"course.$ENV{'request.course.id'}.num"};
98: my $hostver=unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'});
99: $request->print(<<ENDHEADER);
1.28 ng 100: <h2><font color="#339933">Verify a Submission Receipt Issued by this Server</font></h2>
1.23 www 101: <form action="/adm/grades" method="post">
102: <tt>$hostver-<input type="text" name="receipt" size="4"></tt>
103: <input type="submit" name="submit" value="Verify">
104: <input type="hidden" name="command" value="verify">
105: ENDHEADER
106: if ($ENV{'form.url'}) {
107: $request->print(
108: '<input type="hidden" name="url" value="'.$ENV{'form.url'}.'" />');
109: }
110: if ($ENV{'form.symb'}) {
111: $request->print(
112: '<input type="hidden" name="symb" value="'.$ENV{'form.symb'}.'" />');
113: }
1.30 ng 114: # $request->print(<<ENDTABLEST);
115: $request->print('</form>');
116: return '';
117: }
118:
119: sub listStudents {
120: my ($request) = shift;
121: my $cdom=$ENV{"course.$ENV{'request.course.id'}.domain"};
122: my $cnum=$ENV{"course.$ENV{'request.course.id'}.num"};
123:
1.23 www 124: $request->print(<<ENDTABLEST);
1.28 ng 125: <h2><font color="#339933">Show Student Submissions on Assessment</font></h2>
126:
1.29 albertel 127: <table border="0"><tr><td bgcolor="#000000">
128: <table border="0">
1.30 ng 129: <tr bgcolor="#e6ffff"><td colspan="6"><b>Resource: </b> $ENV{'form.url'}</td></tr>
130: <tr bgcolor="#e6ffff"><td><b>Username</b></td><td><b>Name</b></td><td><b>Domain</b></td>
131: <td><b>View Problem</b></td><td><b>Submissions</b></td><td><b>Action</b></td></tr>
1.23 www 132: ENDTABLEST
1.24 albertel 133: my (%classlist) = &getclasslist($cdom,$cnum,'0');
1.13 albertel 134: foreach my $student ( sort(@{ $classlist{'allids'} }) ) {
135: my ($sname,$sdom) = split(/:/,$student);
136:
1.25 albertel 137: my %name=&Apache::lonnet::get('environment', ['lastname','generation',
138: 'firstname','middlename'],
139: $sdom,$sname);
140: my $fullname;
141: my ($tmp) = keys(%name);
142: if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
143: $fullname=$name{'lastname'}.$name{'generation'};
1.28 ng 144: if ($fullname =~ /[^\s]+/) { $fullname.=', '; }
1.25 albertel 145: $fullname.=$name{'firstname'}.' '.$name{'middlename'};
146: }
1.13 albertel 147: if ( $Apache::grades::viewgrades eq 'F' ) {
1.30 ng 148: $request->print("\n".'<tr bgcolor=#ffffe6>'."<td>$sname</td><td>$fullname</td><td>$sdom</td>".
1.21 albertel 149: '<form action="/adm/grades" method="post">');
1.16 albertel 150: if ($ENV{'form.url'}) {
1.19 www 151: $request->print(
1.30 ng 152: '<input type="hidden" name="url" value="'.$ENV{'form.url'}.'" />');
1.16 albertel 153: }
154: if ($ENV{'form.symb'}) {
1.19 www 155: $request->print(
1.30 ng 156: '<input type="hidden" name="symb" value="'.$ENV{'form.symb'}.'" />');
1.16 albertel 157: }
1.19 www 158: $request->print(
1.30 ng 159: '<input type="hidden" name="command" value="'.$ENV{'form.command'}.'" />');
1.19 www 160: $request->print(
1.30 ng 161: '<input type="hidden" name="student" value="'.$sname.'" />');
1.19 www 162: $request->print(
1.30 ng 163: '<input type="hidden" name="fullname" value="'.$fullname.'" />');
1.28 ng 164: $request->print(
1.30 ng 165: '<input type="hidden" name="domain" value="'.$sdom.'" />');
166: $request->print('<td>'.
167: '<input type="radio" name="vProb" value="no" checked> no '.
168: '<input type="radio" name="vProb" value="yes"> yes </td>');
169: $request->print('<td>'.
170: '<input type="radio" name="submission" value="last" checked> last '.
171: '<input type="radio" name="submission" value="all"> all </td>');
1.19 www 172: $request->print(
1.30 ng 173: '<td><input type="submit" name="submit" value="View/Grade" />');
1.28 ng 174: $request->print('</td></tr></form>');
175: # $request->print('</form></td></tr>');
1.13 albertel 176: }
177: }
1.28 ng 178: $request->print('</table></td></tr></table>');
1.10 ng 179: }
180:
1.13 albertel 181:
1.7 albertel 182: #FIXME - needs to handle multiple matches
1.2 albertel 183: sub finduser {
1.13 albertel 184: my ($name) = @_;
185: my $domain = '';
186:
187: if ( $Apache::grades::viewgrades eq 'F' ) {
188: #get classlist
189: my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});
1.24 albertel 190: #print "Found $cdom:$cnum<br />";
191: my (%classlist) = &getclasslist($cdom,$cnum,'0');
1.13 albertel 192: foreach my $student ( sort(@{ $classlist{'allids'} }) ) {
193: my ($posname,$posdomain) = split(/:/,$student);
194: if ($posname =~ $name) { $name=$posname; $domain=$posdomain; last; }
1.7 albertel 195: }
1.13 albertel 196: return ($name,$domain);
197: } else {
198: return ($ENV{'user.name'},$ENV{'user.domain'});
199: }
1.5 albertel 200: }
201:
202: sub getclasslist {
1.24 albertel 203: my ($coursedomain,$coursenum,$hideexpired) = @_;
204: my %classlist=&Apache::lonnet::dump('classlist',$coursedomain,$coursenum);
1.13 albertel 205: my $now = time;
1.24 albertel 206: foreach my $student (keys(%classlist)) {
207: my ($end,$start)=split(/:/,$classlist{$student});
1.13 albertel 208: # still a student?
209: if (($hideexpired) && ($end) && ($end < $now)) {
1.15 albertel 210: #print "Skipping:$name:$end:$now<br />\n";
1.13 albertel 211: next;
212: }
1.15 albertel 213: #print "record=$record<br>";
1.24 albertel 214: push( @{ $classlist{'allids'} }, $student);
1.13 albertel 215: }
216: return (%classlist);
1.5 albertel 217: }
218:
219: sub getpartlist {
1.13 albertel 220: my ($url) = @_;
221: my @parts =();
222: my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));
223: foreach my $key (@metakeys) {
1.30 ng 224: if ( $key =~ m/stores_([0-9]+)_.*/) {
1.13 albertel 225: push(@parts,$key);
1.6 albertel 226: }
1.13 albertel 227: }
228: return @parts;
1.5 albertel 229: }
230:
231: sub viewstudentgrade {
1.13 albertel 232: my ($url,$symb,$courseid,$student,@parts) = @_;
233: my $result ='';
234: my $cellclr = '"#ffffdd"';
1.28 ng 235: my ($username,$domain) = split(/:/,$student);
1.13 albertel 236:
1.28 ng 237: my (@requests) = ('lastname','firstname','middlename','generation');
238: my (%name) = &Apache::lonnet::get('environment',\@requests,$domain,$username);
239: my %record=&Apache::lonnet::restore($symb,$courseid,$domain,$username);
240: my $fullname=$name{'lastname'}.$name{'generation'};
241: if ($fullname =~ /[^\s]+/) { $fullname.=', '; }
242: $fullname.=$name{'firstname'}.' '.$name{'middlename'};
1.13 albertel 243:
1.28 ng 244: $result.="<tr bgcolor=$cellclr><td>$username</td><td>$fullname</td><td align=\"middle\">$domain</td>\n";
1.13 albertel 245: foreach my $part (@parts) {
246: my ($temp,$part,$type)=split(/_/,$part);
1.31 ! ng 247: my $score=$record{"resource.$part.$type"};
! 248: if ($type eq 'awarded' || $type eq 'tries') {
! 249: $result.='<td align="middle"><input type="text" name="GRADE.'.$student.'.'.$part.'.'.$type.
! 250: '" value="'.$score.'" size="4" /></td>'."\n";
1.13 albertel 251: } elsif ($type eq 'solved') {
1.31 ! ng 252: my ($status,$foo)=split(/_/,$score,2);
1.28 ng 253: $result.="<td align=\"middle\"><select name=\"GRADE.$student.$part.$type\">\n";
1.31 ! ng 254: my $optsel = '<option>correct</option><option>incorrect</option><option>excused</option>'.
! 255: '<option>ungraded</option><option>handgraded</option><option>nothing</option>'."\n";
! 256: $status = 'nothing' if ($status eq '');
! 257: $optsel =~ s/<option>$status/<option selected="on">$status/;
! 258: $result.=$optsel;
1.13 albertel 259: $result.="</select></td>\n";
260: }
261: }
1.29 albertel 262: $result.='<td></td></tr>';
1.13 albertel 263: return $result;
1.5 albertel 264: }
1.31 ! ng 265:
! 266: #FIXME need to look at the metadata <stores> spec on what type of data to accept and provide an
1.6 albertel 267: #interface based on that, also do that to above function.
1.5 albertel 268: sub setstudentgrade {
1.13 albertel 269: my ($url,$symb,$courseid,$student,@parts) = @_;
270:
271: my $result ='';
272: my ($stuname,$domain) = split(/:/,$student);
273: my %record=&Apache::lonnet::restore($symb,$courseid,$domain,$stuname);
274: my %newrecord;
275:
276: foreach my $part (@parts) {
277: my ($temp,$part,$type)=split(/_/,$part);
278: my $oldscore=$record{"resource.$part.$type"};
279: my $newscore=$ENV{"form.GRADE.$student.$part.$type"};
280: if ($type eq 'solved') {
281: my $update=0;
282: if ($newscore eq 'nothing' ) {
283: if ($oldscore ne '') {
284: $update=1;
285: $newscore = '';
1.6 albertel 286: }
1.13 albertel 287: } elsif ($oldscore !~ m/^$newscore/) {
288: $update=1;
289: $result.="Updating $stuname to $newscore<br />\n";
290: if ($newscore eq 'correct') { $newscore = 'correct_by_override'; }
291: if ($newscore eq 'incorrect') { $newscore = 'incorrect_by_override'; }
292: if ($newscore eq 'excused') { $newscore = 'excused'; }
293: if ($newscore eq 'ungraded') { $newscore = 'ungraded_attempted'; }
294: } else {
295: #$result.="$stuname:$part:$type:unchanged $oldscore to $newscore:<br />\n";
296: }
297: if ($update) { $newrecord{"resource.$part.$type"}=$newscore; }
298: } else {
299: if ($oldscore ne $newscore) {
300: $newrecord{"resource.$part.$type"}=$newscore;
301: $result.="Updating $student"."'s status for $part.$type to $newscore<br />\n";
302: } else {
303: #$result.="$stuname:$part:$type:unchanged $oldscore to $newscore:<br />\n";
304: }
305: }
306: }
307: if ( scalar(keys(%newrecord)) > 0 ) {
308: $newrecord{"resource.regrader"}="$ENV{'user.name'}:$ENV{'user.domain'}";
309: &Apache::lonnet::cstore(\%newrecord,$symb,$courseid,$domain,$stuname);
310:
311: $result.="Stored away ".scalar(keys(%newrecord))." elements.<br />\n";
312: }
313: return $result;
1.2 albertel 314: }
315:
1.31 ! ng 316: # -- show submissions of a student, option to grade
! 317: #
1.2 albertel 318: sub submission {
1.13 albertel 319: my ($request) = @_;
1.31 ! ng 320: $request->print(<<JAVASCRIPT);
! 321: <script type="text/javascript">
! 322: function changeRadio(title,url) {
! 323: }
! 324: </script>
! 325: JAVASCRIPT
1.13 albertel 326: my $url=$ENV{'form.url'};
327: $url=~s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
328: if ($ENV{'form.student'} eq '') { &moreinfo($request,"Need student login id"); return ''; }
329: my ($uname,$udom) = &finduser($ENV{'form.student'});
330: if ($uname eq '') { &moreinfo($request,"Unable to find student"); return ''; }
1.16 albertel 331: my $symb;
332: if ($ENV{'form.symb'}) {
333: $symb=$ENV{'form.symb'};
334: } else {
335: $symb=&Apache::lonnet::symbread($url);
336: }
1.13 albertel 337: if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
1.31 ! ng 338: #
! 339: # header info
1.28 ng 340: my $result='<h2><font color="#339933">Submission Record</font></h2>';
1.30 ng 341: $result.='<table border="0"><tr><td><b>Username: </b>'.$uname.'</td><td><b>Fullname: </b>'.$ENV{'form.fullname'}.'</td><td><b>Domain: </b>'.$udom.'</td></tr>';
342: $result.='<tr><td colspan=3><b>Resource: </b>'.$url.'</td></tr></table>';
1.31 ! ng 343: #
! 344: # option to display problem
1.30 ng 345: if ($ENV{'form.vProb'} eq 'yes') {
346: my $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom,
1.17 albertel 347: $ENV{'request.course.id'});
1.30 ng 348: my $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom,
349: $ENV{'request.course.id'});
350: $result.='<table border="0"><tr><td bgcolor="#000000">';
351: $result.='<table border="0"><tr><td bgcolor="#e6ffff">';
1.31 ! ng 352: $result.='<b>Student\'s view of the problem</b></td></tr><tr><td bgcolor="#ffffff">'.$rendered.'<br />';
1.30 ng 353: $result.='<b>Correct answer:</b><br />'.$companswer;
354: $result.='</td></tr></table>';
355: $result.='</td></tr></table><br />';
356: }
357: my $last = '';
358: $last = 'last' if ($ENV{'form.submission'} eq 'last');
359: my $answer=&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,
360: $ENV{'request.course.id'},$last);
1.31 ! ng 361: $result.=$answer;
1.18 albertel 362:
1.31 ! ng 363: my $maxpt = &Apache::lonnet::EXT('resource.partid.weight',$symb,$udom,$uname);
! 364: my %record= &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
! 365: my $score = $record{'resource.0.awarded'}*$maxpt;
1.30 ng 366:
1.31 ! ng 367: $result.= '<form action="/adm/grades" method="post" name="SCORE">'."\n".
1.30 ng 368: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
369: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
370: '<input type="hidden" name="command" value="editgrades" />'."\n".
1.31 ! ng 371: '<table border="0"><tr><td><b>Points</b></td><td>';
! 372: my $ctr = 0;
! 373: while ($ctr<=$maxpt) {
! 374: $result.= '<input type="radio" name="radval" '.
! 375: 'onclick="javascript:SCORE.GRADE_'.$ENV{'form.student'}.'.value='.$ctr.'" value="'.$ctr.'" '.
! 376: ($score == $ctr? 'checked':'').' /> '.$ctr."\n";
! 377: $ctr++;
! 378: }
! 379: $result.='</td><td> <b>or</b> </td>';
! 380: $result.='<td><input type="text" name="GRADE_'.$ENV{'form.student'}.'"'.
! 381: ($score ne ''? ' value = "'.$score.'"':'').' size="4" /></td>'."\n";
! 382: $result.='<td>/'.$maxpt.' (max pts)</td><td>';
1.30 ng 383:
1.31 ! ng 384: foreach my $part (&getpartlist($url)) {
! 385: my ($temp,$part,$type)=split(/_/,$part);
! 386: if ($type eq 'solved') {
! 387: my ($status,$foo)=split(/_/,$record{"resource.$part.$type"},2);
! 388: $result.="<select name=\"GRADE.$uname:$udom.$part.$type\">\n";
! 389: my $optsel = '<option>correct</option><option>incorrect</option>'.
! 390: '<option>excused</option><option>ungraded</option>'.
! 391: '<option>handgraded</option><option>nothing</option>'."\n";
! 392: $status = 'nothing' if ($status eq '');
! 393: $optsel =~ s/<option>$status/<option selected="on">$status/;
! 394: $result.=$optsel;
! 395: $result.="</select></td></tr>\n";
! 396: }
! 397: }
! 398: $result.='<tr><td colspan="5">The buttons do not work yet.<input type="submit" name="gradeOpt" value="Save N Next" /> ';
! 399: $result.='<input type="submit" name="gradeOpt" value="Next" /> ';
! 400: $result.='<input type="submit" name="gradeOpt" value="Previous" /> ';
! 401: $result.='</td><tr></table></form>';
1.13 albertel 402: return $result;
1.2 albertel 403: }
404:
1.26 albertel 405: sub get_symb_and_url {
406: my ($request) = @_;
1.13 albertel 407: my $url=$ENV{'form.url'};
408: $url=~s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
409: my $symb=$ENV{'form.symb'};
410: if (!$symb) { $symb=&Apache::lonnet::symbread($url); }
411: if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
1.26 albertel 412: return ($symb,$url);
413: }
1.13 albertel 414:
1.29 albertel 415: sub view_edit_entire_class_form {
416: my ($symb,$url)=@_;
417: my $result.='<form action="/adm/grades" method="post">'."\n".
418: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
419: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
420: '<input type="hidden" name="command" value="viewgrades" />'."\n".
1.30 ng 421: '<input type="submit" name="submit" value="View/Grade Entire Class" />'."\n".
1.29 albertel 422: '</form>'."\n";
423: return $result;
424: }
425:
426: sub show_grading_menu_form {
427: my ($symb,$url)=@_;
428: my $result.='<form action="/adm/grades" method="post">'."\n".
429: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
430: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
431: '<input type="hidden" name="command" value="gradingmenu" />'."\n".
432: '<input type="submit" name="submit" value="Grading Menu" />'."\n".
433: '</form>'."\n";
434: return $result;
435: }
436:
1.26 albertel 437: sub gradingmenu {
438: my ($request) = @_;
439: my ($symb,$url)=&get_symb_and_url($request);
440: if (!$symb) {return '';}
1.28 ng 441:
442: my $result='<h2> <font color="#339933">Select a Grading Method</font></h2><br />';
1.29 albertel 443: $result.='<table width=100% border=0><tr><td bgcolor=#000000>'."\n";
1.28 ng 444: $result.='<table width=100% border=0><tr><td bgcolor=#e6ffff>'."\n";
445: $result.=' <b>Resource :</b> '.$url.'</td></tr>'."\n";
446: $result.='<tr bgcolor=#ffffe6><td>'."\n";
1.29 albertel 447: $result.=&view_edit_entire_class_form($symb,$url);
1.26 albertel 448: $result.='<form action="/adm/grades" method="post">'."\n".
449: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
450: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
451: '<input type="hidden" name="command" value="csvupload" />'."\n".
452: '<input type="submit" name="submit" value="Upload Scores" />'."\n".
1.28 ng 453: '</form>'."\n";
1.26 albertel 454: $result.='<form action="/adm/grades" method="post">'."\n".
455: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
456: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
457: '<input type="hidden" name="command" value="submission" />'."\n".
1.30 ng 458: '<input type="submit" name="submit" value="View/Grade A Student" />'."\n".
459: '</form>'."\n";
460: $result.='<form action="/adm/grades" method="post">'."\n".
461: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
462: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
463: '<input type="hidden" name="command" value="receiptInput" />'."\n".
464: '<input type="submit" name="submit" value="Verify Receipt" />'."\n".
1.28 ng 465: '</form>'."\n";
466: $result.='</td></tr></table>'."\n";
467: $result.='</td></tr></table>'."\n";
1.26 albertel 468: return $result;
469: }
470:
471: sub viewgrades {
472: my ($request) = @_;
473: my $result='';
474:
475: #get resource reference
476: my ($symb,$url)=&get_symb_and_url($request);
477: if (!$symb) {return '';}
1.13 albertel 478: #get classlist
479: my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});
1.24 albertel 480: #print "Found $cdom:$cnum<br />";
481: my (%classlist) = &getclasslist($cdom,$cnum,'0');
1.13 albertel 482: my $headerclr = '"#ccffff"';
483: my $cellclr = '"#ffffcc"';
484:
485: #get list of parts for this problem
1.29 albertel 486: my (@parts) = sort(&getpartlist($url));
1.13 albertel 487:
1.28 ng 488: $request->print ("<h2><font color=\"#339933\">Manual Grading</font></h2>");
1.13 albertel 489:
490: #start the form
491: $result = '<form action="/adm/grades" method="post">'."\n".
1.16 albertel 492: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
493: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
1.13 albertel 494: '<input type="hidden" name="command" value="editgrades" />'."\n".
495: '<input type="submit" name="submit" value="Submit Changes" />'."\n".
1.29 albertel 496: '<table border=0><tr><td bgcolor="#000000">'."\n".
1.13 albertel 497: '<table border=0>'."\n".
1.28 ng 498: '<tr bgcolor='.$headerclr.'><td><b>Username</b></td><td><b>Name</b></td><td><b>Domain</b></td>'."\n";
1.29 albertel 499: foreach my $part (@parts) {
1.13 albertel 500: my $display=&Apache::lonnet::metadata($url,$part.'.display');
501: if (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
1.31 ! ng 502: print "Manual grading:$url:$part:$display:<br>";
1.28 ng 503: $result.='<td><b>'.$display.'</b></td>'."\n";
1.30 ng 504: }
1.28 ng 505: $result.='</tr>';
1.13 albertel 506: #get info for each student
507: foreach my $student ( sort(@{ $classlist{'allids'} }) ) {
1.31 ! ng 508: my $display=&viewstudentgrade($url,$symb,$ENV{'request.course.id'},$student,@parts);
! 509: # print "ID=$ENV{'request.course.id'}:STU=$student:DIS=$display:<br>\n";
1.13 albertel 510: $result.=&viewstudentgrade($url,$symb,$ENV{'request.course.id'},$student,@parts);
511: }
1.31 ! ng 512: $result.='</table></td></tr></table>';
! 513: $result.='<input type="submit" name="submit" value="Submit Changes" /></form>';
1.29 albertel 514: $result.=&show_grading_menu_form($symb,$url);
1.13 albertel 515: return $result;
1.5 albertel 516: }
517:
518: sub editgrades {
1.13 albertel 519: my ($request) = @_;
520: my $result='';
1.5 albertel 521:
1.13 albertel 522: my $symb=$ENV{'form.symb'};
523: if ($symb eq '') { $request->print("Unable to handle ambiguous references:$symb:$ENV{'form.url'}"); return ''; }
524: my $url=$ENV{'form.url'};
525: #get classlist
526: my ($cdom,$cnum) = split(/_/,$ENV{'request.course.id'});
1.24 albertel 527: #print "Found $cdom:$cnum<br />";
528: my (%classlist) = &getclasslist($cdom,$cnum,'0');
1.13 albertel 529:
530: #get list of parts for this problem
531: my (@parts) = &getpartlist($url);
532:
533: $result.='<form action="/adm/grades" method="post">'."\n".
534: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
535: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
536: '<input type="hidden" name="command" value="viewgrades" />'."\n".
537: '<input type="submit" name="submit" value="See Grades" /> <br />'."\n";
538:
539: foreach my $student ( sort(@{ $classlist{'allids'} }) ) {
540: $result.=&setstudentgrade($url,$symb,$ENV{'request.course.id'},$student,@parts);
541: }
1.5 albertel 542:
1.13 albertel 543: $result.='<input type="submit" name="submit" value="See Grades" /></table></form>';
544: return $result;
1.5 albertel 545: }
546:
1.26 albertel 547: sub csvupload {
548: my ($request)= @_;
549: my $result;
550: my ($symb,$url)=&get_symb_and_url($request);
551: if (!$symb) {return '';}
552: my $upfile_select=&Apache::loncommon::upfile_select_html();
553: $result.=<<ENDUPFORM;
554: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
555: <input type="hidden" name="symb" value="$symb" />
556: <input type="hidden" name="url" value="$url" />
557: <input type="hidden" name="command" value="csvuploadmap" />
558: <hr />
559: <h3>Specify a file containing the class grades for resource $url</h3>
560: $upfile_select
561: <p><input type="submit" name="submit" value="Upload Grades" />
562: ENDUPFORM
563: return $result;
564: }
565:
1.27 albertel 566: sub csvupload_javascript_reverse_associate {
567: return(<<ENDPICK);
568: function verify(vf) {
569: var foundsomething=0;
570: var founduname=0;
571: var founddomain=0;
572: for (i=0;i<=vf.nfields.value;i++) {
573: tw=eval('vf.f'+i+'.selectedIndex');
574: if (i==0 && tw!=0) { founduname=1; }
575: if (i==1 && tw!=0) { founddomain=1; }
576: if (i!=0 && i!=1 && tw!=0) { foundsomething=1; }
577: }
578: if (founduname==0 || founddomain==0) {
579: alert('You need to specify at both the username and domain');
580: return;
581: }
582: if (foundsomething==0) {
583: alert('You need to specify at least one grading field');
584: return;
585: }
586: vf.submit();
587: }
588: function flip(vf,tf) {
589: var nw=eval('vf.f'+tf+'.selectedIndex');
590: var i;
591: for (i=0;i<=vf.nfields.value;i++) {
592: //can not pick the same destination field for both name and domain
593: if (((i ==0)||(i ==1)) &&
594: ((tf==0)||(tf==1)) &&
595: (i!=tf) &&
596: (eval('vf.f'+i+'.selectedIndex')==nw)) {
597: eval('vf.f'+i+'.selectedIndex=0;')
598: }
599: }
600: }
601: ENDPICK
602: }
603:
604: sub csvupload_javascript_forward_associate {
605: return(<<ENDPICK);
606: function verify(vf) {
607: var foundsomething=0;
608: var founduname=0;
609: var founddomain=0;
610: for (i=0;i<=vf.nfields.value;i++) {
611: tw=eval('vf.f'+i+'.selectedIndex');
612: if (tw==1) { founduname=1; }
613: if (tw==2) { founddomain=1; }
614: if (tw>2) { foundsomething=1; }
615: }
616: if (founduname==0 || founddomain==0) {
617: alert('You need to specify at both the username and domain');
618: return;
619: }
620: if (foundsomething==0) {
621: alert('You need to specify at least one grading field');
622: return;
623: }
624: vf.submit();
625: }
626: function flip(vf,tf) {
627: var nw=eval('vf.f'+tf+'.selectedIndex');
628: var i;
629: //can not pick the same destination field twice
630: for (i=0;i<=vf.nfields.value;i++) {
631: if ((i!=tf) && (eval('vf.f'+i+'.selectedIndex')==nw)) {
632: eval('vf.f'+i+'.selectedIndex=0;')
633: }
634: }
635: }
636: ENDPICK
637: }
638:
1.26 albertel 639: sub csvuploadmap_header {
640: my ($request,$symb,$url,$datatoken,$distotal)= @_;
641: my $result;
642: my $javascript;
643: if ($ENV{'form.upfile_associate'} eq 'reverse') {
1.27 albertel 644: $javascript=&csvupload_javascript_reverse_associate();
1.26 albertel 645: } else {
1.27 albertel 646: $javascript=&csvupload_javascript_forward_associate();
1.26 albertel 647: }
648: $request->print(<<ENDPICK);
649: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
650: <h3>Uploading Class Grades for resource $url</h3>
651: <hr>
652: <h3>Identify fields</h3>
653: Total number of records found in file: $distotal <hr />
654: Enter as many fields as you can. The system will inform you and bring you back
655: to this page if the data selected is insufficient to run your class.<hr />
656: <input type="button" value="Reverse Association" onClick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" />
657: <input type="hidden" name="associate" value="" />
658: <input type="hidden" name="phase" value="three" />
659: <input type="hidden" name="datatoken" value="$datatoken" />
660: <input type="hidden" name="fileupload" value="$ENV{'form.fileupload'}" />
661: <input type="hidden" name="upfiletype" value="$ENV{'form.upfiletype'}" />
662: <input type="hidden" name="upfile_associate"
663: value="$ENV{'form.upfile_associate'}" />
664: <input type="hidden" name="symb" value="$symb" />
665: <input type="hidden" name="url" value="$url" />
666: <input type="hidden" name="command" value="csvuploadassign" />
667: <hr />
668: <script type="text/javascript" language="Javascript">
669: $javascript
670: </script>
671: ENDPICK
672: return '';
673:
674: }
675:
676: sub csvupload_fields {
677: my ($url) = @_;
678: my (@parts) = &getpartlist($url);
1.27 albertel 679: my @fields=(['username','Student Username'],['domain','Student Domain']);
680: foreach my $part (sort(@parts)) {
1.26 albertel 681: my @datum;
682: my $display=&Apache::lonnet::metadata($url,$part.'.display');
1.27 albertel 683: my $name=$part;
1.26 albertel 684: if (!$display) { $display = $name; }
685: @datum=($name,$display);
686: push(@fields,\@datum);
687: }
688: return (@fields);
689: }
690:
691: sub csvuploadmap_footer {
692: my ($request,$i,$keyfields) =@_;
693: $request->print(<<ENDPICK);
694: </table>
695: <input type="hidden" name="nfields" value="$i" />
696: <input type="hidden" name="keyfields" value="$keyfields" />
697: <input type="button" onClick="javascript:verify(this.form)" value="Assign Grades" /><br />
698: </form>
699: ENDPICK
700: }
701:
702: sub csvuploadmap {
703: my ($request)= @_;
704: my ($symb,$url)=&get_symb_and_url($request);
705: if (!$symb) {return '';}
706: my $datatoken;
707: if (!$ENV{'form.datatoken'}) {
708: $datatoken=&Apache::loncommon::upfile_store($request);
709: } else {
710: $datatoken=$ENV{'form.datatoken'};
711: &Apache::loncommon::load_tmp_file($request);
712: }
713: my @records=&Apache::loncommon::upfile_record_sep();
714: &csvuploadmap_header($request,$symb,$url,$datatoken,$#records+1);
715: my $i;
716: my $keyfields;
717: if (@records) {
718: my @fields=&csvupload_fields($url);
719: if ($ENV{'form.upfile_associate'} eq 'reverse') {
720: &Apache::loncommon::csv_print_samples($request,\@records);
721: $i=&Apache::loncommon::csv_print_select_table($request,\@records,
722: \@fields);
723: foreach (@fields) { $keyfields.=$_->[0].','; }
724: chop($keyfields);
725: } else {
726: unshift(@fields,['none','']);
727: $i=&Apache::loncommon::csv_samples_select_table($request,\@records,
728: \@fields);
729: my %sone=&Apache::loncommon::record_sep($records[0]);
730: $keyfields=join(',',sort(keys(%sone)));
731: }
732: }
733: &csvuploadmap_footer($request,$i,$keyfields);
734: return '';
1.27 albertel 735: }
736:
737: sub csvuploadassign {
738: my ($request)= @_;
739: my ($symb,$url)=&get_symb_and_url($request);
740: if (!$symb) {return '';}
741: &Apache::loncommon::load_tmp_file($request);
742: my @gradedata=&Apache::loncommon::upfile_record_sep();
743: my @keyfields = split(/\,/,$ENV{'form.keyfields'});
744: my %fields=();
745: for (my $i=0; $i<=$ENV{'form.nfields'}; $i++) {
746: if ($ENV{'form.upfile_associate'} eq 'reverse') {
747: if ($ENV{'form.f'.$i} ne 'none') {
748: $fields{$keyfields[$i]}=$ENV{'form.f'.$i};
749: }
750: } else {
751: if ($ENV{'form.f'.$i} ne 'none') {
752: $fields{$ENV{'form.f'.$i}}=$keyfields[$i];
753: }
754: }
755: }
756: $request->print('<h3>Assigning Grades</h3>');
757: my $courseid=$ENV{'request.course.id'};
758: my $cdom=$ENV{"course.$courseid.domain"};
759: my $cnum=$ENV{"course.$courseid.num"};
760: my (%classlist) = &getclasslist($cdom,$cnum,'1');
1.29 albertel 761: my @skipped;
762: my $countdone=0;
763: foreach my $grade (@gradedata) {
764: my %entries=&Apache::loncommon::record_sep($grade);
765: my $username=$entries{$fields{'username'}};
766: my $domain=$entries{$fields{'domain'}};
767: if (!exists($classlist{"$username:$domain"})) {
768: push(@skipped,"$username:$domain");
769: next;
1.27 albertel 770: }
1.29 albertel 771: my %grades;
772: foreach my $dest (keys(%fields)) {
773: if ($dest eq 'username' || $dest eq 'domain') { next; }
774: if ($entries{$fields{$dest}} eq '') { next; }
775: my $store_key=$dest;
776: $store_key=~s/^stores/resource/;
777: $store_key=~s/_/\./g;
778: $grades{$store_key}=$entries{$fields{$dest}};
779: }
780: $grades{"resource.regrader"}="$ENV{'user.name'}:$ENV{'user.domain'}";
781: &Apache::lonnet::cstore(\%grades,$symb,$ENV{'request.course.id'},
782: $domain,$username);
783: $request->print('.');
784: $request->rflush();
785: $countdone++;
786: }
787: $request->print("<br />Stored $countdone students\n");
788: if (@skipped) {
789: $request->print('<br /><font size="+1"><b>Skipped Students</b></font><br />');
790: foreach my $student (@skipped) { $request->print("<br />$student"); }
1.27 albertel 791: }
1.29 albertel 792: $request->print(&view_edit_entire_class_form($symb,$url));
793: $request->print(&show_grading_menu_form($symb,$url));
794: return '';
1.26 albertel 795: }
796:
1.2 albertel 797: sub send_header {
1.13 albertel 798: my ($request)= @_;
799: $request->print(&Apache::lontexconvert::header());
1.6 albertel 800: # $request->print("
801: #<script>
802: #remotewindow=open('','homeworkremote');
803: #remotewindow.close();
804: #</script>");
1.13 albertel 805: $request->print('<body bgcolor="#FFFFFF">');
1.2 albertel 806: }
807:
808: sub send_footer {
1.13 albertel 809: my ($request)= @_;
1.2 albertel 810: $request->print('</body>');
811: $request->print(&Apache::lontexconvert::footer());
812: }
813:
1.1 albertel 814: sub handler {
1.13 albertel 815: my $request=$_[0];
816:
817: if ($ENV{'browser.mathml'}) {
818: $request->content_type('text/xml');
819: } else {
820: $request->content_type('text/html');
821: }
822: $request->send_http_header;
823: return OK if $request->header_only;
1.16 albertel 824: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});
1.13 albertel 825: my $url=$ENV{'form.url'};
826: my $symb=$ENV{'form.symb'};
827: my $command=$ENV{'form.command'};
1.16 albertel 828: if (!$url) {
829: my ($temp1,$temp2);
830: ($temp1,$temp2,$ENV{'form.url'})=split(/___/,$symb);
831: $url = $ENV{'form.url'};
832: }
1.13 albertel 833: &send_header($request);
834: if ($url eq '' && $symb eq '') {
1.14 www 835: if ($ENV{'user.adv'}) {
836: if (($ENV{'form.codeone'}) && ($ENV{'form.codetwo'}) &&
837: ($ENV{'form.codethree'})) {
838: my $token=$ENV{'form.codeone'}.'*'.$ENV{'form.codetwo'}.'*'.
839: $ENV{'form.codethree'};
840: my ($tsymb,$tuname,$tudom,$tcrsid)=
841: &Apache::lonnet::checkin($token);
842: if ($tsymb) {
843: my ($map,$id,$url)=split(/\_\_\_/,$tsymb);
844: if (&Apache::lonnet::allowed('mgr',$tcrsid)) {
845: $request->print(
846: &Apache::lonnet::ssi('/res/'.$url,
847: ('grade_username' => $tuname,
848: 'grade_domain' => $tudom,
849: 'grade_courseid' => $tcrsid,
850: 'grade_symb' => $tsymb)));
851: } else {
852: $request->print('<h1>Not authorized: '.$token.'</h1>');
853: }
854: } else {
855: $request->print('<h1>Not a valid DocID: '.$token.'</h1>');
856: }
857: } else {
858: $request->print(&Apache::lonxml::tokeninputfield());
859: }
860: }
1.13 albertel 861: } else {
1.29 albertel 862: #&Apache::lonhomework::showhashsubset(\%ENV,'^form');
1.13 albertel 863: $Apache::grades::viewgrades=&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'});
864: if ($command eq 'submission') {
1.20 albertel 865: &listStudents($request) if ($ENV{'form.student'} eq '');
1.13 albertel 866: $request->print(&submission($request)) if ($ENV{'form.student'} ne '');
1.26 albertel 867: } elsif ($command eq 'gradingmenu') {
868: $request->print(&gradingmenu($request));
1.13 albertel 869: } elsif ($command eq 'viewgrades') {
870: $request->print(&viewgrades($request));
871: } elsif ($command eq 'editgrades') {
872: $request->print(&editgrades($request));
1.23 www 873: } elsif ($command eq 'verify') {
874: $request->print(&verifyreceipt($request));
1.26 albertel 875: } elsif ($command eq 'csvupload') {
876: $request->print(&csvupload($request));
877: } elsif ($command eq 'csvuploadmap') {
878: $request->print(&csvuploadmap($request));
1.30 ng 879: } elsif ($command eq 'receiptInput') {
880: &receiptInput($request);
1.26 albertel 881: } elsif ($command eq 'csvuploadassign') {
882: if ($ENV{'form.associate'} ne 'Reverse Association') {
883: $request->print(&csvuploadassign($request));
884: } else {
885: if ( $ENV{'form.upfile_associate'} ne 'reverse' ) {
886: $ENV{'form.upfile_associate'} = 'reverse';
887: } else {
888: $ENV{'form.upfile_associate'} = 'forward';
889: }
890: $request->print(&csvuploadmap($request));
891: }
1.12 harris41 892: } else {
1.23 www 893: $request->print("Unknown action: $command:");
1.2 albertel 894: }
1.13 albertel 895: }
896: &send_footer($request);
897: return OK;
1.1 albertel 898: }
899:
900: 1;
901:
1.13 albertel 902: __END__;
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>