Annotation of loncom/homework/grades.pm, revision 1.71
1.17 albertel 1: # The LearningOnline Network with CAPA
1.13 albertel 2: # The LON-CAPA Grading handler
1.17 albertel 3: #
1.71 ! ng 4: # $Id: grades.pm,v 1.70 2003/02/28 21:06:28 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
1.44 ng 33: # June-August H.K. Ng
1.68 ng 34: # Year 2003
1.71 ! ng 35: # February, March H.K. Ng
1.30 ng 36: #
1.1 albertel 37:
38: package Apache::grades;
39: use strict;
40: use Apache::style;
41: use Apache::lonxml;
42: use Apache::lonnet;
1.3 albertel 43: use Apache::loncommon;
1.68 ng 44: use Apache::lonnavmaps;
1.1 albertel 45: use Apache::lonhomework;
1.55 matthew 46: use Apache::loncoursedata;
1.38 ng 47: use Apache::lonmsg qw(:user_normal_msg);
1.1 albertel 48: use Apache::Constants qw(:common);
49:
1.68 ng 50: # ----- These first few routines are general use routines.----
1.44 ng 51: #
52: # --- Retrieve the parts that matches stores_\d+ from the metadata file.---
53: sub getpartlist {
54: my ($url) = @_;
55: my @parts =();
56: my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys'));
57: foreach my $key (@metakeys) {
1.54 albertel 58: if ( $key =~ m/stores_(\w+)_.*/) {
1.44 ng 59: push(@parts,$key);
1.41 ng 60: }
1.16 albertel 61: }
1.44 ng 62: return @parts;
1.2 albertel 63: }
64:
1.44 ng 65: # --- Get the symbolic name of a problem and the url
66: sub get_symb_and_url {
67: my ($request) = @_;
68: (my $url=$ENV{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
1.41 ng 69: my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));
1.44 ng 70: if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
71: return ($symb,$url);
1.32 ng 72: }
73:
1.44 ng 74: # --- Retrieve the fullname for a user. Return lastname, first middle ---
75: # --- Generation is attached next to the lastname if it exists. ---
1.34 ng 76: sub get_fullname {
1.39 ng 77: my ($uname,$udom) = @_;
1.34 ng 78: my %name=&Apache::lonnet::get('environment', ['lastname','generation',
1.55 matthew 79: 'firstname','middlename'],
80: $udom,$uname);
1.34 ng 81: my $fullname;
82: my ($tmp) = keys(%name);
83: if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
1.55 matthew 84: $fullname = &Apache::loncoursedata::ProcessFullName
85: (@name{qw/lastname generation firstname middlename/});
86: } else {
87: &Apache::lonnet::logthis('grades.pm: no name data for '.$uname.
88: '@'.$udom.':'.$tmp);
1.34 ng 89: }
90: return $fullname;
91: }
92:
1.44 ng 93: #--- Get the partlist and the response type for a given problem. ---
94: #--- Indicate if a response type is coded handgraded or not. ---
1.39 ng 95: sub response_type {
1.41 ng 96: my ($url) = shift;
97: my $allkeys = &Apache::lonnet::metadata($url,'keys');
98: my %seen = ();
99: my (@partlist,%handgrade);
100: foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) {
1.54 albertel 101: if (/^\w+response_\w+.*/) {
1.41 ng 102: my ($responsetype,$part) = split(/_/,$_,2);
103: my ($partid,$respid) = split(/_/,$part);
104: $handgrade{$part} = $responsetype.':'.($allkeys =~ /parameter_$part\_handgrade/ ? 'yes' : 'no');
105: next if ($seen{$partid} > 0);
106: $seen{$partid}++;
107: push @partlist,$partid;
108: }
109: }
110: return \@partlist,\%handgrade;
1.39 ng 111: }
112:
1.44 ng 113: #--- Dumps the class list with usernames,list of sections,
114: #--- section, ids and fullnames for each user.
115: sub getclasslist {
116: my ($getsec,$hideexpired) = @_;
1.56 matthew 117: my $classlist=&Apache::loncoursedata::get_classlist();
1.49 albertel 118: # Bail out if we were unable to get the classlist
1.56 matthew 119: return if (! defined($classlist));
120: #
121: my %sections;
122: my %fullnames;
123: foreach (keys(%$classlist)) {
124: # the following undefs are for 'domain', and 'username' respectively.
125: my (undef,undef,$end,$start,$id,$section,$fullname,$status)=
126: @{$classlist->{$_}};
1.44 ng 127: # still a student?
1.56 matthew 128: if (($hideexpired) && ($status ne 'Active')) {
129: delete ($classlist->{$_});
130: next;
131: }
1.44 ng 132: $section = ($section ne '' ? $section : 'no');
133: if ($getsec eq 'all' || $getsec eq $section) {
1.56 matthew 134: $sections{$section}++;
135: $fullnames{$_}=$fullname;
136: } else {
137: delete($classlist->{$_});
138: }
1.44 ng 139: }
140: my %seen = ();
1.56 matthew 141: my @sections = sort(keys(%sections));
142: return ($classlist,\@sections,\%fullnames);
1.44 ng 143: }
144:
145: #find user domain
146: sub finduser {
147: my ($name) = @_;
148: my $domain = '';
149: if ( $Apache::grades::viewgrades eq 'F' ) {
150: my %classlist=&Apache::lonnet::dump('classlist',
151: $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
152: $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
153: my (@fields) = grep /^$name:/, keys %classlist;
154: ($name, $domain) = split(/:/,$fields[0]);
155: return ($name,$domain);
156: } else {
157: return ($ENV{'user.name'},$ENV{'user.domain'});
158: }
159: }
160:
161: #--- Prompts a user to enter a username.
162: sub moreinfo {
163: my ($request,$reason) = @_;
164: $request->print("Unable to process request: $reason");
165: if ( $Apache::grades::viewgrades eq 'F' ) {
166: $request->print('<form action="/adm/grades" method="post">'."\n");
167: if ($ENV{'form.url'}) {
168: $request->print('<input type="hidden" name="url" value="'.$ENV{'form.url'}.'" />'."\n");
169: }
170: if ($ENV{'form.symb'}) {
171: $request->print('<input type="hidden" name="symb" value="'.$ENV{'form.symb'}.'" />'."\n");
172: }
173: $request->print('<input type="hidden" name="command" value="'.$ENV{'form.command'}.'" />'."\n");
174: $request->print("Student:".'<input type="text" name="student" value="'.$ENV{'form.student'}.'" />'."<br />\n");
175: $request->print("Domain:".'<input type="text" name="domain" value="'.$ENV{'user.domain'}.'" />'."<br />\n");
176: $request->print('<input type="submit" name="submit" value="ReSubmit" />'."<br />\n");
177: $request->print('</form>');
178: }
179: return '';
180: }
181:
182: #--- Retrieve the grade status of a student for all the parts
183: sub student_gradeStatus {
184: my ($url,$symb,$udom,$uname,$partlist) = @_;
185: my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
186: my %partstatus = ();
187: foreach (@$partlist) {
188: my ($status,$foo) = split(/_/,$record{"resource.$_.solved"},2);
189: $status = 'nothing' if ($status eq '');
190: $partstatus{$_} = $status;
191: my $subkey = "resource.$_.submitted_by";
192: $partstatus{$subkey} = $record{$subkey} if ($record{$subkey} ne '');
193: }
194: return %partstatus;
195: }
196:
1.45 ng 197: # hidden form and javascript that calls the form
198: # Use by verifyscript and viewgrades
199: # Shows a student's view of problem and submission
200: sub jscriptNform {
201: my ($url,$symb) = @_;
202: my $jscript='<script type="text/javascript" language="javascript">'."\n".
203: ' function viewOneStudent(user,domain) {'."\n".
204: ' document.onestudent.student.value = user;'."\n".
205: ' document.onestudent.userdom.value = domain;'."\n".
206: ' document.onestudent.submit();'."\n".
207: ' }'."\n".
208: '</script>'."\n";
209: $jscript.= '<form action="/adm/grades" method="post" name="onestudent">'."\n".
210: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
211: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
212: '<input type="hidden" name="command" value="submission" />'."\n".
213: '<input type="hidden" name="student" value="" />'."\n".
214: '<input type="hidden" name="userdom" value="" />'."\n".
215: '</form>'."\n";
216: return $jscript;
217: }
1.39 ng 218:
1.44 ng 219: #------------------ End of general use routines --------------------
220: #-------------------------------------------------------------------
221:
222: #------------------------------------ Receipt Verification Routines
1.45 ng 223: #
1.44 ng 224: #--- Check whether a receipt number is valid.---
225: sub verifyreceipt {
226: my $request = shift;
227:
228: my $courseid = $ENV{'request.course.id'};
229: my $receipt = unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'}).'-'.
230: $ENV{'form.receipt'};
231: $receipt =~ s/[^\-\d]//g;
232: my $url = $ENV{'form.url'};
233: my $symb = $ENV{'form.symb'};
234: unless ($symb) {
235: $symb = &Apache::lonnet::symbread($url);
236: }
237:
1.45 ng 238: my $title.='<h3><font color="#339933">Verifying Submission Receipt '.
239: $receipt.'</h3></font>'."\n".
1.71 ! ng 240: '<font size=+1><b>Problem: </b>'.
! 241: &Apache::lonnet::metadata($ENV{'form.url'},'title').'</font><br><br>'."\n";
1.44 ng 242:
243: my ($string,$contents,$matches) = ('','',0);
1.56 matthew 244: my (undef,undef,$fullname) = &getclasslist('all','0');
245:
1.53 albertel 246: foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
1.44 ng 247: my ($uname,$udom)=split(/\:/);
248: if ($receipt eq
249: &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb)) {
250: $contents.='<tr bgcolor="#ffffe6"><td> '."\n".
251: '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.
252: '\')"; TARGET=_self>'.$$fullname{$_}.'</a> </td>'."\n".
253: '<td> '.$uname.' </td>'.
254: '<td> '.$udom.' </td></tr>'."\n";
255:
256: $matches++;
257: }
258: }
259: if ($matches == 0) {
260: $string = $title.'No match found for the above receipt.';
261: } else {
1.45 ng 262: $string = &jscriptNform($url,$symb).$title.
1.44 ng 263: 'The above receipt matches the following student'.
264: ($matches <= 1 ? '.' : 's.')."\n".
265: '<table border="0"><tr><td bgcolor="#777777">'."\n".
266: '<table border="0"><tr bgcolor="#e6ffff">'."\n".
267: '<td><b> Fullname </b></td>'."\n".
268: '<td><b> Username </b></td>'."\n".
269: '<td><b> Domain </b></td></tr>'."\n".
270: $contents.
271: '</table></td></tr></table>'."\n";
272: }
1.50 albertel 273: return $string.&show_grading_menu_form($symb,$url);
1.44 ng 274: }
275:
1.68 ng 276: #
277: # Pick student and page/sequence for manual grading
278:
279:
1.44 ng 280: #--- This is called by a number of programs.
281: #--- Called from the Grading Menu - View/Grade an individual student
282: #--- Also called directly when one clicks on the subm button
283: # on the problem page.
1.30 ng 284: sub listStudents {
1.41 ng 285: my ($request) = shift;
1.49 albertel 286:
287: my ($symb,$url) = &get_symb_and_url();
288: my $cdom = $ENV{"course.$ENV{'request.course.id'}.domain"};
289: my $cnum = $ENV{"course.$ENV{'request.course.id'}.num"};
290: my $getsec = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};
291: my $submitonly= $ENV{'form.submitonly'} eq '' ? 'all' : $ENV{'form.submitonly'};
292:
293: my $result;
294: my ($partlist,$handgrade) = &response_type($url);
295: for (sort keys(%$handgrade)) {
296: my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});
297: $ENV{'form.handgrade'} = 'yes' if ($handgrade eq 'yes');
298: $result.='<tr><td><b>Part </b>'.(split(/_/))[0].'</td>'.
299: '<td><b>Type: </b>'.$responsetype.'</td>'.
300: '<td><b>Handgrade: </b>'.$handgrade.'</font></td></tr>';
301: }
302: $result.='</table>';
303:
1.68 ng 304: my $viewgrade = $ENV{'form.handgrade'} eq 'yes' ? 'View/Grade' : 'View';
1.49 albertel 305:
306: $result='<h3><font color="#339933"> '.
307: $viewgrade.
308: ' Submissions for a Student or a Group of Students</font></h3>'.
309: '<table border="0"><tr><td colspan=3><font size=+1>'.
1.71 ! ng 310: '<b>Problem: </b>'.&Apache::lonnet::metadata($url,'title').'</font></td></tr>'.$result;
1.49 albertel 311:
1.45 ng 312: $request->print(<<LISTJAVASCRIPT);
313: <script type="text/javascript" language="javascript">
314: function checkSelect(checkBox) {
315: var ctr=0;
1.46 ng 316: var sense="";
1.45 ng 317: if (checkBox.length > 1) {
318: for (var i=0; i<checkBox.length; i++) {
319: if (checkBox[i].checked) {
320: ctr++;
321: }
322: }
1.46 ng 323: sense = "a student or group of students";
1.45 ng 324: } else {
325: if (checkBox.checked) {
326: ctr = 1;
327: }
1.46 ng 328: sense = "the student";
1.45 ng 329: }
330: if (ctr == 0) {
1.49 albertel 331: alert("Please select "+sense+" before clicking on the $viewgrade button.");
1.45 ng 332: return false;
333: }
334: document.gradesub.submit();
335: }
336: </script>
337: LISTJAVASCRIPT
338:
1.41 ng 339: $request->print($result);
1.39 ng 340:
1.45 ng 341: my $checkhdgrade = $ENV{'form.handgrade'} eq 'yes' ? 'checked' : '';
342: my $checklastsub = $ENV{'form.handgrade'} eq 'yes' ? '' : 'checked';
1.44 ng 343:
1.45 ng 344: my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'."\n".
1.58 albertel 345: ' <b>View Problem: </b><input type="radio" name="vProb" value="no" /> no '."\n".
346: '<input type="radio" name="vProb" value="yes" checked /> one student '."\n".
347: '<input type="radio" name="vProb" value="all" /> all students <br />'."\n".
1.49 albertel 348: ' <b>Submissions: </b>'."\n";
349: if ($ENV{'form.handgrade'} eq 'yes') {
350: $gradeTable.='<input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> handgrade only'."\n";
351: }
352: $gradeTable.='<input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> last sub only'."\n".
1.45 ng 353: '<input type="radio" name="lastSub" value="last" /> last sub & parts info'."\n".
354: '<input type="radio" name="lastSub" value="all" /> all details'."\n".
355: '<input type="hidden" name="section" value="'.$getsec.'" />'."\n".
356: '<input type="hidden" name="submitonly" value="'.$submitonly.'" />'."\n".
357: '<input type="hidden" name="response" value="'.$ENV{'form.response'}.'" />'."\n".
1.65 albertel 358: '<input type="hidden" name="handgrade" value="'.$ENV{'form.handgrade'}.'" /><br />'."\n".
1.64 albertel 359: '<input type="hidden" name="showgrading" value="'.$ENV{'form.showgrading'}.'" /><br />'."\n".
1.48 albertel 360: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
361: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
1.49 albertel 362: 'To '.lc($viewgrade).' a submission, click on the check box next to the student\'s name. Then '."\n".
363: 'click on the '.$viewgrade.' button. To view the submissions for a group of students, click'."\n".
1.45 ng 364: ' on the check boxes for the group of students.<br />'."\n".
365: '<input type="hidden" name="command" value="processGroup" />'."\n".
366: '<input type="button" '."\n".
367: 'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".
1.49 albertel 368: 'value="'.$viewgrade.'" />'."\n";
1.45 ng 369:
1.56 matthew 370: my (undef,undef,$fullname) = &getclasslist($getsec,'0');
1.41 ng 371:
1.45 ng 372: $gradeTable.='<table border="0"><tr><td bgcolor="#777777">'.
1.41 ng 373: '<table border="0"><tr bgcolor="#e6ffff">'.
1.44 ng 374: '<td><b> Select </b></td><td><b> Fullname </b></td>'.
375: '<td><b> Username </b></td><td><b> Domain </b></td>';
1.41 ng 376: foreach (sort(@$partlist)) {
1.45 ng 377: $gradeTable.='<td><b> Part '.(split(/_/))[0].' Status </b></td>';
1.41 ng 378: }
1.45 ng 379: $gradeTable.='</tr>'."\n";
1.41 ng 380:
1.45 ng 381: my $ctr = 0;
1.53 albertel 382: foreach my $student (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
1.41 ng 383: my ($uname,$udom) = split(/:/,$student);
1.48 albertel 384: my (%status) =&student_gradeStatus($url,$symb,$udom,$uname,$partlist);
1.41 ng 385: my $statusflg = '';
386: foreach (keys(%status)) {
387: $statusflg = 1 if ($status{$_} ne 'nothing');
1.43 ng 388: my ($foo,$partid,$foo1) = split(/\./,$_);
1.41 ng 389: if ($status{'resource.'.$partid.'.submitted_by'} ne '') {
390: $statusflg = '';
1.45 ng 391: $gradeTable.='<input type="hidden" name="'.
392: $student.':submitted_by" value="'.
393: $status{'resource.'.$partid.'.submitted_by'}.'" />';
1.41 ng 394: }
395: }
396: next if ($statusflg eq '' && $submitonly eq 'yes');
1.34 ng 397:
1.45 ng 398: $ctr++;
1.41 ng 399: if ( $Apache::grades::viewgrades eq 'F' ) {
1.45 ng 400: $gradeTable.='<tr bgcolor="#ffffe6">'.
1.41 ng 401: '<td align="center"><input type=checkbox name="stuinfo" value="'.
402: $student.':'.$$fullname{$student}.'"></td>'."\n".
1.44 ng 403: '<td> '.$$fullname{$student}.' </td>'."\n".
1.41 ng 404: '<td> '.$uname.' </td>'."\n".
405: '<td align="middle"> '.$udom.' </td>'."\n";
406:
407: foreach (sort keys(%status)) {
408: next if (/^resource.*?submitted_by$/);
1.45 ng 409: $gradeTable.='<td align="middle"> '.$status{$_}.' </td>'."\n";
1.41 ng 410: }
1.45 ng 411: $gradeTable.='</tr>'."\n";
1.41 ng 412: }
413: }
1.45 ng 414: $gradeTable.='</table></td></tr></table>'.
415: '<input type="button" '.
416: 'onClick="javascript:checkSelect(this.form.stuinfo);" '.
1.50 albertel 417: 'value="'.$viewgrade.'" /></form>'."\n";
1.45 ng 418: if ($ctr == 0) {
419: $gradeTable='<br /> <font color="red">'.
420: 'No submission found for this resource.</font><br />';
1.46 ng 421: } elsif ($ctr == 1) {
422: $gradeTable =~ s/type=checkbox/type=checkbox checked/;
1.45 ng 423: }
1.50 albertel 424: $gradeTable.=&show_grading_menu_form($symb,$url);
1.45 ng 425: $request->print($gradeTable);
1.44 ng 426: return '';
1.10 ng 427: }
428:
1.44 ng 429: #---- Called from the listStudents routine
430: # Displays the submissions for one student or a group of students
1.34 ng 431: sub processGroup {
1.41 ng 432: my ($request) = shift;
433: my $ctr = 0;
434: my @stuchecked = (ref($ENV{'form.stuinfo'}) ? @{$ENV{'form.stuinfo'}}
435: : ($ENV{'form.stuinfo'}));
436: my $total = scalar(@stuchecked)-1;
1.45 ng 437:
1.41 ng 438: foreach (@stuchecked) {
439: my ($uname,$udom,$fullname) = split(/:/);
1.44 ng 440: $ENV{'form.student'} = $uname;
441: $ENV{'form.userdom'} = $udom;
442: $ENV{'form.fullname'} = $fullname;
1.41 ng 443: &submission($request,$ctr,$total);
444: $ctr++;
445: }
446: return '';
1.35 ng 447: }
1.34 ng 448:
1.44 ng 449: #------------------------------------------------------------------------------------
450: #
451: #-------------------------- Next few routines handles grading by student, essentially
452: # handles essay response type problem/part
453: #
454: #--- Javascript to handle the submission page functionality ---
455: sub sub_page_js {
456: my $request = shift;
457: $request->print(<<SUBJAVASCRIPT);
458: <script type="text/javascript" language="javascript">
1.71 ! ng 459: function updateRadio(formname,id,weight) {
! 460: var gradeBox = eval("formname.GD_BOX"+id);
! 461: var radioButton = eval("formname.RADVAL"+id);
! 462: var pts = checkSolved(formname,id) == 'update' ? gradeBox.value : weight;
! 463: gradeBox.value = pts;
! 464: var resetbox = false;
! 465: if (isNaN(pts) || pts < 0) {
! 466: alert("A number equal or greater than 0 is expected. Entered value = "+pts);
! 467: for (var i=0; i<radioButton.length; i++) {
! 468: if (radioButton[i].checked) {
! 469: gradeBox.value = i;
! 470: resetbox = true;
! 471: }
! 472: }
! 473: if (!resetbox) {
! 474: formtextbox.value = "";
! 475: }
! 476: return;
1.44 ng 477: }
1.71 ! ng 478:
! 479: if (pts > weight) {
! 480: var resp = confirm("You entered a value ("+pts+
! 481: ") greater than the weight for the part. Accept?");
! 482: if (resp == false) {
! 483: gradeBox.value = "";
! 484: return;
! 485: }
1.44 ng 486: }
1.13 albertel 487:
1.71 ! ng 488: for (var i=0; i<radioButton.length; i++) {
! 489: radioButton[i].checked=false;
! 490: if (pts == i && pts != "") {
! 491: radioButton[i].checked=true;
! 492: }
! 493: }
! 494: updateSelect(formname,id);
! 495: var stores = eval("formname.stores"+id);
! 496: stores.value = "0";
1.41 ng 497: }
1.5 albertel 498:
1.71 ! ng 499: function writeBox(formname,id,pts,weight) {
! 500: var gradeBox = eval("formname.GD_BOX"+id);
! 501: if (checkSolved(formname,id) == 'update') {
! 502: gradeBox.value = pts;
! 503: } else {
! 504: gradeBox.value = weight;
! 505: var radioButton = eval("formname.RADVAL"+id);
! 506: for (var i=0; i<radioButton.length; i++) {
! 507: radioButton[i].checked=false;
! 508: if (i == weight) {
! 509: radioButton[i].checked=true;
! 510: }
! 511: }
1.41 ng 512: }
1.71 ! ng 513: var stores = eval("formname.stores"+id);
! 514: stores.value = "0";
! 515: updateSelect(formname,id);
! 516: return;
1.41 ng 517: }
1.44 ng 518:
1.71 ! ng 519: function clearRadBox(formname,id) {
! 520: if (checkSolved(formname,id) == 'noupdate') {
! 521: updateSelect(formname,id);
! 522: return;
! 523: }
! 524: gradeSelect = eval("formname.GD_SEL"+id);
! 525: for (var i=0; i<gradeSelect.length; i++) {
! 526: if (gradeSelect[i].selected) {
! 527: var selectx=i;
! 528: }
! 529: }
! 530: var stores = eval("formname.stores"+id);
! 531: if (selectx == stores.value) { return };
! 532: var gradeBox = eval("formname.GD_BOX"+id);
! 533: gradeBox.value = "";
! 534: var radioButton = eval("formname.RADVAL"+id);
! 535: for (var i=0; i<radioButton.length; i++) {
! 536: radioButton[i].checked=false;
! 537: }
! 538: stores.value = selectx;
! 539: }
1.5 albertel 540:
1.71 ! ng 541: function checkSolved(formname,id) {
! 542: if (eval("formname.solved"+id+".value") == "correct_by_student") {
! 543: alert("This problem has been graded correct by the computer. The score cannot be changed.");
! 544: return "noupdate";
1.41 ng 545: }
1.71 ! ng 546: return "update";
1.13 albertel 547: }
1.71 ! ng 548:
! 549: function updateSelect(formname,id) {
! 550: var gradeSelect = eval("formname.GD_SEL"+id);
! 551: gradeSelect[0].selected = true;
! 552: return;
1.41 ng 553: }
1.33 ng 554:
1.71 ! ng 555: //=========== Check that a point is assigned for all the parts (essay grading only) ============
! 556: function checksubmit(formname,val,total,parttot) {
! 557: document.SCORE.gradeOpt.value = val;
! 558: if (val == "Save & Next") {
! 559: for (i=0;i<=total;i++) {
! 560: for (j=0;j<parttot;j++) {
! 561: var partid = eval("formname.partid"+i+"_"+j+".value");
! 562: var selopt = eval("formname.GD_SEL"+i+"_"+partid);
! 563: if (selopt[0].selected) {
! 564: var points = eval("formname.GD_BOX"+i+"_"+partid+".value");
! 565: if (points == "") {
! 566: var name = eval("formname.name"+i+".value");
! 567: var resp = confirm("You did not assign a score for "+name+", part "+partid+". Continue?");
! 568: if (resp == false) {
! 569: eval("formname.GD_BOX"+i+"_"+partid+".focus()");
! 570: return false;
! 571: }
! 572: }
! 573: }
! 574:
! 575: }
! 576: }
! 577:
! 578: }
! 579: formname.submit();
! 580: }
1.44 ng 581:
1.71 ! ng 582: //======= Check that a score is assigned for all the problems (page/sequence grading only) =========
! 583: function checkSubmitPage(formname,total) {
! 584: noscore = new Array(100);
! 585: var ptr = 0;
! 586: for (i=1;i<total;i++) {
! 587: var partid = eval("formname.q_"+i+".value");
! 588: var selopt = eval("formname.GD_SEL"+i+"_"+partid);
! 589: if (selopt[0].selected) {
! 590: var points = eval("formname.GD_BOX"+i+"_"+partid+".value");
! 591: var status = eval("formname.solved"+i+"_"+partid+".value");
! 592: if (points == "" && status != "correct_by_student") {
! 593: noscore[ptr] = i;
! 594: ptr++;
! 595: }
! 596: }
! 597: }
! 598: if (ptr != 0) {
! 599: var sense = ptr == 1 ? ": " : "s: ";
! 600: var prolist = "";
! 601: if (ptr == 1) {
! 602: prolist = noscore[0];
! 603: } else {
! 604: var i = 0;
! 605: while (i < ptr-1) {
! 606: prolist += noscore[i]+", ";
! 607: i++;
! 608: }
! 609: prolist += "and "+noscore[i];
! 610: }
! 611: var resp = confirm("You did not assign any score for the following problem"+sense+prolist+". Continue?");
! 612: if (resp == false) {
! 613: return false;
! 614: }
! 615: }
1.45 ng 616:
1.71 ! ng 617: formname.submit();
! 618: }
! 619: </script>
! 620: SUBJAVASCRIPT
! 621: }
1.45 ng 622:
1.71 ! ng 623: #--- javascript for essay type problem --
! 624: sub sub_page_kw_js {
! 625: my $request = shift;
! 626: $request->print(<<SUBJAVASCRIPT);
! 627: <script type="text/javascript" language="javascript">
1.45 ng 628:
1.44 ng 629: //===================== Show list of keywords ====================
630: function keywords(keyform) {
631: var keywds = keyform.value;
632: var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",keywds);
633: if (nret==null) return;
634: keyform.value = nret;
635:
636: document.SCORE.refresh.value = "on";
637: if (document.SCORE.keywords.value != "") {
638: document.SCORE.submit();
639: }
640: return;
641: }
642:
643: //===================== Script to view submitted by ==================
644: function viewSubmitter(submitter) {
645: document.SCORE.refresh.value = "on";
646: document.SCORE.NCT.value = "1";
647: document.SCORE.unamedom0.value = submitter;
648: document.SCORE.submit();
649: return;
650: }
651:
652: //===================== Script to add keyword(s) ==================
653: function getSel() {
654: if (document.getSelection) txt = document.getSelection();
655: else if (document.selection) txt = document.selection.createRange().text;
656: else return;
657: var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," ");
658: if (cleantxt=="") {
1.46 ng 659: alert("Please select a word or group of words from document and then click this link.");
1.44 ng 660: return;
661: }
662: var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt);
663: if (nret==null) return;
664: var curlist = document.SCORE.keywords.value;
665: document.SCORE.keywords.value = curlist+" "+nret;
666: document.SCORE.refresh.value = "on";
667: if (document.SCORE.keywords.value != "") {
668: document.SCORE.submit();
669: }
670: return;
671: }
672:
673: //====================== Script for composing message ==============
674: function msgCenter(msgform,usrctr,fullname) {
675: var Nmsg = msgform.savemsgN.value;
676: savedMsgHeader(Nmsg,usrctr,fullname);
677: var subject = msgform.msgsub.value;
678: var rtrchk = eval("document.SCORE.includemsg"+usrctr);
679: var msgchk = rtrchk.value;
680: re = /msgsub/;
681: var shwsel = "";
682: if (re.test(msgchk)) { shwsel = "checked" }
683: displaySubject(subject,shwsel);
684: for (var i=1; i<=Nmsg; i++) {
685: var testpt = "savemsg"+i+",";
686: re = /testpt/;
687: shwsel = "";
688: if (re.test(msgchk)) { shwsel = "checked" }
689: var message = eval("document.SCORE.savemsg"+i+".value");
690: displaySavedMsg(i,message,shwsel);
691: }
692: newmsg = eval("document.SCORE.newmsg"+usrctr+".value");
693: shwsel = "";
694: re = /newmsg/;
695: if (re.test(msgchk)) { shwsel = "checked" }
696: newMsg(newmsg,shwsel);
697: msgTail();
698: return;
699: }
700:
701: function savedMsgHeader(Nmsg,usrctr,fullname) {
702: var height = 30*Nmsg+250;
703: var scrollbar = "no";
704: if (height > 600) {
705: height = 600;
706: scrollbar = "yes";
707: }
708: /* if (window.pWin)
709: window.pWin.close(); */
710: pWin = window.open('', 'MessageCenter', 'toolbar=no,location=no,scrollbars='+scrollbar+',screenx=70,screeny=75,width=600,height='+height);
711: pWin.document.write("<html><head>");
712: pWin.document.write("<title>Message Central</title>");
713:
714: pWin.document.write("<script language=javascript>");
715: pWin.document.write("function checkInput() {");
716: pWin.document.write(" opener.document.SCORE.msgsub.value = document.msgcenter.msgsub.value;");
717: pWin.document.write(" var nmsg = opener.document.SCORE.savemsgN.value;");
718: pWin.document.write(" var usrctr = document.msgcenter.usrctr.value;");
719: pWin.document.write(" var newval = eval(\\"opener.document.SCORE.newmsg\\"+usrctr);");
720: pWin.document.write(" newval.value = document.msgcenter.newmsg.value;");
721:
722: pWin.document.write(" var msgchk = \\"\\";");
723: pWin.document.write(" if (document.msgcenter.subchk.checked) {");
724: pWin.document.write(" msgchk = \\"msgsub,\\";");
725: pWin.document.write(" }");
726: pWin.document.write( "for (var i=1; i<=nmsg; i++) {");
727: pWin.document.write(" var opnmsg = eval(\\"opener.document.SCORE.savemsg\\"+i);");
728: pWin.document.write(" var frmmsg = eval(\\"document.msgcenter.msg\\"+i);");
729: pWin.document.write(" opnmsg.value = frmmsg.value;");
730: pWin.document.write(" var chkbox = eval(\\"document.msgcenter.msgn\\"+i);");
731: pWin.document.write(" if (chkbox.checked) {");
732: pWin.document.write(" msgchk += \\"savemsg\\"+i+\\",\\";");
733: pWin.document.write(" }");
734: pWin.document.write(" }");
735: pWin.document.write(" if (document.msgcenter.newmsgchk.checked) {");
736: pWin.document.write(" msgchk += \\"newmsg\\"+usrctr;");
737: pWin.document.write(" }");
738: pWin.document.write(" var includemsg = eval(\\"opener.document.SCORE.includemsg\\"+usrctr);");
739: pWin.document.write(" includemsg.value = msgchk;");
740:
741: pWin.document.write(" self.close()");
742:
743: pWin.document.write("}");
744:
745: pWin.document.write("<");
746: pWin.document.write("/script>");
747:
748: pWin.document.write("</head><body bgcolor=white>");
749:
750: pWin.document.write("<form action=\\"inactive\\" name=\\"msgcenter\\">");
751: pWin.document.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">");
752: pWin.document.write("<font color=\\"green\\" size=+1> Compose Message for \"+fullname+\"</font><br><br>");
753:
754: pWin.document.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
755: pWin.document.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
756: pWin.document.write("<td><b>Type</b></td><td><b>Include</b></td><td><b>Message</td></tr>");
757: }
758: function displaySubject(msg,shwsel) {
759: pWin.document.write("<tr bgcolor=\\"#ffffdd\\">");
760: pWin.document.write("<td>Subject</td>");
761: pWin.document.write("<td align=\\"center\\"><input name=\\"subchk\\" type=\\"checkbox\\"" +shwsel+"></td>");
1.62 albertel 762: pWin.document.write("<td><input name=\\"msgsub\\" type=\\"text\\" value=\\""+msg+"\\"size=\\"60\\" maxlength=\\"80\\"></td></tr>");
1.44 ng 763: }
764:
765: function displaySavedMsg(ctr,msg,shwsel) {
766: pWin.document.write("<tr bgcolor=\\"#ffffdd\\">");
767: pWin.document.write("<td align=\\"center\\">"+ctr+"</td>");
768: pWin.document.write("<td align=\\"center\\"><input name=\\"msgn"+ctr+"\\" type=\\"checkbox\\"" +shwsel+"></td>");
1.62 albertel 769: pWin.document.write("<td><input name=\\"msg"+ctr+"\\" type=\\"text\\" value=\\""+msg+"\\" size=\\"60\\" maxlength=\\"80\\"></td></tr>");
1.44 ng 770: }
771:
772: function newMsg(newmsg,shwsel) {
773: pWin.document.write("<tr bgcolor=\\"#ffffdd\\">");
774: pWin.document.write("<td align=\\"center\\">New</td>");
775: pWin.document.write("<td align=\\"center\\"><input name=\\"newmsgchk\\" type=\\"checkbox\\"" +shwsel+"></td>");
1.62 albertel 776: pWin.document.write("<td><input name=\\"newmsg\\" type=\\"text\\" onchange=\\"javascript:this.form.newmsgchk.checked=true\\" value=\\""+newmsg+"\\" size=\\"60\\" maxlength=\\"80\\"></td></tr>");
1.44 ng 777: }
778:
779: function msgTail() {
780: pWin.document.write("</table>");
781: pWin.document.write("</td></tr></table> ");
782: pWin.document.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:checkInput()\\"> ");
783: pWin.document.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br><br>");
784: pWin.document.write("</form>");
785: pWin.document.write("</body></html>");
786: }
787:
788: //====================== Script for keyword highlight options ==============
789: function kwhighlight() {
790: var kwclr = document.SCORE.kwclr.value;
791: var kwsize = document.SCORE.kwsize.value;
792: var kwstyle = document.SCORE.kwstyle.value;
793: var redsel = "";
794: var grnsel = "";
795: var blusel = "";
796: if (kwclr=="red") {var redsel="checked"};
797: if (kwclr=="green") {var grnsel="checked"};
798: if (kwclr=="blue") {var blusel="checked"};
799: var sznsel = "";
800: var sz1sel = "";
801: var sz2sel = "";
802: if (kwsize=="0") {var sznsel="checked"};
803: if (kwsize=="+1") {var sz1sel="checked"};
804: if (kwsize=="+2") {var sz2sel="checked"};
805: var synsel = "";
806: var syisel = "";
807: var sybsel = "";
808: if (kwstyle=="") {var synsel="checked"};
809: if (kwstyle=="<i>") {var syisel="checked"};
810: if (kwstyle=="<b>") {var sybsel="checked"};
811: highlightCentral();
812: highlightbody('red','red',redsel,'0','normal',sznsel,'','normal',synsel);
813: highlightbody('green','green',grnsel,'+1','+1',sz1sel,'<i>','italic',syisel);
814: highlightbody('blue','blue',blusel,'+2','+2',sz2sel,'<b>','bold',sybsel);
815: highlightend();
816: return;
817: }
818:
819:
820: function highlightCentral() {
821: hwdWin = window.open('', 'KeywordHighlightCentral', 'toolbar=no,location=no,scrollbars=no,width=400,height=300,screenx=100,screeny=75');
822: hwdWin.document.write("<html><head>");
823: hwdWin.document.write("<title>Highlight Central</title>");
824:
825: hwdWin.document.write("<script language=javascript>");
826: hwdWin.document.write("function updateChoice(flag) {");
827: hwdWin.document.write(" opener.document.SCORE.kwclr.value = radioSelection(document.hlCenter.kwdclr);");
828: hwdWin.document.write(" opener.document.SCORE.kwsize.value = radioSelection(document.hlCenter.kwdsize);");
829: hwdWin.document.write(" opener.document.SCORE.kwstyle.value = radioSelection(document.hlCenter.kwdstyle);");
830: hwdWin.document.write(" opener.document.SCORE.refresh.value = \\"on\\";");
831: hwdWin.document.write(" if (opener.document.SCORE.keywords.value!=\\"\\"){");
832: hwdWin.document.write(" opener.document.SCORE.submit();");
833: hwdWin.document.write(" }");
834: hwdWin.document.write(" self.close()");
835: hwdWin.document.write("}");
836:
837: hwdWin.document.write("function radioSelection(radioButton) {");
838: hwdWin.document.write(" var selection=null;");
839: hwdWin.document.write(" for (var i=0; i<radioButton.length; i++) {");
840: hwdWin.document.write(" if (radioButton[i].checked) {");
841: hwdWin.document.write(" selection=radioButton[i].value;");
842: hwdWin.document.write(" return selection;");
843: hwdWin.document.write(" }");
844: hwdWin.document.write(" }");
845: hwdWin.document.write("}");
846:
847: hwdWin.document.write("<");
848: hwdWin.document.write("/script>");
849:
850: hwdWin.document.write("</head><body bgcolor=white>");
851:
852: hwdWin.document.write("<form action=\\"inactive\\" name=\\"hlCenter\\">");
853: hwdWin.document.write("<font color=\\"green\\" size=+1> Keyword Highlight Options</font><br><br>");
854:
855: hwdWin.document.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
856: hwdWin.document.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
857: hwdWin.document.write("<td><b>Text Color</b></td><td><b>Font Size</b></td><td><b>Font Style</td></tr>");
858: }
859:
860: function highlightbody(clrval,clrtxt,clrsel,szval,sztxt,szsel,syval,sytxt,sysel) {
861: hwdWin.document.write("<tr bgcolor=\\"#ffffdd\\">");
862: hwdWin.document.write("<td align=\\"left\\">");
863: hwdWin.document.write("<input name=\\"kwdclr\\" type=\\"radio\\" value=\\""+clrval+"\\" "+clrsel+"> "+clrtxt+"</td>");
864: hwdWin.document.write("<td align=\\"left\\">");
865: hwdWin.document.write("<input name=\\"kwdsize\\" type=\\"radio\\" value=\\""+szval+"\\" "+szsel+"> "+sztxt+"</td>");
866: hwdWin.document.write("<td align=\\"left\\">");
867: hwdWin.document.write("<input name=\\"kwdstyle\\" type=\\"radio\\" value=\\""+syval+"\\" "+sysel+"> "+sytxt+"</td>");
868: hwdWin.document.write("</tr>");
869: }
870:
871: function highlightend() {
872: hwdWin.document.write("</table>");
873: hwdWin.document.write("</td></tr></table> ");
874: // hwdWin.document.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:updateChoice(0)\\"> ");
875: hwdWin.document.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:updateChoice(1)\\"> ");
876: hwdWin.document.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br><br>");
877: hwdWin.document.write("</form>");
878: hwdWin.document.write("</body></html>");
879: }
880:
881: </script>
882: SUBJAVASCRIPT
883: }
884:
1.71 ! ng 885: #--- displays the grading box, used in essay type problem and grading by page/sequence
! 886: sub gradeBox {
! 887: my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_;
! 888:
! 889: my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').
! 890: '/check.gif" height="16" border="0" />';
! 891:
! 892: my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb,$udom,$uname);
! 893: my $wgtmsg = ($wgt > 0 ? '(problem weight)' :
! 894: '<font color="red">problem weight assigned by computer</font>');
! 895: $wgt = ($wgt > 0 ? $wgt : '1');
! 896: my $score = ($$record{'resource.'.$partid.'.awarded'} eq '' ?
! 897: '' : $$record{'resource.'.$partid.'.awarded'}*$wgt);
! 898: my $result='<input type="hidden" name="WGT'.$counter.'_'.$partid.'" value="'.$wgt.'" />'."\n";
! 899:
! 900: $result.='<table border="0"><tr><td>'.
! 901: '<b>Part </b>'.$partid.' <b>Points: </b></td><td>'."\n";
! 902:
! 903: my $ctr = 0;
! 904: $result.='<table border="0"><tr>'."\n"; # display radio buttons in a nice table 10 across
! 905: while ($ctr<=$wgt) {
! 906: $result.= '<td><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '.
! 907: 'onclick="javascript:writeBox(this.form,\''.$counter.'_'.$partid.'\','.
! 908: $ctr.','.$wgt.')" value="'.$ctr.'" '.
! 909: ($score eq $ctr ? 'checked':'').' /> '.$ctr."</td>\n";
! 910: $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
! 911: $ctr++;
! 912: }
! 913: $result.='</tr></table>';
! 914:
! 915: $result.='</td><td> <b>or</b> </td>'."\n";
! 916: $result.='<td><input type="text" name="GD_BOX'.$counter.'_'.$partid.'"'.
! 917: ($score ne ''? ' value = "'.$score.'"':'').' size="4" '.
! 918: 'onChange="javascript:updateRadio(this.form,\''.$counter.'_'.$partid.'\','.
! 919: $wgt.')" /></td>'."\n";
! 920: $result.='<td>/'.$wgt.' '.$wgtmsg.
! 921: ($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? ' '.$checkIcon : '').
! 922: ' </td><td>'."\n";
! 923:
! 924: $result.='<select name="GD_SEL'.$counter.'_'.$partid.'" '.
! 925: 'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n";
! 926: if ($$record{'resource.'.$partid.'.solved'} eq 'excused') {
! 927: $result.='<option> </option>'.
! 928: '<option selected="on">excused</option></select>'."\n";
! 929: } else {
! 930: $result.='<option selected="on"> </option>'.
! 931: '<option>excused</option></select>'."\n";
! 932: }
! 933: $result.="  \n";
! 934: $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n".
! 935: '<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n".
! 936: '<input type="hidden" name="solved'.$counter.'_'.$partid.'" value="'.
! 937: $$record{'resource.'.$partid.'.solved'}.'" />'."\n";
! 938: $result.='</td></tr></table>'."\n";
! 939: return $result;
! 940: }
1.44 ng 941:
1.58 albertel 942: sub show_problem {
1.71 ! ng 943: my ($request,$symb,$uname,$udom,$removeform,$viewon) = @_;
1.58 albertel 944: my $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom,
945: $ENV{'request.course.id'});
946: if ($removeform) {
947: $rendered=~s|<form(.*?)>||g;
948: $rendered=~s|</form>||g;
949: $rendered=~s|name="submit"|name="would_have_been_submit"|g;
950: }
951: my $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom,
952: $ENV{'request.course.id'});
953: if ($removeform) {
954: $companswer=~s|<form(.*?)>||g;
955: $companswer=~s|</form>||g;
956: $rendered=~s|name="submit"|name="would_have_been_submit"|g;
957: }
958: my $result.='<table border="0" width="100%"><tr><td bgcolor="#777777">';
1.71 ! ng 959: $result.='<table border="0" width="100%">';
! 960: $result.='<tr><td bgcolor="#e6ffff"><b> View of the problem - '.$ENV{'form.fullname'}.
! 961: '</b></td></tr>' if ($viewon);
! 962: $result.='<tr><td bgcolor="#ffffff">'.$rendered.'<br />';
1.58 albertel 963: $result.='<b>Correct answer:</b><br />'.$companswer;
964: $result.='</td></tr></table>';
965: $result.='</td></tr></table><br />';
1.71 ! ng 966: return $result;
! 967: # $request->print($result);
1.58 albertel 968: }
969:
1.44 ng 970: # --------------------------- show submissions of a student, option to grade
971: sub submission {
972: my ($request,$counter,$total) = @_;
973:
974: (my $url=$ENV{'form.url'})=~s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
975: # if ($ENV{'form.student'} eq '') { &moreinfo($request,'Need student login id'); return ''; }
976: my ($uname,$udom) = ($ENV{'form.student'},$ENV{'form.userdom'});
977: ($uname,$udom) = &finduser($uname) if $udom eq '';
978: $ENV{'form.fullname'} = &get_fullname ($uname,$udom) if $ENV{'form.fullname'} eq '';
979: # if ($uname eq '') { &moreinfo($request,'Unable to find student'); return ''; }
1.41 ng 980:
981: my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));
982: if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
983: my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : '');
1.58 albertel 984: # $ENV{'form.vProb'} = $ENV{'form.vProb'} ne '' ? $ENV{'form.vProb'} : 'yes';
1.41 ng 985:
986: # header info
987: if ($counter == 0) {
988: &sub_page_js($request);
1.71 ! ng 989: &sub_page_kw_js($request);
1.45 ng 990: $request->print('<h3> <font color="#339933">Submission Record</font></h3>'."\n".
1.71 ! ng 991: '<font size=+1> <b>Problem: </b>'.
! 992: &Apache::lonnet::metadata($url,'title').'</font>'."\n");
1.41 ng 993:
1.44 ng 994: # option to display problem, only once else it cause problems
995: # with the form later since the problem has a form.
1.66 albertel 996: if ($ENV{'form.vProb'} eq 'yes' or !$ENV{'form.vProb'}) {
1.71 ! ng 997: $request->print(&show_problem($request,$symb,$uname,$udom,0,1));
1.41 ng 998: }
999:
1.44 ng 1000: # kwclr is the only variable that is guaranteed to be non blank
1001: # if this subroutine has been called once.
1.41 ng 1002: my %keyhash = ();
1003: if ($ENV{'form.kwclr'} eq '') {
1004: %keyhash = &Apache::lonnet::dump('nohist_handgrade',
1005: $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
1006: $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
1007:
1008: my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'};
1009: $ENV{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : '';
1010: $ENV{'form.kwclr'} = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red';
1011: $ENV{'form.kwsize'} = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';
1012: $ENV{'form.kwstyle'} = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
1013: $ENV{'form.msgsub'} = $keyhash{$symb.'_subject'} ne '' ?
1014: $keyhash{$symb.'_subject'} : &Apache::lonnet::metadata($url,'title');
1015: $ENV{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0';
1.38 ng 1016:
1.41 ng 1017: }
1.44 ng 1018:
1.41 ng 1019: $request->print('<form action="/adm/grades" method="post" name="SCORE">'."\n".
1020: '<input type="hidden" name="command" value="handgrade" />'."\n".
1021: '<input type="hidden" name="refresh" value="off" />'."\n".
1022: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
1023: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
1024: '<input type="hidden" name="showgrading" value="'.$ENV{'form.showgrading'}.'" />'."\n".
1025: '<input type="hidden" name="vProb" value="'.$ENV{'form.vProb'}.'" />'."\n".
1026: '<input type="hidden" name="lastSub" value="'.$ENV{'form.lastSub'}.'" />'."\n".
1027: '<input type="hidden" name="section" value="'.$ENV{'form.section'}.'">'."\n".
1028: '<input type="hidden" name="submitonly" value="'.$ENV{'form.submitonly'}.'">'."\n".
1029: '<input type="hidden" name="response" value="'.$ENV{'form.response'}.'">'."\n".
1030: '<input type="hidden" name="handgrade" value="'.$ENV{'form.handgrade'}.'">'."\n".
1031: '<input type="hidden" name="keywords" value="'.$ENV{'form.keywords'}.'" />'."\n".
1032: '<input type="hidden" name="kwclr" value="'.$ENV{'form.kwclr'}.'" />'."\n".
1033: '<input type="hidden" name="kwsize" value="'.$ENV{'form.kwsize'}.'" />'."\n".
1034: '<input type="hidden" name="kwstyle" value="'.$ENV{'form.kwstyle'}.'" />'."\n".
1035: '<input type="hidden" name="msgsub" value="'.$ENV{'form.msgsub'}.'" />'."\n".
1036: '<input type="hidden" name="savemsgN" value="'.$ENV{'form.savemsgN'}.'" />'."\n".
1037: '<input type="hidden" name="NCT"'.
1038: ' value="'.($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : $total+1).'" />'."\n");
1039:
1040: my ($cts,$prnmsg) = (1,'');
1041: while ($cts <= $ENV{'form.savemsgN'}) {
1042: $prnmsg.='<input type="hidden" name="savemsg'.$cts.'" value="'.
1043: ($keyhash{$symb.'_savemsg'.$cts} eq '' ? $ENV{'form.savemsg'.$cts} : $keyhash{$symb.'_savemsg'.$cts}).
1044: '" />'."\n";
1045: $cts++;
1046: }
1047: $request->print($prnmsg);
1.32 ng 1048:
1.41 ng 1049: if ($ENV{'form.handgrade'} eq 'yes' && $ENV{'form.showgrading'} eq 'yes') {
1050: $request->print(<<KEYWORDS);
1.38 ng 1051: <b>Keyword Options:</b>
1052: <a href="javascript:keywords(document.SCORE.keywords)"; TARGET=_self>List</a>
1053: <a href="#" onMouseDown="javascript:getSel(); return false"
1054: CLASS="page">Paste Selection to List</a>
1055: <a href="javascript:kwhighlight()"; TARGET=_self>Highlight Attribute</a><br /><br />
1056: KEYWORDS
1.41 ng 1057: }
1058: }
1.44 ng 1059:
1.58 albertel 1060: if ($ENV{'form.vProb'} eq 'all') {
1.71 ! ng 1061: $request->print('<br /><br /><br />') if ($counter > 0);
! 1062: $request->print(&show_problem($request,$symb,$uname,$udom,1,1));
1.58 albertel 1063: }
1064:
1.41 ng 1065: my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
1066: my ($partlist,$handgrade) = &response_type($url);
1067:
1.44 ng 1068: # Display student info
1.41 ng 1069: $request->print(($counter == 0 ? '' : '<br />'));
1.45 ng 1070: my $result='<table border="0" width=100%><tr><td bgcolor="#777777">'."\n".
1071: '<table border="0" width=100%><tr bgcolor="#edffff"><td>'."\n";
1.44 ng 1072:
1073: $result.='<b>Fullname: </b>'.$ENV{'form.fullname'}.
1074: '<font color="#999999"> Username: '.$uname.'</font>'.
1.45 ng 1075: '<font color="#999999"> Domain: '.$udom.'</font><br />'."\n";
1076: $result.='<input type="hidden" name="name'.$counter.
1077: '" value="'.$ENV{'form.fullname'}.'" />'."\n";
1.41 ng 1078:
1.44 ng 1079: # If this is handgraded, then check for collaborators
1.45 ng 1080: my @col_fullnames;
1.56 matthew 1081: my ($classlist,$fullname);
1.41 ng 1082: if ($ENV{'form.handgrade'} eq 'yes') {
1083: my @col_list;
1.56 matthew 1084: ($classlist,undef,$fullname) = &getclasslist('all','0');
1.41 ng 1085: for (keys (%$handgrade)) {
1.44 ng 1086: my $ncol = &Apache::lonnet::EXT('resource.'.$_.
1.57 matthew 1087: '.maxcollaborators',
1088: $symb,$udom,$uname);
1089: next if ($ncol <= 0);
1090: s/\_/\./g;
1091: next if ($record{'resource.'.$_.'.collaborators'} eq '');
1092: my (@collaborators) = split(/,?\s+/,
1093: $record{'resource.'.$_.'.collaborators'});
1094: my (@badcollaborators);
1095: if (scalar(@collaborators) != 0) {
1096: $result.='<b>Collaborators: </b>';
1097: foreach my $collaborator (@collaborators) {
1098: my ($co_name,$co_dom) = split /\@|:/,$collaborator;
1099: $co_dom = $udom if (! defined($co_dom));
1100: next if ($co_name eq $uname && $co_dom eq $udom);
1101: # Doing this grep allows 'fuzzy' specification
1102: my @Matches = grep /^$co_name:$co_dom/i,
1103: keys %$classlist;
1104: if (! scalar(@Matches)) {
1105: push @badcollaborators,$collaborator;
1106: next;
1107: }
1108: push @col_list, @Matches;
1109: foreach (@Matches) {
1110: my ($lastname,$givenn) = split(/,/,$$fullname{$_});
1111: push @col_fullnames, $givenn.' '.$lastname;
1112: $result.=$$fullname{$_}.' ';
1113: }
1114: }
1115: $result.='<br />'."\n";
1116: if (scalar(@badcollaborators) > 0) {
1117: $result.='<table border="0"><tr bgcolor="#ffbbbb"><td>';
1118: $result.='This student has submitted ';
1119: if (scalar(@badcollaborators) == 1) {
1120: $result .= 'an invalid collaborator';
1121: } else {
1122: $result .= 'invalid collaborators';
1123: }
1124: $result .= ': '.join(', ',@badcollaborators);
1125:
1126: }
1127: if (scalar(@collaborators > $ncol)) {
1128: $result .= '<table border="0"><tr bgcolor="#ffbbbb"><td>';
1129: $result .= 'This student has sumbitted too many '.
1130: 'collaborators. Maximum is '.$ncol;
1131: $result .= '</td></tr></table>';
1132: }
1133: $result.='<input type="hidden" name="collaborator'.$counter.
1134: '" value="'.(join ':',@col_list).'" />'."\n";
1135: }
1.41 ng 1136: }
1137: }
1.44 ng 1138: $request->print($result."\n");
1.33 ng 1139:
1.44 ng 1140: # print student answer/submission
1141: # Options are (1) Handgaded submission only
1142: # (2) Last submission, includes submission that is not handgraded
1143: # (for multi-response type part)
1144: # (3) Last submission plus the parts info
1145: # (4) The whole record for this student
1.41 ng 1146: if ($ENV{'form.lastSub'} =~ /^(lastonly|hdgrade)$/) {
1147: if ($ENV{'form.'.$uname.':'.$udom.':submitted_by'}) {
1.44 ng 1148: my $submitby=''.
1.41 ng 1149: '<b>Collaborative submission by: </b>'.
1.44 ng 1150: '<a href="javascript:viewSubmitter(\''.
1151: $ENV{'form.'.$uname.':'.$udom.':submitted_by'}.
1.41 ng 1152: '\')"; TARGET=_self>'.
1153: $$fullname{$ENV{'form.'.$uname.':'.$udom.':submitted_by'}}.'</a>';
1154: $request->print($submitby);
1155: } else {
1.44 ng 1156: my ($string,$timestamp)=
1.46 ng 1157: &get_last_submission (%record);
1.71 ! ng 1158: my $lastsubonly=''.
1.44 ng 1159: ($$timestamp eq '' ? '' : '<b>Date Submitted:</b> '.
1160: $$timestamp).'';
1.41 ng 1161: if ($$timestamp eq '') {
1.45 ng 1162: $lastsubonly.='<tr><td bgcolor="#ffffe6">'.$$string[0].'</td></tr>'."\n";
1.41 ng 1163: } else {
1164: for my $part (sort keys(%$handgrade)) {
1165: foreach (@$string) {
1166: my ($partid,$respid) = /^resource\.(\d+)\.(\d+)\.submission/;
1167: if ($part eq ($partid.'_'.$respid)) {
1168: my ($ressub,$subval) = split(/:/,$_,2);
1.44 ng 1169: $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part '.
1170: $partid.'</b> <font color="#999999">( ID '.$respid.
1.67 www 1171: ' )</font> '.
1172: ($record{"resource.$partid.$respid.uploadedurl"}?
1173: '<a href="'.
1174: &Apache::lonnet::tokenwrapper($record{"resource.$partid.$respid.uploadedurl"}).
1175: '"><img src="/adm/lonIcons/unknown.gif" border=0"> File uploaded by student</a> <font color="red" size="1">Like all files provided by users, this file may contain virusses</font><br />':'').
1176: '<b>Answer: </b>'.
1.45 ng 1177: &keywords_highlight($subval).'</td></tr>'."\n"
1.41 ng 1178: if ($ENV{'form.lastSub'} eq 'lastonly' ||
1.44 ng 1179: ($ENV{'form.lastSub'} eq 'hdgrade' &&
1180: $$handgrade{$part} =~ /:yes$/));
1.41 ng 1181: }
1182: }
1183: }
1184: }
1.45 ng 1185: $lastsubonly.='</td></tr>'."\n";
1.41 ng 1186: $request->print($lastsubonly);
1187: }
1188: } else {
1189: $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,
1.44 ng 1190: $ENV{'request.course.id'},
1191: $last,'.submission',
1192: 'Apache::grades::keywords_highlight'));
1.41 ng 1193: }
1194:
1.44 ng 1195: # return if view submission with no grading option
1.41 ng 1196: if ($ENV{'form.showgrading'} eq '') {
1.45 ng 1197: $request->print('</td></tr></table></td></tr></table></form>'."\n");
1.41 ng 1198: return;
1199: }
1.33 ng 1200:
1.44 ng 1201: # Grading options
1.41 ng 1202: $result='<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n".
1203: '<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".
1.45 ng 1204: '<input type="hidden" name="unamedom'.$counter.'" value="'.$uname.':'
1205: .$udom.'" />'."\n";
1206: my ($lastname,$givenn) = split(/,/,$ENV{'form.fullname'});
1207: my $msgfor = $givenn.' '.$lastname;
1208: if (scalar(@col_fullnames) > 0) {
1209: my $lastone = pop @col_fullnames;
1210: $msgfor .= ', '.(join ', ',@col_fullnames).' and '.$lastone.'.';
1211: }
1212: $result.='<tr><td bgcolor="#ffffff">'."\n".
1213: ' <a href="javascript:msgCenter(document.SCORE,'.$counter.
1214: ',\''.$msgfor.'\')"; TARGET=_self>'.
1215: 'Compose Message to student'.(scalar(@col_fullnames) >= 1 ? 's' : '').'</a>'.
1.44 ng 1216: '<br /> (Message will be sent when you click on Save & Next below.)'."\n"
1217: if ($ENV{'form.handgrade'} eq 'yes');
1.41 ng 1218: $request->print($result);
1219:
1220: my %seen = ();
1221: my @partlist;
1222: for (sort keys(%$handgrade)) {
1223: my ($partid,$respid) = split(/_/);
1224: next if ($seen{$partid} > 0);
1225: $seen{$partid}++;
1226: next if ($$handgrade{$_} =~ /:no$/);
1227: push @partlist,$partid;
1228:
1.71 ! ng 1229: $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));
1.39 ng 1230:
1.71 ! ng 1231: # $request->print($result);
1.41 ng 1232: }
1.45 ng 1233: $result='<input type="hidden" name="partlist'.$counter.
1234: '" value="'.(join ":",@partlist).'" />'."\n";
1235: my $ctr = 0;
1236: while ($ctr < scalar(@partlist)) {
1237: $result.='<input type="hidden" name="partid'.$counter.'_'.$ctr.'" value="'.
1238: $partlist[$ctr].'" />'."\n";
1239: $ctr++;
1240: }
1241: $request->print($result.'</td></tr></table></td></tr></table>'."\n");
1.41 ng 1242:
1243: # print end of form
1244: if ($counter == $total) {
1.45 ng 1245: my $endform='<table border="0"><tr><td>'.
1246: '<input type="hidden" name="gradeOpt" value="" />'."\n";
1247: if ($ENV{'form.handgrade'} eq 'yes') {
1248: $endform.='<input type="button" value="Save & Next" '.
1.71 ! ng 1249: 'onClick="javascript:checksubmit(this.form,\'Save & Next\','.
1.45 ng 1250: $total.','.scalar(@partlist).');" TARGET=_self> '."\n";
1251: my $ntstu ='<select name="NTSTU">'.
1252: '<option>1</option><option>2</option>'.
1253: '<option>3</option><option>5</option>'.
1254: '<option>7</option><option>10</option></select>'."\n";
1255: my $nsel = ($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : '1');
1256: $ntstu =~ s/<option>$nsel</<option selected="on">$nsel</;
1257: $endform.=$ntstu.'student(s) ';
1258: } else {
1259: $endform.='<input type="hidden" name="NTSTU" value="1" />'."\n";
1260: }
1261: $endform.='<input type="button" value="Next" '.
1.71 ! ng 1262: 'onClick="javascript:checksubmit(this.form,\'Next\');" TARGET=_self> '."\n".
1.45 ng 1263: '<input type="button" value="Previous" '.
1.71 ! ng 1264: 'onClick="javascript:checksubmit(this.form,\'Previous\');" TARGET=_self> ';
1.45 ng 1265: $endform.='(Next and Previous do not save the scores.)'."\n"
1266: if ($ENV{'form.handgrade'} eq 'yes');
1267: $endform.='</td><tr></table></form>';
1.50 albertel 1268: $endform.=&show_grading_menu_form($symb,$url);
1.41 ng 1269: $request->print($endform);
1270: }
1271: return '';
1.38 ng 1272: }
1273:
1.44 ng 1274: #--- Retrieve the last submission for all the parts
1.38 ng 1275: sub get_last_submission {
1.46 ng 1276: my (%returnhash)=@_;
1277: my (@string,$timestamp);
1278: if ($returnhash{'version'}) {
1279: my %lasthash=();
1280: my ($version);
1281: for ($version=1;$version<=$returnhash{'version'};$version++) {
1282: foreach (sort(split(/\:/,$returnhash{$version.':keys'}))) {
1283: $lasthash{$_}=$returnhash{$version.':'.$_};
1.61 albertel 1284: # if ($returnhash{$version.':'.$_} =~ /(SUBMITTED|DRAFT)$/) {
1.46 ng 1285: $timestamp = scalar(localtime($returnhash{$version.':timestamp'}));
1.61 albertel 1286: # }
1.46 ng 1287: }
1288: }
1289: foreach ((keys %lasthash)) {
1290: if ($_ =~ /\.submission$/) {
1291: my ($partid,$foo) = split(/submission$/,$_);
1292: my $draft = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ?
1293: '<font color="red">Draft Copy</font> ' : '';
1294: push @string, (join(':',$_,$draft.$lasthash{$_}));
1.41 ng 1295: }
1296: }
1297: }
1.46 ng 1298: @string = $string[0] eq '' ? 'Nothing submitted - no attempts.' : @string;
1299: return \@string,\$timestamp;
1.38 ng 1300: }
1.35 ng 1301:
1.44 ng 1302: #--- High light keywords, with style choosen by user.
1.38 ng 1303: sub keywords_highlight {
1.44 ng 1304: my $string = shift;
1305: my $size = $ENV{'form.kwsize'} eq '0' ? '' : 'size='.$ENV{'form.kwsize'};
1306: my $styleon = $ENV{'form.kwstyle'} eq '' ? '' : $ENV{'form.kwstyle'};
1.41 ng 1307: (my $styleoff = $styleon) =~ s/\</\<\//;
1.44 ng 1308: my @keylist = split(/[,\s+]/,$ENV{'form.keywords'});
1.41 ng 1309: foreach (@keylist) {
1.60 albertel 1310: $string =~ s/\b\Q$_\E(\b|\.)/\<font color\=$ENV{'form.kwclr'} $size\>$styleon$_$styleoff\<\/font\>/gi;
1.41 ng 1311: }
1.57 matthew 1312: # This is not really the right place to do this, but I cannot find a
1313: # better one at this time. So here we go - the m in the s:::mg causes
1314: # ^ to match the beginning of a new line. So we replace(???) the beginning
1315: # of the line with <br /> to make things formatted a little better.
1316: $string =~ s:^:<br />:mg;
1.41 ng 1317: return $string;
1.38 ng 1318: }
1.36 ng 1319:
1.44 ng 1320: #--- Called from submission routine
1.38 ng 1321: sub processHandGrade {
1.41 ng 1322: my ($request) = shift;
1323: my $url = $ENV{'form.url'};
1324: my $symb = $ENV{'form.symb'};
1325: my $button = $ENV{'form.gradeOpt'};
1326: my $ngrade = $ENV{'form.NCT'};
1327: my $ntstu = $ENV{'form.NTSTU'};
1328:
1.44 ng 1329: if ($button eq 'Save & Next') {
1330: my $ctr = 0;
1331: while ($ctr < $ngrade) {
1332: my ($uname,$udom) = split(/:/,$ENV{'form.unamedom'.$ctr});
1.45 ng 1333: my ($errorflag) = &saveHandGrade($request,$url,$symb,$uname,$udom,$ctr);
1.71 ! ng 1334: if ($errorflag eq 'no_score') {
! 1335: $ctr++;
! 1336: next;
! 1337: }
1.44 ng 1338:
1339: my $includemsg = $ENV{'form.includemsg'.$ctr};
1340: my ($subject,$message,$msgstatus) = ('','','');
1.62 albertel 1341: if ($includemsg =~ /savemsg|newmsg\Q$ctr\E/) {
1.44 ng 1342: $subject = $ENV{'form.msgsub'} if ($includemsg =~ /^msgsub/);
1343: my (@msgnum) = split(/,/,$includemsg);
1344: foreach (@msgnum) {
1345: $message.=$ENV{'form.'.$_} if ($_ =~ /savemsg|newmsg/ && $_ ne '');
1346: }
1347: $message =~ s/\s+/ /g;
1348: $msgstatus = &Apache::lonmsg::user_normal_msg ($uname,$udom,
1349: $ENV{'form.msgsub'},$message);
1350: }
1351: if ($ENV{'form.collaborator'.$ctr}) {
1352: my (@collaborators) = split(/:/,$ENV{'form.collaborator'.$ctr});
1353: foreach (@collaborators) {
1354: &saveHandGrade($request,$url,$symb,$_,$udom,$ctr,
1355: $ENV{'form.unamedom'.$ctr});
1356: if ($message ne '') {
1357: $msgstatus = &Apache::lonmsg::user_normal_msg ($_,$udom,
1358: $ENV{'form.msgsub'},
1359: $message);
1360: }
1361: }
1362: }
1363: $ctr++;
1364: }
1365: }
1366:
1367: # Keywords sorted in alphabatical order
1.41 ng 1368: my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'};
1369: my %keyhash = ();
1370: $ENV{'form.keywords'} =~ s/,\s{0,}|\s+/ /g;
1371: $ENV{'form.keywords'} =~ s/^\s+|\s+$//;
1.44 ng 1372: my (@keywords) = sort(split(/\s+/,$ENV{'form.keywords'}));
1373: $ENV{'form.keywords'} = join(' ',@keywords);
1.41 ng 1374: $keyhash{$symb.'_keywords'} = $ENV{'form.keywords'};
1375: $keyhash{$symb.'_subject'} = $ENV{'form.msgsub'};
1376: $keyhash{$loginuser.'_kwclr'} = $ENV{'form.kwclr'};
1377: $keyhash{$loginuser.'_kwsize'} = $ENV{'form.kwsize'};
1378: $keyhash{$loginuser.'_kwstyle'} = $ENV{'form.kwstyle'};
1379:
1.44 ng 1380: # message center - Order of message gets changed. Blank line is eliminated.
1381: # New messages are saved in ENV for the next student.
1382: # All messages are saved in nohist_handgrade.db
1.41 ng 1383: my ($ctr,$idx) = (1,1);
1384: while ($ctr <= $ENV{'form.savemsgN'}) {
1385: if ($ENV{'form.savemsg'.$ctr} ne '') {
1386: $keyhash{$symb.'_savemsg'.$idx} = $ENV{'form.savemsg'.$ctr};
1387: $idx++;
1388: }
1389: $ctr++;
1390: }
1391: $ctr = 0;
1392: while ($ctr < $ngrade) {
1393: if ($ENV{'form.newmsg'.$ctr} ne '') {
1394: $keyhash{$symb.'_savemsg'.$idx} = $ENV{'form.newmsg'.$ctr};
1395: $ENV{'form.savemsg'.$idx} = $ENV{'form.newmsg'.$ctr};
1396: $idx++;
1397: }
1398: $ctr++;
1399: }
1400: $ENV{'form.savemsgN'} = --$idx;
1401: $keyhash{$symb.'_savemsgN'} = $ENV{'form.savemsgN'};
1402: my $putresult = &Apache::lonnet::put
1403: ('nohist_handgrade',\%keyhash,
1404: $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
1405: $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
1406:
1.44 ng 1407: # Called by Save & Refresh from Highlight Attribute Window
1.41 ng 1408: if ($ENV{'form.refresh'} eq 'on') {
1409: my $ctr = 0;
1410: $ENV{'form.NTSTU'}=$ngrade;
1411: while ($ctr < $ngrade) {
1.44 ng 1412: ($ENV{'form.student'},$ENV{'form.userdom'}) = split(/:/,$ENV{'form.unamedom'.$ctr});
1.41 ng 1413: &submission($request,$ctr,$ngrade-1);
1414: $ctr++;
1415: }
1416: return '';
1417: }
1.36 ng 1418:
1.44 ng 1419: # Get the next/previous one or group of students
1.41 ng 1420: my $firststu = $ENV{'form.unamedom0'};
1421: my $laststu = $ENV{'form.unamedom'.($ngrade-1)};
1422: $ctr = 2;
1423: while ($laststu eq '') {
1424: $laststu = $ENV{'form.unamedom'.($ngrade-$ctr)};
1425: $ctr++;
1426: $laststu = $firststu if ($ctr > $ngrade);
1427: }
1.44 ng 1428:
1.56 matthew 1429: my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'0');
1.41 ng 1430: my (@parsedlist,@nextlist);
1431: my ($nextflg) = 0;
1.53 albertel 1432: foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
1.41 ng 1433: if ($nextflg == 1 && $button =~ /Next$/) {
1434: push @parsedlist,$_;
1435: }
1436: $nextflg = 1 if ($_ eq $laststu);
1437: if ($button eq 'Previous') {
1438: last if ($_ eq $firststu);
1439: push @parsedlist,$_;
1440: }
1441: }
1442: $ctr = 0;
1443: my ($partlist,$handgrade) = &response_type($ENV{'form.url'});
1444: @parsedlist = reverse @parsedlist if ($button eq 'Previous');
1445: foreach my $student (@parsedlist) {
1446: my ($uname,$udom) = split(/:/,$student);
1447: if ($ENV{'form.submitonly'} eq 'yes') {
1.44 ng 1448: my (%status) = &student_gradeStatus($ENV{'form.url'},$symb,$udom,$uname,$partlist) ;
1.41 ng 1449: my $statusflg = '';
1450: foreach (keys(%status)) {
1451: $statusflg = 1 if ($status{$_} ne 'nothing');
1.44 ng 1452: my ($foo,$partid,$foo1) = split(/\./);
1.41 ng 1453: $statusflg = '' if ($status{'resource.'.$partid.'.submitted_by'} ne '');
1454: }
1455: next if ($statusflg eq '');
1456: }
1457: push @nextlist,$student if ($ctr < $ntstu);
1458: $ctr++;
1459: }
1.36 ng 1460:
1.41 ng 1461: $ctr = 0;
1462: my $total = scalar(@nextlist)-1;
1.39 ng 1463:
1.41 ng 1464: foreach (sort @nextlist) {
1465: my ($uname,$udom,$submitter) = split(/:/);
1.44 ng 1466: $ENV{'form.student'} = $uname;
1467: $ENV{'form.userdom'} = $udom;
1.41 ng 1468: $ENV{'form.fullname'} = $$fullname{$_};
1469: # $ENV{'form.'.$_.':submitted_by'} = $submitter;
1470: # print "submitter=$ENV{'form.'.$_.':submitted_by'}= $submitter:<br>";
1471: &submission($request,$ctr,$total);
1472: $ctr++;
1473: }
1474: if ($total < 0) {
1475: my $the_end = '<h3><font color="red">LON-CAPA User Message</font></h3><br />'."\n";
1476: $the_end.='<b>Message: </b> No more students for this section or class.<br /><br />'."\n";
1477: $the_end.='Click on the button below to return to the grading menu.<br /><br />'."\n";
1478: $the_end.=&show_grading_menu_form ($symb,$url);
1479: $request->print($the_end);
1480: }
1481: return '';
1.38 ng 1482: }
1.36 ng 1483:
1.44 ng 1484: #---- Save the score and award for each student, if changed
1.38 ng 1485: sub saveHandGrade {
1.41 ng 1486: my ($request,$url,$symb,$stuname,$domain,$newflg,$submitter) = @_;
1.44 ng 1487: my %record=&Apache::lonnet::restore($symb,$ENV{'request.course.id'},$domain,$stuname);
1.41 ng 1488: my %newrecord;
1489: foreach (split(/:/,$ENV{'form.partlist'.$newflg})) {
1.43 ng 1490: if ($ENV{'form.GD_SEL'.$newflg.'_'.$_} eq 'excused') {
1.58 albertel 1491: if ($record{'resource.'.$_.'.solved'} ne 'excused') {
1492: $newrecord{'resource.'.$_.'.solved'} = 'excused';
1493: if (exists($record{'resource.'.$_.'.awarded'})) {
1494: $newrecord{'resource.'.$_.'.awarded'} = '';
1495: }
1496: }
1.41 ng 1497: } else {
1.44 ng 1498: my $pts = ($ENV{'form.GD_BOX'.$newflg.'_'.$_} ne '' ?
1499: $ENV{'form.GD_BOX'.$newflg.'_'.$_} :
1500: $ENV{'form.RADVAL'.$newflg.'_'.$_});
1.71 ! ng 1501: return 'no_score' if ($pts eq '' && $ENV{'form.GD_SEL'.$newflg.'_'.$_} eq '');
1.44 ng 1502: my $wgt = $ENV{'form.WGT'.$newflg.'_'.$_} eq '' ? 1 :
1503: $ENV{'form.WGT'.$newflg.'_'.$_};
1.41 ng 1504: my $partial= $pts/$wgt;
1.44 ng 1505: $newrecord{'resource.'.$_.'.awarded'} = $partial
1506: if ($record{'resource.'.$_.'.awarded'} ne $partial);
1507: my $reckey = 'resource.'.$_.'.solved';
1.41 ng 1508: if ($partial == 0) {
1.44 ng 1509: $newrecord{$reckey} = 'incorrect_by_override'
1510: if ($record{$reckey} ne 'incorrect_by_override');
1.41 ng 1511: } else {
1.44 ng 1512: $newrecord{$reckey} = 'correct_by_override'
1513: if ($record{$reckey} ne 'correct_by_override');
1.41 ng 1514: }
1.44 ng 1515: $newrecord{'resource.'.$_.'.submitted_by'} = $submitter
1516: if ($submitter && ($record{'resource.'.$_.'.submitted_by'} ne $submitter));
1.41 ng 1517: }
1518: }
1.44 ng 1519:
1520: if (scalar(keys(%newrecord)) > 0) {
1.41 ng 1521: $newrecord{'resource.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
1.44 ng 1522: &Apache::lonnet::cstore(\%newrecord,$symb,
1523: $ENV{'request.course.id'},$domain,$stuname);
1.41 ng 1524: }
1525: return '';
1.36 ng 1526: }
1.38 ng 1527:
1.44 ng 1528: #--------------------------------------------------------------------------------------
1529: #
1530: #-------------------------- Next few routines handles grading by section or whole class
1531: #
1532: #--- Javascript to handle grading by section or whole class
1.42 ng 1533: sub viewgrades_js {
1534: my ($request) = shift;
1535:
1.41 ng 1536: $request->print(<<VIEWJAVASCRIPT);
1537: <script type="text/javascript" language="javascript">
1.45 ng 1538: function writePoint(partid,weight,point) {
1.42 ng 1539: var radioButton = eval("document.classgrade.RADVAL_"+partid);
1540: var textbox = eval("document.classgrade.TEXTVAL_"+partid);
1541: if (point == "textval") {
1542: var point = eval("document.classgrade.TEXTVAL_"+partid+".value");
1543: if (isNaN(point) || point < 0) {
1544: alert("A number equal or greater than 0 is expected. Entered value = "+point);
1545: var resetbox = false;
1546: for (var i=0; i<radioButton.length; i++) {
1547: if (radioButton[i].checked) {
1548: textbox.value = i;
1549: resetbox = true;
1550: }
1551: }
1552: if (!resetbox) {
1553: textbox.value = "";
1554: }
1555: return;
1556: }
1.44 ng 1557: if (point > weight) {
1558: var resp = confirm("You entered a value ("+point+
1559: ") greater than the weight for the part. Accept?");
1560: if (resp == false) {
1561: textbox.value = "";
1562: return;
1563: }
1564: }
1.42 ng 1565: for (var i=0; i<radioButton.length; i++) {
1566: radioButton[i].checked=false;
1567: if (point == i) {
1568: radioButton[i].checked=true;
1569: }
1570: }
1.41 ng 1571:
1.42 ng 1572: } else {
1573: textbox.value = point;
1574: }
1.41 ng 1575: for (i=0;i<document.classgrade.total.value;i++) {
1.43 ng 1576: var user = eval("document.classgrade.ctr"+i+".value");
1577: var scorename = eval("document.classgrade.GD_"+user+
1.54 albertel 1578: "_"+partid+"_awarded");
1.43 ng 1579: var saveval = eval("document.classgrade.GD_"+user+
1.54 albertel 1580: "_"+partid+"_solved_s.value");
1581: var selname = eval("document.classgrade.GD_"+user+"_"+partid+"_solved");
1.42 ng 1582: if (saveval != "correct") {
1583: scorename.value = point;
1.43 ng 1584: if (selname[0].selected != true) {
1585: selname[0].selected = true;
1586: }
1.42 ng 1587: }
1588: }
1589: var selval = eval("document.classgrade.SELVAL_"+partid);
1590: selval[0].selected = true;
1591: }
1592:
1593: function writeRadText(partid,weight) {
1594: var selval = eval("document.classgrade.SELVAL_"+partid);
1.43 ng 1595: var radioButton = eval("document.classgrade.RADVAL_"+partid);
1596: var textbox = eval("document.classgrade.TEXTVAL_"+partid);
1.42 ng 1597: if (selval[1].selected) {
1598: for (var i=0; i<radioButton.length; i++) {
1599: radioButton[i].checked=false;
1600:
1601: }
1602: textbox.value = "";
1603:
1604: for (i=0;i<document.classgrade.total.value;i++) {
1.43 ng 1605: var user = eval("document.classgrade.ctr"+i+".value");
1606: var scorename = eval("document.classgrade.GD_"+user+
1.54 albertel 1607: "_"+partid+"_awarded");
1.43 ng 1608: var saveval = eval("document.classgrade.GD_"+user+
1.54 albertel 1609: "_"+partid+"_solved_s.value");
1.43 ng 1610: var selname = eval("document.classgrade.GD_"+user+
1.54 albertel 1611: "_"+partid+"_solved");
1.42 ng 1612: if (saveval != "correct") {
1613: scorename.value = "";
1614: selname[1].selected = true;
1615: }
1616: }
1.43 ng 1617: } else {
1618: for (i=0;i<document.classgrade.total.value;i++) {
1619: var user = eval("document.classgrade.ctr"+i+".value");
1620: var scorename = eval("document.classgrade.GD_"+user+
1.54 albertel 1621: "_"+partid+"_awarded");
1.43 ng 1622: var saveval = eval("document.classgrade.GD_"+user+
1.54 albertel 1623: "_"+partid+"_solved_s.value");
1.43 ng 1624: var selname = eval("document.classgrade.GD_"+user+
1.54 albertel 1625: "_"+partid+"_solved");
1.43 ng 1626: if (saveval != "correct") {
1627: scorename.value = eval("document.classgrade.GD_"+user+
1.54 albertel 1628: "_"+partid+"_awarded_s.value");;
1.43 ng 1629: selname[0].selected = true;
1630: }
1631: }
1632: }
1.42 ng 1633: }
1634:
1635: function changeSelect(partid,user) {
1.54 albertel 1636: var selval = eval("document.classgrade.GD_"+user+'_'+partid+"_solved");
1637: var textbox = eval("document.classgrade.GD_"+user+'_'+partid+"_awarded");
1.44 ng 1638: var point = textbox.value;
1639: var weight = eval("document.classgrade.weight_"+partid+".value");
1640:
1641: if (isNaN(point) || point < 0) {
1642: alert("A number equal or greater than 0 is expected. Entered value = "+point);
1643: textbox.value = "";
1644: return;
1645: }
1646: if (point > weight) {
1647: var resp = confirm("You entered a value ("+point+
1648: ") greater than the weight of the part. Accept?");
1649: if (resp == false) {
1650: textbox.value = "";
1651: return;
1652: }
1653: }
1.42 ng 1654: selval[0].selected = true;
1655: }
1656:
1657: function changeOneScore(partid,user) {
1.54 albertel 1658: var selval = eval("document.classgrade.GD_"+user+'_'+partid+"_solved");
1.42 ng 1659: if (selval[1].selected) {
1.54 albertel 1660: var boxval = eval("document.classgrade.GD_"+user+'_'+partid+"_awarded");
1.42 ng 1661: boxval.value = "";
1662: }
1663: }
1664:
1665: function resetEntry(numpart) {
1666: for (ctpart=0;ctpart<numpart;ctpart++) {
1667: var partid = eval("document.classgrade.partid_"+ctpart+".value");
1668: var radioButton = eval("document.classgrade.RADVAL_"+partid);
1669: var textbox = eval("document.classgrade.TEXTVAL_"+partid);
1670: var selval = eval("document.classgrade.SELVAL_"+partid);
1671: for (var i=0; i<radioButton.length; i++) {
1672: radioButton[i].checked=false;
1673:
1674: }
1675: textbox.value = "";
1676: selval[0].selected = true;
1677:
1678: for (i=0;i<document.classgrade.total.value;i++) {
1.43 ng 1679: var user = eval("document.classgrade.ctr"+i+".value");
1680: var resetscore = eval("document.classgrade.GD_"+user+
1.54 albertel 1681: "_"+partid+"_awarded");
1.43 ng 1682: resetscore.value = eval("document.classgrade.GD_"+user+
1.54 albertel 1683: "_"+partid+"_awarded_s.value");
1.42 ng 1684:
1.43 ng 1685: var saveselval = eval("document.classgrade.GD_"+user+
1.54 albertel 1686: "_"+partid+"_solved_s.value");
1.42 ng 1687:
1.54 albertel 1688: var selname = eval("document.classgrade.GD_"+user+"_"+partid+"_solved");
1.42 ng 1689: if (saveselval == "excused") {
1.43 ng 1690: if (selname[1].selected == false) { selname[1].selected = true;}
1.42 ng 1691: } else {
1.43 ng 1692: if (selname[0].selected == false) {selname[0].selected = true};
1.42 ng 1693: }
1694: }
1.41 ng 1695: }
1.42 ng 1696: }
1697:
1.41 ng 1698: </script>
1699: VIEWJAVASCRIPT
1.42 ng 1700: }
1701:
1.44 ng 1702: #--- show scores for a section or whole class w/ option to change/update a score
1.42 ng 1703: sub viewgrades {
1704: my ($request) = shift;
1705: &viewgrades_js($request);
1.41 ng 1706:
1707: my ($symb,$url) = ($ENV{'form.symb'},$ENV{'form.url'});
1.45 ng 1708: my $result='<h3><font color="#339933">Manual Grading</font></h3>';
1.38 ng 1709:
1.71 ! ng 1710: $result.='<font size=+1><b>Problem: </b>'.&Apache::lonnet::metadata($ENV{'form.url'},'title')
! 1711: .'</font>'."\n";
1.41 ng 1712:
1713: #view individual student submission form - called using Javascript viewOneStudent
1.45 ng 1714: $result.=&jscriptNform($url,$symb);
1.41 ng 1715:
1.44 ng 1716: #beginning of class grading form
1.41 ng 1717: $result.= '<form action="/adm/grades" method="post" name="classgrade">'."\n".
1718: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
1719: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
1.38 ng 1720: '<input type="hidden" name="command" value="editgrades" />'."\n".
1.41 ng 1721: '<input type="hidden" name="section" value="'.$ENV{'form.section'}.'" />'."\n";
1.52 albertel 1722: $result.='<h3>Assign Common Grade To ';
1723: if ($ENV{'form.section'} eq 'all') {
1724: $result.='Class </h3>';
1725: } elsif ($ENV{'form.section'} eq 'no') {
1726: $result.='Students in no Section </h3>';
1727: } else {
1728: $result.='Students in Section '.$ENV{'form.section'}.'</h3>';
1729: }
1730: $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".
1731: '<table border=0><tr bgcolor="#ffffdd"><td>';
1732: # $result.='To assign the same score for all the students use the radio buttons or '.
1733: # 'text box below. To assign scores individually fill in the score boxes for '.
1734: # 'each student in the table below. <font color="red">A part that has already '.
1735: # 'been graded does not get changed using the radio buttons or text box. '.
1736: # 'If needed, it has to be changed individually.</font>';
1737: # $result.='</td></tr><tr><td>';
1.44 ng 1738: #radio buttons/text box for assigning points for a section or class.
1739: #handles different parts of a problem
1.42 ng 1740: my ($partlist,$handgrade) = &response_type($ENV{'form.url'});
1741: my %weight = ();
1742: my $ctsparts = 0;
1.41 ng 1743: $result.='<table border="0">';
1.45 ng 1744: my %seen = ();
1.42 ng 1745: for (sort keys(%$handgrade)) {
1.54 albertel 1746: my ($partid,$respid) = split (/_/,$_,2);
1.45 ng 1747: next if $seen{$partid};
1748: $seen{$partid}++;
1.42 ng 1749: my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});
1750: my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);
1751: $weight{$partid} = $wgt eq '' ? '1' : $wgt;
1752:
1.44 ng 1753: $result.='<input type="hidden" name="partid_'.
1754: $ctsparts.'" value="'.$partid.'" />'."\n";
1755: $result.='<input type="hidden" name="weight_'.
1756: $partid.'" value="'.$weight{$partid}.'" />'."\n";
1757: $result.='<tr><td><b>Part '.$partid.' Point:</b> </td><td>';
1.42 ng 1758: $result.='<table border="0"><tr>';
1.41 ng 1759: my $ctr = 0;
1.42 ng 1760: while ($ctr<=$weight{$partid}) { # display radio buttons in a nice table 10 across
1761: $result.= '<td><input type="radio" name="RADVAL_'.$partid.'" '.
1.54 albertel 1762: 'onclick="javascript:writePoint(\''.$partid.'\','.$weight{$partid}.
1.41 ng 1763: ','.$ctr.')" />'.$ctr."</td>\n";
1764: $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
1765: $ctr++;
1766: }
1767: $result.='</tr></table>';
1.44 ng 1768: $result.= '</td><td><b> or </b><input type="text" name="TEXTVAL_'.
1.54 albertel 1769: $partid.'" size="4" '.'onChange="javascript:writePoint(\''.
1770: $partid.'\','.$weight{$partid}.',\'textval\')" /> /'.
1.42 ng 1771: $weight{$partid}.' (problem weight)</td>'."\n";
1772: $result.= '</td><td><select name="SELVAL_'.$partid.'"'.
1.54 albertel 1773: 'onChange="javascript:writeRadText(\''.$partid.'\','.
1.59 albertel 1774: $weight{$partid}.')"> '.
1.42 ng 1775: '<option selected="on"> </option>'.
1776: '<option>excused</option></select></td></tr>'."\n";
1777: $ctsparts++;
1.41 ng 1778: }
1.52 albertel 1779: $result.='</table>'.'</td></tr></table>'.'</td></tr></table>'."\n".
1780: '<input type="hidden" name="totalparts" value="'.$ctsparts.'" />';
1.42 ng 1781: $result.='<input type="button" value="Reset" '.
1.43 ng 1782: 'onClick="javascript:resetEntry('.$ctsparts.');" TARGET=_self> ';
1.45 ng 1783: $result.='<input type="button" value="Submit Changes" '.
1784: 'onClick="javascript:submit();" TARGET=_self />'."\n";
1.41 ng 1785:
1.44 ng 1786: #table listing all the students in a section/class
1787: #header of table
1.52 albertel 1788: $result.= '<h3>Assign Grade to Specific Students in ';
1789: if ($ENV{'form.section'} eq 'all') {
1790: $result.='the Class </h3>';
1791: } elsif ($ENV{'form.section'} eq 'no') {
1792: $result.='no Section </h3>';
1793: } else {
1794: $result.='Section '.$ENV{'form.section'}.'</h3>';
1795: }
1.42 ng 1796: $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".
1.41 ng 1797: '<table border=0><tr bgcolor="#deffff">'.
1.44 ng 1798: '<td><b>Fullname</b></td><td><b>Username</b></td><td><b>Domain</b></td>'."\n";
1.41 ng 1799: my (@parts) = sort(&getpartlist($url));
1800: foreach my $part (@parts) {
1801: my $display=&Apache::lonnet::metadata($url,$part.'.display');
1802: if (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
1803: if ($display =~ /^Partial Credit Factor/) {
1.54 albertel 1804: my ($partid) = &split_part_type($part);
1.53 albertel 1805: $result.='<td><b>Score Part '.$partid.'<br />(weight = '.
1.42 ng 1806: $weight{$partid}.')</b></td>'."\n";
1.41 ng 1807: next;
1808: }
1.53 albertel 1809: $display =~ s|Problem Status|Grade Status<br />|;
1.41 ng 1810: $result.='<td><b>'.$display.'</b></td>'."\n";
1811: }
1812: $result.='</tr>';
1.44 ng 1813:
1.41 ng 1814: #get info for each student
1.44 ng 1815: #list all the students - with points and grade status
1.56 matthew 1816: my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'0');
1.41 ng 1817: my $ctr = 0;
1.53 albertel 1818: foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
1.44 ng 1819: my ($uname,$udom) = split(/:/);
1820: $result.='<input type="hidden" name="ctr'.$ctr.'" value="'.$uname.'" />'."\n";
1.41 ng 1821: $result.=&viewstudentgrade($url,$symb,$ENV{'request.course.id'},
1822: $_,$$fullname{$_},\@parts,\%weight);
1823: $ctr++;
1824: }
1825: $result.='</table></td></tr></table>';
1826: $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n";
1.45 ng 1827: $result.='<input type="button" value="Submit Changes" '.
1828: 'onClick="javascript:submit();" TARGET=_self /></form>'."\n";
1.41 ng 1829: $result.=&show_grading_menu_form($symb,$url);
1830: return $result;
1831: }
1832:
1.44 ng 1833: #--- call by previous routine to display each student
1.41 ng 1834: sub viewstudentgrade {
1835: my ($url,$symb,$courseid,$student,$fullname,$parts,$weight) = @_;
1.44 ng 1836: my ($uname,$udom) = split(/:/,$student);
1837: my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);
1.41 ng 1838: my $result='<tr bgcolor="#ffffdd"><td>'.
1.44 ng 1839: '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.
1840: '\')"; TARGET=_self>'.$fullname.'</a>'.
1841: '</td><td>'.$uname.'</td><td align="middle">'.$udom.'</td>'."\n";
1.63 albertel 1842: foreach my $apart (@$parts) {
1843: my ($part,$type) = &split_part_type($apart);
1.41 ng 1844: my $score=$record{"resource.$part.$type"};
1845: if ($type eq 'awarded') {
1.42 ng 1846: my $pts = $score eq '' ? '' : $score*$$weight{$part};
1847: $result.='<input type="hidden" name="'.
1.54 albertel 1848: 'GD_'.$uname.'_'.$part.'_awarded_s" value="'.$pts.'" />'."\n";
1.42 ng 1849: $result.='<td align="middle"><input type="text" name="'.
1.54 albertel 1850: 'GD_'.$uname.'_'.$part.'_awarded" '.
1851: 'onChange="javascript:changeSelect(\''.$part.'\',\''.$uname.
1.44 ng 1852: '\')" value="'.$pts.'" size="4" /></td>'."\n";
1.41 ng 1853: } elsif ($type eq 'solved') {
1854: my ($status,$foo)=split(/_/,$score,2);
1855: $status = 'nothing' if ($status eq '');
1.54 albertel 1856: $result.='<input type="hidden" name="'.'GD_'.$uname.'_'.
1857: $part.'_solved_s" value="'.$status.'" />'."\n";
1.42 ng 1858: $result.='<td align="middle"><select name="'.
1.54 albertel 1859: 'GD_'.$uname.'_'.$part.'_solved" '.
1860: 'onChange="javascript:changeOneScore(\''.$part.'\',\''.$uname.'\')" >'."\n";
1.42 ng 1861: my $optsel = '<option selected="on"> </option><option>excused</option>'."\n";
1862: $optsel = '<option> </option><option selected="on">excused</option>'."\n"
1863: if ($status eq 'excused');
1.41 ng 1864: $result.=$optsel;
1865: $result.="</select></td>\n";
1.54 albertel 1866: } else {
1867: $result.='<input type="hidden" name="'.
1868: 'GD_'.$uname.'_'.$part.'_'.$type.'_s" value="'.$score.'" />'.
1869: "\n";
1870: $result.='<td align="middle"><input type="text" name="'.
1871: 'GD_'.$uname.'_'.$part.'_'.$type.'" '.
1872: 'value="'.$score.'" size="4" /></td>'."\n";
1.41 ng 1873: }
1874: }
1875: $result.='</tr>';
1876: return $result;
1.38 ng 1877: }
1878:
1.44 ng 1879: #--- change scores for all the students in a section/class
1880: # record does not get update if unchanged
1.38 ng 1881: sub editgrades {
1.41 ng 1882: my ($request) = @_;
1883:
1884: my $symb=$ENV{'form.symb'};
1.43 ng 1885: my $url =$ENV{'form.url'};
1.45 ng 1886: my $title='<h3><font color="#339933">Current Grade Status</font></h3>';
1.71 ! ng 1887: $title.='<font size=+1><b>Problem: </b>'.
! 1888: &Apache::lonnet::metadata($ENV{'form.url'},'title').'</font><br />'."\n";
1.44 ng 1889: $title.='<font size=+1><b>Section: </b>'.$ENV{'form.section'}.'</font>'."\n";
1890: $title.= &show_grading_menu_form ($symb,$url);
1891: my $result= '<table border="0"><tr><td bgcolor="#777777">'."\n";
1.43 ng 1892: $result.= '<table border="0"><tr bgcolor="#deffff">'.
1893: '<td rowspan=2><b>Username</b></td><td rowspan=2><b>Fullname</b></td>'."\n";
1894:
1895: my %scoreptr = (
1896: 'correct' =>'correct_by_override',
1897: 'incorrect'=>'incorrect_by_override',
1898: 'excused' =>'excused',
1899: 'ungraded' =>'ungraded_attempted',
1900: 'nothing' => '',
1901: );
1.56 matthew 1902: my ($classlist,undef,$fullname) = &getclasslist($ENV{'form.section'},'0');
1.34 ng 1903:
1.44 ng 1904: my (@partid);
1905: my %weight = ();
1.54 albertel 1906: my %columns = ();
1.44 ng 1907: my ($i,$ctr,$count,$rec_update) = (0,0,0,0);
1.54 albertel 1908:
1909: my (@parts) = sort(&getpartlist($url));
1910: my $header;
1.44 ng 1911: while ($ctr < $ENV{'form.totalparts'}) {
1912: my $partid = $ENV{'form.partid_'.$ctr};
1913: push @partid,$partid;
1914: $weight{$partid} = $ENV{'form.weight_'.$partid};
1915: $ctr++;
1.54 albertel 1916: }
1917: foreach my $partid (@partid) {
1918: $header .= '<td align="center"> <b>Old Score</b> </td>'.
1919: '<td align="center"> <b>New Score</b> </td>';
1920: $columns{$partid}=2;
1921: foreach my $stores (@parts) {
1922: my ($part,$type) = &split_part_type($stores);
1923: if ($part !~ m/^\Q$partid\E/) { next;}
1924: if ($type eq 'awarded' || $type eq 'solved') { next; }
1925: my $display=&Apache::lonnet::metadata($url,$stores.'.display');
1926: $display =~ s/\[Part: (\w)+\]//;
1927: $header .= '<td align="center"> <b>Old</b> '.$display.' </td>'.
1928: '<td align="center"> <b>New</b> '.$display.' </td>';
1929: $columns{$partid}+=2;
1930: }
1931: }
1932: foreach my $partid (@partid) {
1933: $result .= '<td colspan="'.$columns{$partid}.
1934: '" align="center"><b>Part '.$partid.
1.44 ng 1935: '</b> (Weight = '.$weight{$partid}.')</td>';
1.54 albertel 1936:
1.44 ng 1937: }
1938: $result .= '</tr><tr bgcolor="#deffff">';
1.54 albertel 1939: $result .= $header;
1.44 ng 1940: $result .= '</tr>'."\n";
1.13 albertel 1941:
1.44 ng 1942: for ($i=0; $i<$ENV{'form.total'}; $i++) {
1943: my $user = $ENV{'form.ctr'.$i};
1944: my %newrecord;
1945: my $updateflag = 0;
1946: my @userdom = grep /^$user:/,keys %$classlist;
1.54 albertel 1947: my (undef,$udom) = split(/:/,$userdom[0]);
1.13 albertel 1948:
1.44 ng 1949: $result .= '<tr bgcolor="#ffffde"><td>'.$user.' </td><td>'.
1950: $$fullname{$userdom[0]}.' </td>';
1951: foreach (@partid) {
1.54 albertel 1952: my $old_aw = $ENV{'form.GD_'.$user.'_'.$_.'_awarded_s'};
1953: my $old_part_pcr = $old_aw/($weight{$_} ne '0' ? $weight{$_}:1);
1954: my $old_part = $old_aw eq '' ? '' : $old_part_pcr;
1955: my $old_score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_solved_s'}};
1956:
1957: my $awarded = $ENV{'form.GD_'.$user.'_'.$_.'_awarded'};
1958: my $pcr = $awarded/($weight{$_} ne '0' ? $weight{$_} : 1);
1959: my $partial = $awarded eq '' ? '' : $pcr;
1.44 ng 1960: my $score;
1961: if ($partial eq '') {
1.54 albertel 1962: $score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_solved_s'}};
1.44 ng 1963: } elsif ($partial > 0) {
1964: $score = 'correct_by_override';
1965: } elsif ($partial == 0) {
1966: $score = 'incorrect_by_override';
1967: }
1.54 albertel 1968: $score = 'excused' if (($ENV{'form.GD_'.$user.'_'.$_.'_solved'} eq 'excused') &&
1.44 ng 1969: ($score ne 'excused'));
1970: $result .= '<td align="center">'.$old_aw.' </td>'.
1971: '<td align="center">'.$awarded.
1972: ($score eq 'excused' ? $score : '').' </td>';
1.5 albertel 1973:
1.54 albertel 1974: if (!($old_part eq $partial && $old_score eq $score)) {
1975: $updateflag = 1;
1976: $newrecord{'resource.'.$_.'.awarded'} = $partial if $partial ne '';
1977: $newrecord{'resource.'.$_.'.solved'} = $score;
1978: $rec_update++;
1979: }
1980:
1981: my $partid=$_;
1982: foreach my $stores (@parts) {
1983: my ($part,$type) = &split_part_type($stores);
1984: if ($part !~ m/^\Q$partid\E/) { next;}
1985: if ($type eq 'awarded' || $type eq 'solved') { next; }
1986: my $old_aw = $ENV{'form.GD_'.$user.'_'.$part.'_'.$type.'_s'};
1987: my $awarded = $ENV{'form.GD_'.$user.'_'.$part.'_'.$type};
1988: if ($awarded ne '' && $awarded ne $old_aw) {
1989: $newrecord{'resource.'.$part.'.'.$type}= $awarded;
1990: $updateflag=1;
1991: }
1992: $result .= '<td align="center">'.$old_aw.' </td>'.
1993: '<td align="center">'.$awarded.' </td>';
1994: }
1.44 ng 1995: }
1996: $result .= '</tr>'."\n";
1997: if ($updateflag) {
1998: $count++;
1999: $newrecord{'resource.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
2000: &Apache::lonnet::cstore(\%newrecord,$symb,$ENV{'request.course.id'},
2001: $udom,$user);
2002: }
2003: }
2004: $result .= '</table></td></tr></table>'."\n";
2005: my $msg = '<b>Number of records updated = '.$rec_update.
2006: ' for '.$count.' student'.($count <= 1 ? '' : 's').'.</b><br />'.
2007: '<b>Total number of students = '.$ENV{'form.total'}.'</b><br />';
2008: return $title.$msg.$result;
1.5 albertel 2009: }
1.54 albertel 2010:
2011: sub split_part_type {
2012: my ($partstr) = @_;
2013: my ($temp,@allparts)=split(/_/,$partstr);
2014: my $type=pop(@allparts);
2015: my $part=join('.',@allparts);
2016: return ($part,$type);
2017: }
2018:
1.44 ng 2019: #------------- end of section for handling grading by section/class ---------
2020: #
2021: #----------------------------------------------------------------------------
2022:
1.5 albertel 2023:
1.44 ng 2024: #----------------------------------------------------------------------------
2025: #
2026: #-------------------------- Next few routines handles grading by csv upload
2027: #
2028: #--- Javascript to handle csv upload
1.27 albertel 2029: sub csvupload_javascript_reverse_associate {
2030: return(<<ENDPICK);
2031: function verify(vf) {
2032: var foundsomething=0;
2033: var founduname=0;
2034: var founddomain=0;
2035: for (i=0;i<=vf.nfields.value;i++) {
2036: tw=eval('vf.f'+i+'.selectedIndex');
2037: if (i==0 && tw!=0) { founduname=1; }
2038: if (i==1 && tw!=0) { founddomain=1; }
2039: if (i!=0 && i!=1 && tw!=0) { foundsomething=1; }
2040: }
2041: if (founduname==0 || founddomain==0) {
2042: alert('You need to specify at both the username and domain');
2043: return;
2044: }
2045: if (foundsomething==0) {
2046: alert('You need to specify at least one grading field');
2047: return;
2048: }
2049: vf.submit();
2050: }
2051: function flip(vf,tf) {
2052: var nw=eval('vf.f'+tf+'.selectedIndex');
2053: var i;
2054: for (i=0;i<=vf.nfields.value;i++) {
2055: //can not pick the same destination field for both name and domain
2056: if (((i ==0)||(i ==1)) &&
2057: ((tf==0)||(tf==1)) &&
2058: (i!=tf) &&
2059: (eval('vf.f'+i+'.selectedIndex')==nw)) {
2060: eval('vf.f'+i+'.selectedIndex=0;')
2061: }
2062: }
2063: }
2064: ENDPICK
2065: }
2066:
2067: sub csvupload_javascript_forward_associate {
2068: return(<<ENDPICK);
2069: function verify(vf) {
2070: var foundsomething=0;
2071: var founduname=0;
2072: var founddomain=0;
2073: for (i=0;i<=vf.nfields.value;i++) {
2074: tw=eval('vf.f'+i+'.selectedIndex');
2075: if (tw==1) { founduname=1; }
2076: if (tw==2) { founddomain=1; }
2077: if (tw>2) { foundsomething=1; }
2078: }
2079: if (founduname==0 || founddomain==0) {
2080: alert('You need to specify at both the username and domain');
2081: return;
2082: }
2083: if (foundsomething==0) {
2084: alert('You need to specify at least one grading field');
2085: return;
2086: }
2087: vf.submit();
2088: }
2089: function flip(vf,tf) {
2090: var nw=eval('vf.f'+tf+'.selectedIndex');
2091: var i;
2092: //can not pick the same destination field twice
2093: for (i=0;i<=vf.nfields.value;i++) {
2094: if ((i!=tf) && (eval('vf.f'+i+'.selectedIndex')==nw)) {
2095: eval('vf.f'+i+'.selectedIndex=0;')
2096: }
2097: }
2098: }
2099: ENDPICK
2100: }
2101:
1.26 albertel 2102: sub csvuploadmap_header {
1.41 ng 2103: my ($request,$symb,$url,$datatoken,$distotal)= @_;
2104: my $javascript;
2105: if ($ENV{'form.upfile_associate'} eq 'reverse') {
2106: $javascript=&csvupload_javascript_reverse_associate();
2107: } else {
2108: $javascript=&csvupload_javascript_forward_associate();
2109: }
1.45 ng 2110:
2111: my $result='<table border="0">';
1.71 ! ng 2112: $result.='<tr><td colspan=3><font size=+1><b>Problem: </b>'.&Apache::lonnet::metadata($url,'title')
! 2113: .'</font></td></tr>';
1.45 ng 2114: my ($partlist,$handgrade) = &response_type($url);
2115: my ($resptype,$hdgrade)=('','no');
2116: for (sort keys(%$handgrade)) {
2117: my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});
2118: $resptype = $responsetype;
2119: $hdgrade = $handgrade if ($handgrade eq 'yes');
2120: $result.='<tr><td><b>Part </b>'.(split(/_/))[0].'</td>'.
2121: '<td><b>Type: </b>'.$responsetype.'</td>'.
2122: '<td><b>Handgrade: </b>'.$handgrade.'</font></td></tr>';
2123: }
2124: $result.='</table>';
1.41 ng 2125: $request->print(<<ENDPICK);
1.26 albertel 2126: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
1.45 ng 2127: <h3><font color="#339933">Uploading Class Grades</font></h3>
2128: $result
1.26 albertel 2129: <hr>
2130: <h3>Identify fields</h3>
2131: Total number of records found in file: $distotal <hr />
2132: Enter as many fields as you can. The system will inform you and bring you back
2133: to this page if the data selected is insufficient to run your class.<hr />
2134: <input type="button" value="Reverse Association" onClick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" />
2135: <input type="hidden" name="associate" value="" />
2136: <input type="hidden" name="phase" value="three" />
2137: <input type="hidden" name="datatoken" value="$datatoken" />
2138: <input type="hidden" name="fileupload" value="$ENV{'form.fileupload'}" />
2139: <input type="hidden" name="upfiletype" value="$ENV{'form.upfiletype'}" />
2140: <input type="hidden" name="upfile_associate"
2141: value="$ENV{'form.upfile_associate'}" />
2142: <input type="hidden" name="symb" value="$symb" />
2143: <input type="hidden" name="url" value="$url" />
2144: <input type="hidden" name="command" value="csvuploadassign" />
2145: <hr />
2146: <script type="text/javascript" language="Javascript">
2147: $javascript
2148: </script>
2149: ENDPICK
1.41 ng 2150: return '';
1.26 albertel 2151:
2152: }
2153:
2154: sub csvupload_fields {
1.41 ng 2155: my ($url) = @_;
2156: my (@parts) = &getpartlist($url);
2157: my @fields=(['username','Student Username'],['domain','Student Domain']);
2158: foreach my $part (sort(@parts)) {
2159: my @datum;
2160: my $display=&Apache::lonnet::metadata($url,$part.'.display');
2161: my $name=$part;
2162: if (!$display) { $display = $name; }
2163: @datum=($name,$display);
2164: push(@fields,\@datum);
2165: }
2166: return (@fields);
1.26 albertel 2167: }
2168:
2169: sub csvuploadmap_footer {
1.41 ng 2170: my ($request,$i,$keyfields) =@_;
2171: $request->print(<<ENDPICK);
1.26 albertel 2172: </table>
2173: <input type="hidden" name="nfields" value="$i" />
2174: <input type="hidden" name="keyfields" value="$keyfields" />
2175: <input type="button" onClick="javascript:verify(this.form)" value="Assign Grades" /><br />
2176: </form>
2177: ENDPICK
2178: }
2179:
2180: sub csvuploadmap {
1.41 ng 2181: my ($request)= @_;
2182: my ($symb,$url)=&get_symb_and_url($request);
2183: if (!$symb) {return '';}
2184: my $datatoken;
2185: if (!$ENV{'form.datatoken'}) {
2186: $datatoken=&Apache::loncommon::upfile_store($request);
1.26 albertel 2187: } else {
1.41 ng 2188: $datatoken=$ENV{'form.datatoken'};
2189: &Apache::loncommon::load_tmp_file($request);
1.26 albertel 2190: }
1.41 ng 2191: my @records=&Apache::loncommon::upfile_record_sep();
2192: &csvuploadmap_header($request,$symb,$url,$datatoken,$#records+1);
2193: my ($i,$keyfields);
2194: if (@records) {
2195: my @fields=&csvupload_fields($url);
1.45 ng 2196:
1.41 ng 2197: if ($ENV{'form.upfile_associate'} eq 'reverse') {
2198: &Apache::loncommon::csv_print_samples($request,\@records);
2199: $i=&Apache::loncommon::csv_print_select_table($request,\@records,
2200: \@fields);
2201: foreach (@fields) { $keyfields.=$_->[0].','; }
2202: chop($keyfields);
2203: } else {
2204: unshift(@fields,['none','']);
2205: $i=&Apache::loncommon::csv_samples_select_table($request,\@records,
2206: \@fields);
2207: my %sone=&Apache::loncommon::record_sep($records[0]);
2208: $keyfields=join(',',sort(keys(%sone)));
2209: }
2210: }
2211: &csvuploadmap_footer($request,$i,$keyfields);
2212: return '';
1.27 albertel 2213: }
2214:
2215: sub csvuploadassign {
1.41 ng 2216: my ($request)= @_;
2217: my ($symb,$url)=&get_symb_and_url($request);
2218: if (!$symb) {return '';}
2219: &Apache::loncommon::load_tmp_file($request);
1.44 ng 2220: my @gradedata = &Apache::loncommon::upfile_record_sep();
1.41 ng 2221: my @keyfields = split(/\,/,$ENV{'form.keyfields'});
2222: my %fields=();
2223: for (my $i=0; $i<=$ENV{'form.nfields'}; $i++) {
2224: if ($ENV{'form.upfile_associate'} eq 'reverse') {
2225: if ($ENV{'form.f'.$i} ne 'none') {
2226: $fields{$keyfields[$i]}=$ENV{'form.f'.$i};
2227: }
2228: } else {
2229: if ($ENV{'form.f'.$i} ne 'none') {
2230: $fields{$ENV{'form.f'.$i}}=$keyfields[$i];
2231: }
2232: }
1.27 albertel 2233: }
1.41 ng 2234: $request->print('<h3>Assigning Grades</h3>');
2235: my $courseid=$ENV{'request.course.id'};
2236: my ($classlist) = &getclasslist('all','1');
2237: my @skipped;
2238: my $countdone=0;
2239: foreach my $grade (@gradedata) {
2240: my %entries=&Apache::loncommon::record_sep($grade);
2241: my $username=$entries{$fields{'username'}};
2242: my $domain=$entries{$fields{'domain'}};
2243: if (!exists($$classlist{"$username:$domain"})) {
2244: push(@skipped,"$username:$domain");
2245: next;
2246: }
2247: my %grades;
2248: foreach my $dest (keys(%fields)) {
2249: if ($dest eq 'username' || $dest eq 'domain') { next; }
2250: if ($entries{$fields{$dest}} eq '') { next; }
2251: my $store_key=$dest;
2252: $store_key=~s/^stores/resource/;
2253: $store_key=~s/_/\./g;
2254: $grades{$store_key}=$entries{$fields{$dest}};
2255: }
2256: $grades{"resource.regrader"}="$ENV{'user.name'}:$ENV{'user.domain'}";
2257: &Apache::lonnet::cstore(\%grades,$symb,$ENV{'request.course.id'},
2258: $domain,$username);
2259: $request->print('.');
2260: $request->rflush();
2261: $countdone++;
2262: }
2263: $request->print("<br />Stored $countdone students\n");
2264: if (@skipped) {
2265: $request->print('<br /><font size="+1"><b>Skipped Students</b></font><br />');
2266: foreach my $student (@skipped) { $request->print("<br />$student"); }
2267: }
2268: $request->print(&view_edit_entire_class_form($symb,$url));
2269: $request->print(&show_grading_menu_form($symb,$url));
2270: return '';
1.26 albertel 2271: }
1.44 ng 2272: #------------- end of section for handling csv file upload ---------
2273: #
2274: #-------------------------------------------------------------------
2275:
2276: #-------------------------- Menu interface -------------------------
2277: #
2278: #--- Show a Grading Menu button - Calls the next routine ---
2279: sub show_grading_menu_form {
2280: my ($symb,$url)=@_;
2281: my $result.='<form action="/adm/grades" method="post">'."\n".
2282: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
2283: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
2284: '<input type="hidden" name="command" value="gradingmenu" />'."\n".
2285: '<input type="submit" name="submit" value="Grading Menu" />'."\n".
2286: '</form>'."\n";
2287: return $result;
2288: }
2289:
2290: #--- Displays the main menu page -------
2291: sub gradingmenu {
2292: my ($request) = @_;
2293: my ($symb,$url)=&get_symb_and_url($request);
2294: if (!$symb) {return '';}
1.45 ng 2295: my $result='<h3> <font color="#339933">Select a Grading Method</font></h3>';
1.44 ng 2296: $result.='<table border="0">';
1.71 ! ng 2297: $result.='<tr><td colspan=3><font size=+1><b>Problem: </b>'.
! 2298: &Apache::lonnet::metadata($ENV{'form.url'},'title').'</font></td></tr>';
1.44 ng 2299: my ($partlist,$handgrade) = &response_type($url);
2300: my ($resptype,$hdgrade)=('','no');
2301: for (sort keys(%$handgrade)) {
2302: my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});
2303: $resptype = $responsetype;
2304: $hdgrade = $handgrade if ($handgrade eq 'yes');
2305: $result.='<tr><td><b>Part </b>'.(split(/_/))[0].'</td>'.
2306: '<td><b>Type: </b>'.$responsetype.'</td>'.
2307: '<td><b>Handgrade: </b>'.$handgrade.'</font></td></tr>';
2308: }
2309: $result.='</table>';
2310: $result.=&view_edit_entire_class_form($symb,$url).'<br />';
2311: $result.=&upcsvScores_form($symb,$url).'<br />';
2312: $result.=&viewGradeaStu_form($symb,$url,$resptype,$hdgrade).'<br />';
1.68 ng 2313: $result.=&gradeByPage_form($symb,$url,$resptype,$hdgrade).'<br />';
1.44 ng 2314: $result.=&verifyReceipt_form($symb,$url)
2315: if ((&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'})) && ($symb));
2316:
2317: return $result;
2318: }
2319:
2320: #--- Menu for grading a section or the whole class ---
2321: sub view_edit_entire_class_form {
2322: my ($symb,$url)=@_;
1.56 matthew 2323: my ($classlist,$sections,undef) = &getclasslist('all','0');
1.44 ng 2324: my $result.='<table width=100% border=0><tr><td bgcolor=#777777>'."\n";
2325: $result.='<table width=100% border=0><tr bgcolor="#e6ffff"><td>'."\n";
1.52 albertel 2326: $result.=' <b>Grade Entire Section or Class</b></td></tr>'."\n";
1.44 ng 2327: $result.='<tr bgcolor=#ffffe6><td>'."\n";
2328: $result.='<form action="/adm/grades" method="post">'."\n".
2329: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
2330: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
2331: '<input type="hidden" name="command" value="viewgrades" />'."\n";
2332: $result.=' <b>Select section:</b> <select name="section">'."\n";
1.49 albertel 2333: if (ref($sections)) {
2334: foreach (sort (@$sections)) {
2335: $result.= '<option>'.$_.'</option>'."\n";
2336: }
1.44 ng 2337: }
2338: $result.='<option selected="on">all</select>'."<br />\n";
1.51 albertel 2339: $result.=' <input type="button" onClick="submit();" value="Grade" /></form>'."\n";
1.44 ng 2340: $result.='</td></tr></table>'."\n";
2341: $result.='</td></tr></table>'."\n";
2342: return $result;
2343: }
2344:
2345: #--- Menu to upload a csv scores ---
2346: sub upcsvScores_form {
2347: my ($symb,$url) = @_;
2348: if (!$symb) {return '';}
1.46 ng 2349: my $result = '<script type="text/javascript" language="javascript">'."\n".
2350: ' function checkUpload(formname) {'."\n".
2351: ' if (formname.upfile.value == "") {'."\n".
2352: ' alert("Please use the browse button to select a file from your local directory.");'."\n".
2353: ' return false;'."\n".
2354: ' }'."\n".
2355: ' formname.submit();'."\n".
2356: ' }'."\n".
2357: '</script>'."\n";
2358:
2359: $result.='<table width=100% border=0><tr><td bgcolor=#777777>'."\n";
1.44 ng 2360: $result.='<table width=100% border=0><tr bgcolor="#e6ffff"><td>'."\n";
2361: $result.=' <b>Specify a file containing the class scores for above resource</b></td></tr>'."\n";
2362: $result.='<tr bgcolor=#ffffe6><td>'."\n";
2363: my $upfile_select=&Apache::loncommon::upfile_select_html();
2364: $result.=<<ENDUPFORM;
2365: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
2366: <input type="hidden" name="symb" value="$symb" />
2367: <input type="hidden" name="url" value="$url" />
2368: <input type="hidden" name="command" value="csvuploadmap" />
2369: $upfile_select
1.46 ng 2370: <br /> <input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Grades" />
1.44 ng 2371: </form>
2372: ENDUPFORM
2373: $result.='</td></tr></table>'."\n";
2374: $result.='</td></tr></table>'."\n";
2375: return $result;
2376: }
2377:
2378: #--- Handgrading problems ---
2379: sub viewGradeaStu_form {
2380: my ($symb,$url,$response,$handgrade) = @_;
2381: my ($classlist,$sections) = &getclasslist('all','0');
2382: my $result.='<table width=100% border=0><tr><td bgcolor=#777777>'."\n";
2383: $result.='<table width=100% border=0><tr bgcolor="#e6ffff"><td>'."\n";
1.49 albertel 2384: $result.=' <b>';
1.68 ng 2385: $result.=($handgrade eq 'yes' ? 'View/Grade' : 'View').' an Individual Student\'s Submission</b></td></tr>'."\n";
1.44 ng 2386: $result.='<tr bgcolor=#ffffe6><td>'."\n";
2387: $result.='<form action="/adm/grades" method="post">'."\n".
2388: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
2389: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
2390: '<input type="hidden" name="response" value="'.$response.'" />'."\n".
2391: '<input type="hidden" name="handgrade" value="'.$handgrade.'" />'."\n".
2392: '<input type="hidden" name="showgrading" value="yes" />'."\n".
2393: '<input type="hidden" name="command" value="submission" />'."\n";
1.26 albertel 2394:
1.44 ng 2395: $result.=' <b>Select section:</b> <select name="section">'."\n";
1.49 albertel 2396: if (ref($sections)) {
2397: foreach (sort (@$sections)) {$result.='<option>'.$_.'</option>'."\n";}
1.44 ng 2398: }
2399: $result.= '<option selected="on">all</select>'."\n";
2400: $result.=' <b>Display students who has: </b>'.
2401: '<input type="radio" name="submitonly" value="yes" checked> submitted'.
2402: '<input type="radio" name="submitonly" value="all"> everybody <br />';
1.49 albertel 2403: if (ref($sections)) {
2404: $result.=' (Section "no" implies the students were not assigned a section.)<br />'
2405: if (grep /no/,@$sections);
2406: }
2407:
2408:
2409: $result.='<br /> <input type="button" onClick="submit();" value="';
2410: if ($handgrade eq 'yes') {
2411: $result.="View/Grade";
2412: } else {
2413: $result.="View";
2414: }
2415: $result.='" />'."\n".'</form>'."\n";
1.44 ng 2416: $result.='</td></tr></table>'."\n";
2417: $result.='</td></tr></table>'."\n";
2418: return $result;
1.2 albertel 2419: }
2420:
1.68 ng 2421: #--- Handgrading problems by page/sequence for each student ---
2422: sub gradeByPage_form {
2423: my ($symb,$url,$response,$handgrade) = @_;
2424: my ($classlist,$sections) = &getclasslist('all','0');
2425: my $result.='<table width=100% border=0><tr><td bgcolor=#777777>'."\n";
2426: $result.='<table width=100% border=0><tr bgcolor="#e6ffff"><td>'."\n";
2427: $result.=' <b>';
2428: $result.='Handgrade an Individual Student\'s by Page/Sequence</b></td></tr>'."\n";
2429: $result.='<tr bgcolor=#ffffe6><td>'."\n";
2430: $result.='<form action="/adm/grades" method="post">'."\n".
2431: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
2432: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
2433: '<input type="hidden" name="response" value="'.$response.'" />'."\n".
2434: '<input type="hidden" name="handgrade" value="'.$handgrade.'" />'."\n".
2435: '<input type="hidden" name="showgrading" value="yes" />'."\n".
2436: '<input type="hidden" name="command" value="pickStudentPage" />'."\n";
2437:
2438: $result.=' <b>Select section:</b> <select name="section">'."\n";
2439: if (ref($sections)) {
2440: foreach (sort (@$sections)) {$result.='<option>'.$_.'</option>'."\n";}
2441: }
2442: $result.= '<option selected="on">all</select>'."\n";
2443:
2444: $result.='<br /> <input type="button" onClick="submit();" value="';
2445: $result.='View/Grade'.'" />'."\n".'</form>'."\n";
2446: $result.='</td></tr></table>'."\n";
2447: $result.='</td></tr></table>'."\n";
2448: return $result;
2449: }
2450:
2451: sub pickStudentPage {
2452: my ($request) = shift;
2453:
2454: $request->print(<<LISTJAVASCRIPT);
2455: <script type="text/javascript" language="javascript">
2456:
2457: function checkPickOne(formname) {
1.70 ng 2458: var user = radioSelection(formname.student);
1.68 ng 2459: if (user == null) {
2460: alert("Please select the student you wish to grade.");
2461: return;
2462: }
1.70 ng 2463: var ptr = pullDownSelection(formname.selectpage);
1.71 ! ng 2464: formname.page.value = eval("formname.page"+ptr+".value");
! 2465: formname.title.value = eval("formname.title"+ptr+".value");
1.68 ng 2466: formname.submit();
2467: }
2468:
2469: function radioSelection(radioButton) {
2470: var selection=null;
2471: for (var i=0; i<radioButton.length; i++) {
2472: if (radioButton[i].checked) {
2473: selection=radioButton[i].value;
2474: return selection;
2475: }
2476: }
2477: return selection;
2478: }
1.70 ng 2479:
2480: function pullDownSelection(selectOne) {
2481: var selection=null;
2482: for (var i=0; i<selectOne.length; i++) {
2483: if (selectOne[i].selected) {
2484: selection=selectOne[i].value;
2485: return selection;
2486: }
2487: }
2488: }
1.68 ng 2489: </script>
2490: LISTJAVASCRIPT
2491:
2492: my ($symb,$url) = &get_symb_and_url();
2493: my $cdom = $ENV{"course.$ENV{'request.course.id'}.domain"};
2494: my $cnum = $ENV{"course.$ENV{'request.course.id'}.num"};
2495: my $getsec = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};
2496:
2497: my $result='<h3><font color="#339933"> '.
2498: 'Manual Grading by Page or Sequence</font></h3>';
2499:
2500: $result.='<form action="/adm/grades" method="post" name="displayPage">'."<br>\n";
1.70 ng 2501: $result.=' <b>Problems from:</b> <select name="selectpage">'."\n";
1.68 ng 2502: my ($titles,$symbx) = &getSymbMap();
1.71 ! ng 2503: my ($curpage,$type,$mapId) = ($symb =~ /(.*?\.(page|sequence))___(\d+)___/);
1.70 ng 2504: my $ctr=0;
1.68 ng 2505: foreach (@$titles) {
2506: my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
1.70 ng 2507: $result.='<option value="'.$ctr.'" '.
1.71 ! ng 2508: ($$symbx{$_} =~ /$curpage$/ ? 'selected="on"' : '').
! 2509: '>'.$showtitle.'</option>'."\n";
1.70 ng 2510: $ctr++;
1.68 ng 2511: }
2512: $result.= '</select>'."<br>\n";
1.70 ng 2513: $ctr=0;
2514: foreach (@$titles) {
2515: my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
2516: $result.='<input type="hidden" name="page'.$ctr.'" value="'.$$symbx{$_}.'" />'."\n";
2517: $result.='<input type="hidden" name="title'.$ctr.'" value="'.$showtitle.'" />'."\n";
2518: $ctr++;
2519: }
2520: $result.='<input type="hidden" name="page" />'."\n";
2521: $result.='<input type="hidden" name="title" />'."\n";
1.68 ng 2522:
1.71 ! ng 2523: $result.=' <b>View Problems: </b><input type="radio" name="vProb" value="no" checked /> no '."\n".
! 2524: '<input type="radio" name="vProb" value="yes" /> yes '."<br>\n";
! 2525: $result.=' <b>Submission Details: </b>'.
! 2526: '<input type="radio" name="lastSub" value="none" /> none'."\n".
! 2527: '<input type="radio" name="lastSub" value="datesub" checked /> dates and submissions'."\n".
! 2528: '<input type="radio" name="lastSub" value="all" /> all details'."\n";
1.68 ng 2529: $result.='<input type="hidden" name="section" value="'.$getsec.'" />'."\n".
2530: '<input type="hidden" name="command" value="displayPage" />'."\n".
2531: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
2532: '<input type="hidden" name="symb" value="'.$symb.'" />'."<br><br>\n";
2533: $request->print($result);
2534:
2535: my $studentTable.=' <b>Select a Student you wish to grade</b><br>'.
2536: '<table border="0"><tr><td bgcolor="#777777">'.
2537: '<table border="0"><tr bgcolor="#e6ffff">'.
2538: '<td><b> Fullname <font color="#999999">(username)</font></b></td>'.
2539: '<td><b> Fullname <font color="#999999">(username)</font></b></td>'.
2540: '<td><b> Fullname <font color="#999999">(username)</font></b></td>'.
2541: '<td><b> Fullname <font color="#999999">(username)</font></b></td></tr>';
2542:
2543: my (undef,undef,$fullname) = &getclasslist($getsec,'0');
2544: my $ptr = 1;
2545: foreach my $student (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
2546: my ($uname,$udom) = split(/:/,$student);
2547: $studentTable.=($ptr%4 == 1 ? '<tr bgcolor="#ffffe6"><td>' : '</td><td>');
1.70 ng 2548: $studentTable.='<input type="radio" name="student" value="'.$student.'" /> '.$$fullname{$student}.
1.68 ng 2549: '<font color="#999999"> ('.$uname.($udom eq $cdom ? '':':'.$udom).')</font>'."\n";
2550: $studentTable.=($ptr%4 == 0 ? '</td></tr>' : '');
2551: $ptr++;
2552: }
2553: $studentTable.='</td><td> </td><td> </td><td> ' if ($ptr%4 == 2);
2554: $studentTable.='</td><td> </td><td> ' if ($ptr%4 == 3);
2555: $studentTable.='</td><td> ' if ($ptr%4 == 0);
2556: $studentTable.='</td></tr></table></td></tr></table>'."\n";
1.70 ng 2557: $studentTable.='<br /> <input type="button" '.
2558: 'onClick="javascript:checkPickOne(this.form);"value="Submit" /></form>'."\n";
1.68 ng 2559:
2560: $studentTable.=&show_grading_menu_form($symb,$url);
2561: $request->print($studentTable);
2562:
2563: return '';
2564: }
2565:
2566: sub getSymbMap {
2567: my $navmap = Apache::lonnavmaps::navmap-> new(
2568: $ENV{'request.course.fn'}.'.db',
2569: $ENV{'request.course.fn'}.'_parms.db',1, 1);
2570:
2571: my $res = $navmap->firstResource(); # temp resource to access constants
2572: $navmap->init();
2573:
2574: # End navmap using boilerplate
2575:
2576: my $iterator = $navmap->getIterator(undef, undef, undef, 1);
2577: my $depth = 1;
2578: $iterator->next(); # ignore first BEGIN_MAP
2579: my $curRes = $iterator->next();
2580:
2581: my %symbx = ();
2582: my @titles = ();
2583: my $minder=0;
2584: while ($depth > 0) {
2585: if ($curRes == $iterator->BEGIN_MAP()) {$depth++;}
2586: if ($curRes == $iterator->END_MAP()) { $depth--; }
2587:
2588: if (ref($curRes) && $curRes->is_map()) {
1.71 ! ng 2589: my ($mapUrl, $id, $resUrl) = split(/___/, $curRes->symb()); # check map contains at least one problem
! 2590: my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
! 2591:
! 2592: my $mapiterator = $navmap->getIterator($map->map_start(),
! 2593: $map->map_finish());
! 2594:
! 2595: my $mapdepth = 1;
! 2596: my $countProblems = 0;
! 2597: $mapiterator->next(); # skip the first BEGIN_MAP
! 2598: my $mapcurRes = $mapiterator->next(); # for "current resource"
! 2599: my $ctr=0;
! 2600: while ($mapdepth > 0 && $ctr < 100) {
! 2601: if($mapcurRes == $mapiterator->BEGIN_MAP) { $mapdepth++; }
! 2602: if($mapcurRes == $mapiterator->END_MAP) { $mapdepth++; }
! 2603:
! 2604: if (ref($mapcurRes) && $mapcurRes->is_problem() && !$mapcurRes->randomout) {
! 2605: $countProblems++;
! 2606: }
! 2607: $ctr++;
! 2608: }
! 2609: if ($countProblems > 0) {
! 2610: my $title = $curRes->compTitle();
! 2611: push @titles,$minder.'.'.$title; # minder, just in case two titles are identical
! 2612: $symbx{$minder.'.'.$title} = $curRes->symb();
! 2613: $minder++;
! 2614: }
1.68 ng 2615: }
2616: $curRes = $iterator->next();
2617: }
2618:
2619: $navmap->untieHashes();
2620: return \@titles,\%symbx;
2621: }
2622:
2623: sub displayPage {
2624: my ($request) = shift;
2625:
1.71 ! ng 2626: my ($symb,$url) = &get_symb_and_url();
1.68 ng 2627: my $cdom = $ENV{"course.$ENV{'request.course.id'}.domain"};
2628: my $cnum = $ENV{"course.$ENV{'request.course.id'}.num"};
2629: my $getsec = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};
2630: my $pageTitle = $ENV{'form.page'};
2631: my (undef,undef,$fullname) = &getclasslist($getsec,'0');
1.70 ng 2632: my ($uname,$udom) = split(/:/,$ENV{'form.student'});
1.68 ng 2633:
1.70 ng 2634: my $result='<h3><font color="#339933"> '.$ENV{'form.title'}.'</font></h3>';
2635: $result.='<h3> Student: '.$$fullname{$ENV{'form.student'}}.
1.68 ng 2636: '<font color="#999999"> ('.$uname.($udom eq $cdom ? '':':'.$udom).')</font></h3>'."\n";
2637:
1.71 ! ng 2638: &sub_page_js($request);
! 2639: $request->print($result);
! 2640:
1.68 ng 2641: my $navmap = Apache::lonnavmaps::navmap-> new(
2642: $ENV{'request.course.fn'}.'.db',
2643: $ENV{'request.course.fn'}.'_parms.db',1, 1);
1.70 ng 2644: my ($mapUrl, $id, $resUrl) = split(/___/, $ENV{'form.page'});
1.68 ng 2645: my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
2646:
2647: my $iterator = $navmap->getIterator($map->map_start(),
2648: $map->map_finish());
2649:
1.71 ! ng 2650: my $studentTable='<form action="/adm/grades" method="post" name="gradePage">'."\n".
! 2651: '<input type="hidden" name="command" value="gradeByPage" />'."\n".
! 2652: '<input type="hidden" name="student" value="'.$ENV{'form.student'}.'" />'."\n".
! 2653: '<input type="hidden" name="page" value="'.$pageTitle.'" />'."\n".
! 2654: '<input type="hidden" name="title" value="'.$ENV{'form.title'}.'" />'."\n".
! 2655: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
! 2656: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n";
! 2657:
! 2658: my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL').
! 2659: '/check.gif" height="16" border="0" />';
! 2660:
! 2661: $studentTable.=' <b>Note:</b> A problem graded correct ('.$checkIcon.
! 2662: ') by the computer cannot be changed.'."\n".
! 2663: '<table border="0"><tr><td bgcolor="#777777">'.
! 2664: '<table border="0"><tr bgcolor="#e6ffff">'.
! 2665: '<td align="center"><b> No </b></td>'.
! 2666: '<td><b> '.($ENV{'form.vProb'} eq 'no' ? 'Title' : 'Problem View').'/Grade</b></td></tr>';
! 2667:
! 2668: my ($depth,$ctr,$question) = (1,0,1);
1.68 ng 2669: $iterator->next(); # skip the first BEGIN_MAP
2670: my $curRes = $iterator->next(); # for "current resource"
2671: while ($depth > 0 && $ctr < 100) { # ctr, just in case it never gets out of loop
2672: if($curRes == $iterator->BEGIN_MAP) { $depth++; }
2673: if($curRes == $iterator->END_MAP) { $depth++; }
2674:
2675: if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) {
2676: my $parts = $curRes->parts();
1.71 ! ng 2677: if (scalar(@{$parts}) > 1) { shift @{$parts}; }
1.68 ng 2678: my $title = $curRes->compTitle();
1.71 ! ng 2679: my $symbx = $curRes->symb();
! 2680: $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$question.
! 2681: (scalar(@{$parts}) == 1 ? '' : '<br>('.scalar(@{$parts}).' parts)').'</td>';
! 2682: $studentTable.='<td valign="top">';
! 2683: if ($ENV{'form.vProb'} eq 'yes') {
! 2684: $studentTable.=&show_problem($request,$symbx,$uname,$udom,1);
! 2685: } else {
! 2686: my $companswer = &Apache::loncommon::get_student_answers(
! 2687: $symbx,$uname,$udom,$ENV{'request.course.id'});
! 2688: $companswer=~s|<form(.*?)>||g;
! 2689: $companswer=~s|</form>||g;
! 2690:
! 2691: # while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>
! 2692: # $request->print('match='.$1.'<br>');
! 2693: # $companswer =~ s/$1/ /s;
! 2694: # }
! 2695: # $companswer =~ s/<table border=\"1\">/<table border=\"0\">/g;
! 2696: $studentTable.=' <b>'.$title.'</b> <br> <b>Correct answer:</b><br>'.$companswer;
! 2697: }
! 2698:
! 2699: my %record = &Apache::lonnet::restore($symbx,$ENV{'request.course.id'},$udom,$uname);
! 2700:
! 2701: if ($ENV{'form.lastSub'} eq 'datesub') {
! 2702: if ($record{'version'} eq '') {
! 2703: $studentTable.='<br /> <font color="red">No recorded submission for this problem</font><br />';
! 2704: } else {
! 2705: $studentTable.='<table border="0" width="100%"><tr><td bgcolor="#777777">'.
! 2706: '<table border="0" width="100%"><tr bgcolor="#e6ffff">'.
! 2707: '<td><b>Date/Time</b></td>'.
! 2708: '<td><b>Submission</b></td>'.
! 2709: '<td><b>Status </b></td></tr>';
! 2710: my ($version);
! 2711: for ($version=1;$version<=$record{'version'};$version++) {
! 2712: my $timestamp = scalar(localtime($record{$version.':timestamp'}));
! 2713: $studentTable.='<tr bgcolor="#ffffff" valign="top"><td>'.$timestamp.'</td>';
! 2714: my @versionKeys = split(/\:/,$record{$version.':keys'});
! 2715: my @displaySub = ();
! 2716: foreach my $partid (@{$parts}) {
! 2717: my @matchKey = grep /^resource\.$partid\..*?\.submission$/,@versionKeys;
! 2718: $displaySub[0].=(exists $record{$version.':'.$matchKey[0]}) ?
! 2719: '<b>Part:</b> '.$partid.' <b>Submission:</b> '
! 2720: .$record{$version.':'.$matchKey[0]}.'<br />' : '';
! 2721: $displaySub[1].=(exists $record{"$version:resource.$partid.award"}) ?
! 2722: '<b>Part:</b> '.$partid.' '.
! 2723: $record{"$version:resource.$partid.award"}.'/'.
! 2724: $record{"$version:resource.$partid.solved"}.'<br />' : '';
! 2725: }
! 2726: $displaySub[1].=(exists $record{"$version:resource.regrader"}) ?
! 2727: 'Manually graded by '.$record{"$version:resource.regrader"} : '';
! 2728: $studentTable.='<td>'.$displaySub[0].' </td><td>'.$displaySub[1].' </td></tr>';
! 2729: }
! 2730: $studentTable.='</table></td></tr></table>';
! 2731: }
! 2732: } elsif ($ENV{'form.lastSub'} eq 'all') {
! 2733: my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : '');
! 2734: $studentTable.=&Apache::loncommon::get_previous_attempt($symbx,$uname,$udom,
! 2735: $ENV{'request.course.id'},
! 2736: '','.submission');
! 2737:
! 2738: }
! 2739:
! 2740: foreach my $partid (@{$parts}) {
! 2741: $studentTable.=&gradeBox($request,$symbx,$uname,$udom,$question,$partid,\%record);
! 2742: $studentTable.='<input type="hidden" name="q_'.$question.'" value="'.$partid.'" />'."\n";
! 2743: $question++;
! 2744: }
! 2745: $studentTable.='</td></tr>';
1.68 ng 2746:
2747: }
2748: $curRes = $iterator->next();
2749: $ctr++;
2750: }
1.71 ! ng 2751: $navmap->init();
1.68 ng 2752:
1.71 ! ng 2753: $studentTable.='</td></tr></table></td></tr></table>'."\n".
! 2754: ' <input type="button" value="Save" '.
! 2755: 'onClick="javascript:checkSubmitPage(this.form,'.$question.');" TARGET=_self />'.
! 2756: '</form>'."\n";
! 2757: $studentTable.=&show_grading_menu_form($symb,$url);
! 2758: $request->print($studentTable);
! 2759:
! 2760: return '';
! 2761: }
! 2762:
! 2763: sub updateGradeByPage {
! 2764: my ($request) = shift;
! 2765:
! 2766: my $cdom = $ENV{"course.$ENV{'request.course.id'}.domain"};
! 2767: my $cnum = $ENV{"course.$ENV{'request.course.id'}.num"};
! 2768: my $getsec = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'};
! 2769: my $pageTitle = $ENV{'form.page'};
! 2770: my (undef,undef,$fullname) = &getclasslist($getsec,'0');
! 2771: my ($uname,$udom) = split(/:/,$ENV{'form.student'});
! 2772:
! 2773: my $result='<h3><font color="#339933"> '.$ENV{'form.title'}.'</font></h3>';
! 2774: $result.='<h3> Student: '.$$fullname{$ENV{'form.student'}}.
! 2775: '<font color="#999999"> ('.$uname.($udom eq $cdom ? '':':'.$udom).')</font></h3>'."\n";
1.70 ng 2776:
1.68 ng 2777: $request->print($result);
2778:
1.71 ! ng 2779: my $navmap = Apache::lonnavmaps::navmap-> new(
! 2780: $ENV{'request.course.fn'}.'.db',
! 2781: $ENV{'request.course.fn'}.'_parms.db',1, 1);
! 2782: my ($mapUrl, $id, $resUrl) = split(/___/, $ENV{'form.page'});
! 2783: my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
! 2784:
! 2785: my $iterator = $navmap->getIterator($map->map_start(),
! 2786: $map->map_finish());
1.70 ng 2787:
1.71 ! ng 2788: my $studentTable='<table border="0"><tr><td bgcolor="#777777">'.
1.68 ng 2789: '<table border="0"><tr bgcolor="#e6ffff">'.
1.70 ng 2790: '<td align="center"><b> No </b></td>'.
1.71 ! ng 2791: '<td><b> Title </b></td>'.
! 2792: '<td><b> Previous Score </b></td>'.
! 2793: '<td><b> New Score </b></td></tr>';
! 2794:
! 2795: $iterator->next(); # skip the first BEGIN_MAP
! 2796: my $curRes = $iterator->next(); # for "current resource"
! 2797: my ($depth,$ctr,$question,$changeflag)= (1,0,1,0);
! 2798: while ($depth > 0 && $ctr < 100) { # ctr, just in case it never gets out of loop
! 2799: if($curRes == $iterator->BEGIN_MAP) { $depth++; }
! 2800: if($curRes == $iterator->END_MAP) { $depth++; }
! 2801:
! 2802: if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) {
! 2803: my $parts = $curRes->parts();
! 2804: if (scalar(@{$parts}) > 1) { shift @{$parts}; }
! 2805: my $title = $curRes->compTitle();
! 2806: my $symbx = $curRes->symb();
! 2807: $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$question.
! 2808: (scalar(@{$parts}) == 1 ? '' : '<br>('.scalar(@{$parts}).' parts)').'</td>';
! 2809: $studentTable.='<td valign="top"> <b>'.$title.'</b> </td>';
! 2810:
! 2811: my %newrecord=();
! 2812: my @displayPts=();
! 2813: foreach my $partid (@{$parts}) {
! 2814: my $newpts = $ENV{'form.GD_BOX'.$question.'_'.$partid};
! 2815: my $oldpts = $ENV{'form.oldpts'.$question.'_'.$partid};
! 2816:
! 2817: my $wgt = $ENV{'form.WGT'.$question.'_'.$partid} != 0 ?
! 2818: $ENV{'form.WGT'.$question.'_'.$partid} : 1;
! 2819: my $partial = $newpts/$wgt;
! 2820: my $score;
! 2821: if ($partial > 0) {
! 2822: $score = 'correct_by_override';
! 2823: } elsif ($partial == 0) {
! 2824: $score = 'incorrect_by_override';
! 2825: }
! 2826: if ($ENV{'form.GD_SEL'.$question.'_'.$partid} eq 'excused') {
! 2827: $partial = '';
! 2828: $score = 'excused';
! 2829: }
! 2830: my $oldstatus = $ENV{'form.solved'.$question.'_'.$partid};
! 2831: $displayPts[0].=' <b>Part</b> '.$partid.' = '.
! 2832: (($oldstatus eq 'excused') ? 'excused' : $oldpts).
! 2833: ' <br>';
! 2834: $displayPts[1].=' <b>Part</b> '.$partid.' = '.
! 2835: ($oldstatus eq 'correct_by_student' ? $oldpts :
! 2836: (($score eq 'excused') ? 'excused' : $newpts)).
! 2837: ' <br>';
! 2838:
! 2839: $question++;
! 2840: if (($oldstatus eq 'correct_by_student') ||
! 2841: ($newpts eq $oldpts && $score eq $oldstatus))
! 2842: {
! 2843: next;
! 2844: }
! 2845: $newrecord{'resource.'.$partid.'.awarded'} = $partial if $partial ne '';
! 2846: $newrecord{'resource.'.$partid.'.solved'} = $score;
! 2847:
! 2848: $changeflag++;
! 2849: }
! 2850: if (scalar(keys(%newrecord)) > 0) {
! 2851: $newrecord{'resource.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
! 2852: &Apache::lonnet::cstore(\%newrecord,$symbx,$ENV{'request.course.id'},
! 2853: $udom,$uname);
! 2854: }
! 2855: $studentTable.='<td valign="top">'.$displayPts[0].'</td>'.
! 2856: '<td valign="top">'.$displayPts[1].'</td>'.
! 2857: '</tr>';
1.68 ng 2858:
2859: }
1.71 ! ng 2860: $curRes = $iterator->next();
! 2861: $ctr++;
1.68 ng 2862: }
1.71 ! ng 2863: $navmap->init();
1.68 ng 2864:
1.71 ! ng 2865: $studentTable.='</td></tr></table></td></tr></table>';
! 2866: $studentTable.=($changeflag == 0 ? 'No score was changed or updated.' :
! 2867: 'The scores were changed for '.
! 2868: $changeflag.' problem'.($changeflag == 1 ? '.' : 's.'));
! 2869: $studentTable.=&show_grading_menu_form($ENV{'form.symb'},$ENV{'form.url'});
1.68 ng 2870: $request->print($studentTable);
2871:
1.70 ng 2872: return '';
2873: }
2874:
1.68 ng 2875:
1.44 ng 2876: #--- Form to input a receipt number ---
2877: sub verifyReceipt_form {
2878: my ($symb,$url) = @_;
1.46 ng 2879: my $result = '<script type="text/javascript" language="javascript">'."\n".
2880: ' function checkEntry(formname) {'."\n".
2881: ' var receipt = formname.receipt.value;'."\n".
2882: ' if (isNaN(receipt) || receipt == "") {'."\n".
2883: ' alert("Please enter a receipt number given by a student in the box.");'."\n".
2884: ' return false;'."\n".
2885: ' }'."\n".
2886: ' formname.submit();'."\n".
2887: ' }'."\n".
2888: '</script>'."\n";
2889:
1.44 ng 2890: my $hostver=unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'});
2891:
1.46 ng 2892: $result.='<table width=100% border=0><tr><td bgcolor=#777777>'."\n";
1.44 ng 2893: $result.='<table width=100% border=0><tr><td bgcolor=#e6ffff>'."\n";
2894: $result.=' <b>Verify a Submission Receipt Issued by this Server</td></tr>'."\n";
2895: $result.='<tr bgcolor=#ffffe6><td>'."\n";
1.46 ng 2896: $result.='<form action="/adm/grades" method="post" name="verifyform">'."\n";
1.44 ng 2897: $result.=' <tt>'.$hostver.'-<input type="text" name="receipt" size="4"></tt><br />'."\n";
1.46 ng 2898: $result.=' <input type="button" onClick="javascript:checkEntry(this.form);"'.
2899: ' value="Verify Receipt">'."\n";
1.44 ng 2900: $result.='<input type="hidden" name="command" value="verify">'."\n";
2901: if ($ENV{'form.url'}) {
2902: $result.='<input type="hidden" name="url" value="'.$ENV{'form.url'}.'" />';
2903: }
2904: if ($ENV{'form.symb'}) {
2905: $result.='<input type="hidden" name="symb" value="'.$ENV{'form.symb'}.'" />';
2906: }
2907: $result.='</form>';
2908: $result.='</td></tr></table>'."\n";
2909: $result.='</td></tr></table>'."\n";
2910: return $result;
1.2 albertel 2911: }
2912:
1.1 albertel 2913: sub handler {
1.41 ng 2914: my $request=$_[0];
2915:
2916: if ($ENV{'browser.mathml'}) {
2917: $request->content_type('text/xml');
2918: } else {
2919: $request->content_type('text/html');
2920: }
2921: $request->send_http_header;
1.44 ng 2922: return '' if $request->header_only;
1.41 ng 2923: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});
2924: my $url=$ENV{'form.url'};
2925: my $symb=$ENV{'form.symb'};
2926: my $command=$ENV{'form.command'};
2927: if (!$url) {
2928: my ($temp1,$temp2);
2929: ($temp1,$temp2,$ENV{'form.url'})=split(/___/,$symb);
2930: $url = $ENV{'form.url'};
2931: }
2932: &send_header($request);
2933: if ($url eq '' && $symb eq '') {
2934: if ($ENV{'user.adv'}) {
2935: if (($ENV{'form.codeone'}) && ($ENV{'form.codetwo'}) &&
2936: ($ENV{'form.codethree'})) {
2937: my $token=$ENV{'form.codeone'}.'*'.$ENV{'form.codetwo'}.'*'.
2938: $ENV{'form.codethree'};
2939: my ($tsymb,$tuname,$tudom,$tcrsid)=
2940: &Apache::lonnet::checkin($token);
2941: if ($tsymb) {
2942: my ($map,$id,$url)=split(/\_\_\_/,$tsymb);
2943: if (&Apache::lonnet::allowed('mgr',$tcrsid)) {
2944: $request->print(
2945: &Apache::lonnet::ssi('/res/'.$url,
2946: ('grade_username' => $tuname,
2947: 'grade_domain' => $tudom,
2948: 'grade_courseid' => $tcrsid,
2949: 'grade_symb' => $tsymb)));
2950: } else {
1.45 ng 2951: $request->print('<h3>Not authorized: '.$token.'</h3>');
1.41 ng 2952: }
2953: } else {
1.45 ng 2954: $request->print('<h3>Not a valid DocID: '.$token.'</h3>');
1.41 ng 2955: }
1.14 www 2956: } else {
1.41 ng 2957: $request->print(&Apache::lonxml::tokeninputfield());
2958: }
2959: }
2960: } else {
2961: $Apache::grades::viewgrades=&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'});
2962: if ($command eq 'submission') {
1.68 ng 2963: ($ENV{'form.student'} eq '' ? &listStudents($request) : &submission($request,0,0));
2964: # if ($command eq 'submission') {
2965: # &listStudents($request) if ($ENV{'form.student'} eq '');
2966: # &submission($request,0,0) if ($ENV{'form.student'} ne '');
2967: } elsif ($command eq 'pickStudentPage') {
2968: &pickStudentPage($request);
2969: } elsif ($command eq 'displayPage') {
2970: &displayPage($request);
1.71 ! ng 2971: } elsif ($command eq 'gradeByPage') {
! 2972: &updateGradeByPage($request);
1.41 ng 2973: } elsif ($command eq 'processGroup') {
2974: &processGroup($request);
2975: } elsif ($command eq 'gradingmenu') {
2976: $request->print(&gradingmenu($request));
2977: } elsif ($command eq 'viewgrades') {
2978: $request->print(&viewgrades($request));
2979: } elsif ($command eq 'handgrade') {
2980: $request->print(&processHandGrade($request));
2981: } elsif ($command eq 'editgrades') {
2982: $request->print(&editgrades($request));
2983: } elsif ($command eq 'verify') {
2984: $request->print(&verifyreceipt($request));
2985: } elsif ($command eq 'csvupload') {
2986: $request->print(&csvupload($request));
2987: } elsif ($command eq 'viewclasslist') {
2988: $request->print(&viewclasslist($request));
2989: } elsif ($command eq 'csvuploadmap') {
2990: $request->print(&csvuploadmap($request));
2991: } elsif ($command eq 'csvuploadassign') {
2992: if ($ENV{'form.associate'} ne 'Reverse Association') {
2993: $request->print(&csvuploadassign($request));
2994: } else {
2995: if ( $ENV{'form.upfile_associate'} ne 'reverse' ) {
2996: $ENV{'form.upfile_associate'} = 'reverse';
2997: } else {
2998: $ENV{'form.upfile_associate'} = 'forward';
2999: }
3000: $request->print(&csvuploadmap($request));
3001: }
1.26 albertel 3002: } else {
1.41 ng 3003: $request->print("Unknown action: $command:");
1.26 albertel 3004: }
1.2 albertel 3005: }
1.41 ng 3006: &send_footer($request);
1.44 ng 3007: return '';
3008: }
3009:
3010: sub send_header {
3011: my ($request)= @_;
3012: $request->print(&Apache::lontexconvert::header());
3013: # $request->print("
3014: #<script>
3015: #remotewindow=open('','homeworkremote');
3016: #remotewindow.close();
3017: #</script>");
1.47 www 3018: $request->print(&Apache::loncommon::bodytag('Grading'));
1.44 ng 3019: }
3020:
3021: sub send_footer {
3022: my ($request)= @_;
3023: $request->print('</body>');
3024: $request->print(&Apache::lontexconvert::footer());
1.1 albertel 3025: }
3026:
3027: 1;
3028:
1.13 albertel 3029: __END__;
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>