1: # The LearningOnline Network with CAPA
2: # The LON-CAPA Grading handler
3: #
4: # $Id: grades.pm,v 1.71 2003/03/11 19:32:02 ng Exp $
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: #
28: # 2/9,2/13 Guy Albertelli
29: # 6/8 Gerd Kortemeyer
30: # 7/26 H.K. Ng
31: # 8/20 Gerd Kortemeyer
32: # Year 2002
33: # June-August H.K. Ng
34: # Year 2003
35: # February, March H.K. Ng
36: #
37:
38: package Apache::grades;
39: use strict;
40: use Apache::style;
41: use Apache::lonxml;
42: use Apache::lonnet;
43: use Apache::loncommon;
44: use Apache::lonnavmaps;
45: use Apache::lonhomework;
46: use Apache::loncoursedata;
47: use Apache::lonmsg qw(:user_normal_msg);
48: use Apache::Constants qw(:common);
49:
50: # ----- These first few routines are general use routines.----
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) {
58: if ( $key =~ m/stores_(\w+)_.*/) {
59: push(@parts,$key);
60: }
61: }
62: return @parts;
63: }
64:
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'})--;
69: my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url)));
70: if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; }
71: return ($symb,$url);
72: }
73:
74: # --- Retrieve the fullname for a user. Return lastname, first middle ---
75: # --- Generation is attached next to the lastname if it exists. ---
76: sub get_fullname {
77: my ($uname,$udom) = @_;
78: my %name=&Apache::lonnet::get('environment', ['lastname','generation',
79: 'firstname','middlename'],
80: $udom,$uname);
81: my $fullname;
82: my ($tmp) = keys(%name);
83: if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
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);
89: }
90: return $fullname;
91: }
92:
93: #--- Get the partlist and the response type for a given problem. ---
94: #--- Indicate if a response type is coded handgraded or not. ---
95: sub response_type {
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'))) {
101: if (/^\w+response_\w+.*/) {
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;
111: }
112:
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) = @_;
117: my $classlist=&Apache::loncoursedata::get_classlist();
118: # Bail out if we were unable to get the classlist
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->{$_}};
127: # still a student?
128: if (($hideexpired) && ($status ne 'Active')) {
129: delete ($classlist->{$_});
130: next;
131: }
132: $section = ($section ne '' ? $section : 'no');
133: if ($getsec eq 'all' || $getsec eq $section) {
134: $sections{$section}++;
135: $fullnames{$_}=$fullname;
136: } else {
137: delete($classlist->{$_});
138: }
139: }
140: my %seen = ();
141: my @sections = sort(keys(%sections));
142: return ($classlist,\@sections,\%fullnames);
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:
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: }
218:
219: #------------------ End of general use routines --------------------
220: #-------------------------------------------------------------------
221:
222: #------------------------------------ Receipt Verification Routines
223: #
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:
238: my $title.='<h3><font color="#339933">Verifying Submission Receipt '.
239: $receipt.'</h3></font>'."\n".
240: '<font size=+1><b>Problem: </b>'.
241: &Apache::lonnet::metadata($ENV{'form.url'},'title').'</font><br><br>'."\n";
242:
243: my ($string,$contents,$matches) = ('','',0);
244: my (undef,undef,$fullname) = &getclasslist('all','0');
245:
246: foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
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 {
262: $string = &jscriptNform($url,$symb).$title.
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: }
273: return $string.&show_grading_menu_form($symb,$url);
274: }
275:
276: #
277: # Pick student and page/sequence for manual grading
278:
279:
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.
284: sub listStudents {
285: my ($request) = shift;
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:
304: my $viewgrade = $ENV{'form.handgrade'} eq 'yes' ? 'View/Grade' : 'View';
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>'.
310: '<b>Problem: </b>'.&Apache::lonnet::metadata($url,'title').'</font></td></tr>'.$result;
311:
312: $request->print(<<LISTJAVASCRIPT);
313: <script type="text/javascript" language="javascript">
314: function checkSelect(checkBox) {
315: var ctr=0;
316: var sense="";
317: if (checkBox.length > 1) {
318: for (var i=0; i<checkBox.length; i++) {
319: if (checkBox[i].checked) {
320: ctr++;
321: }
322: }
323: sense = "a student or group of students";
324: } else {
325: if (checkBox.checked) {
326: ctr = 1;
327: }
328: sense = "the student";
329: }
330: if (ctr == 0) {
331: alert("Please select "+sense+" before clicking on the $viewgrade button.");
332: return false;
333: }
334: document.gradesub.submit();
335: }
336: </script>
337: LISTJAVASCRIPT
338:
339: $request->print($result);
340:
341: my $checkhdgrade = $ENV{'form.handgrade'} eq 'yes' ? 'checked' : '';
342: my $checklastsub = $ENV{'form.handgrade'} eq 'yes' ? '' : 'checked';
343:
344: my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'."\n".
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".
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".
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".
358: '<input type="hidden" name="handgrade" value="'.$ENV{'form.handgrade'}.'" /><br />'."\n".
359: '<input type="hidden" name="showgrading" value="'.$ENV{'form.showgrading'}.'" /><br />'."\n".
360: '<input type="hidden" name="url" value="'.$url.'" />'."\n".
361: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n".
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".
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".
368: 'value="'.$viewgrade.'" />'."\n";
369:
370: my (undef,undef,$fullname) = &getclasslist($getsec,'0');
371:
372: $gradeTable.='<table border="0"><tr><td bgcolor="#777777">'.
373: '<table border="0"><tr bgcolor="#e6ffff">'.
374: '<td><b> Select </b></td><td><b> Fullname </b></td>'.
375: '<td><b> Username </b></td><td><b> Domain </b></td>';
376: foreach (sort(@$partlist)) {
377: $gradeTable.='<td><b> Part '.(split(/_/))[0].' Status </b></td>';
378: }
379: $gradeTable.='</tr>'."\n";
380:
381: my $ctr = 0;
382: foreach my $student (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
383: my ($uname,$udom) = split(/:/,$student);
384: my (%status) =&student_gradeStatus($url,$symb,$udom,$uname,$partlist);
385: my $statusflg = '';
386: foreach (keys(%status)) {
387: $statusflg = 1 if ($status{$_} ne 'nothing');
388: my ($foo,$partid,$foo1) = split(/\./,$_);
389: if ($status{'resource.'.$partid.'.submitted_by'} ne '') {
390: $statusflg = '';
391: $gradeTable.='<input type="hidden" name="'.
392: $student.':submitted_by" value="'.
393: $status{'resource.'.$partid.'.submitted_by'}.'" />';
394: }
395: }
396: next if ($statusflg eq '' && $submitonly eq 'yes');
397:
398: $ctr++;
399: if ( $Apache::grades::viewgrades eq 'F' ) {
400: $gradeTable.='<tr bgcolor="#ffffe6">'.
401: '<td align="center"><input type=checkbox name="stuinfo" value="'.
402: $student.':'.$$fullname{$student}.'"></td>'."\n".
403: '<td> '.$$fullname{$student}.' </td>'."\n".
404: '<td> '.$uname.' </td>'."\n".
405: '<td align="middle"> '.$udom.' </td>'."\n";
406:
407: foreach (sort keys(%status)) {
408: next if (/^resource.*?submitted_by$/);
409: $gradeTable.='<td align="middle"> '.$status{$_}.' </td>'."\n";
410: }
411: $gradeTable.='</tr>'."\n";
412: }
413: }
414: $gradeTable.='</table></td></tr></table>'.
415: '<input type="button" '.
416: 'onClick="javascript:checkSelect(this.form.stuinfo);" '.
417: 'value="'.$viewgrade.'" /></form>'."\n";
418: if ($ctr == 0) {
419: $gradeTable='<br /> <font color="red">'.
420: 'No submission found for this resource.</font><br />';
421: } elsif ($ctr == 1) {
422: $gradeTable =~ s/type=checkbox/type=checkbox checked/;
423: }
424: $gradeTable.=&show_grading_menu_form($symb,$url);
425: $request->print($gradeTable);
426: return '';
427: }
428:
429: #---- Called from the listStudents routine
430: # Displays the submissions for one student or a group of students
431: sub processGroup {
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;
437:
438: foreach (@stuchecked) {
439: my ($uname,$udom,$fullname) = split(/:/);
440: $ENV{'form.student'} = $uname;
441: $ENV{'form.userdom'} = $udom;
442: $ENV{'form.fullname'} = $fullname;
443: &submission($request,$ctr,$total);
444: $ctr++;
445: }
446: return '';
447: }
448:
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">
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;
477: }
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: }
486: }
487:
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";
497: }
498:
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: }
512: }
513: var stores = eval("formname.stores"+id);
514: stores.value = "0";
515: updateSelect(formname,id);
516: return;
517: }
518:
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: }
540:
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";
545: }
546: return "update";
547: }
548:
549: function updateSelect(formname,id) {
550: var gradeSelect = eval("formname.GD_SEL"+id);
551: gradeSelect[0].selected = true;
552: return;
553: }
554:
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: }
581:
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: }
616:
617: formname.submit();
618: }
619: </script>
620: SUBJAVASCRIPT
621: }
622:
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">
628:
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=="") {
659: alert("Please select a word or group of words from document and then click this link.");
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>");
762: pWin.document.write("<td><input name=\\"msgsub\\" type=\\"text\\" value=\\""+msg+"\\"size=\\"60\\" maxlength=\\"80\\"></td></tr>");
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>");
769: pWin.document.write("<td><input name=\\"msg"+ctr+"\\" type=\\"text\\" value=\\""+msg+"\\" size=\\"60\\" maxlength=\\"80\\"></td></tr>");
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>");
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>");
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:
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: }
941:
942: sub show_problem {
943: my ($request,$symb,$uname,$udom,$removeform,$viewon) = @_;
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">';
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 />';
963: $result.='<b>Correct answer:</b><br />'.$companswer;
964: $result.='</td></tr></table>';
965: $result.='</td></tr></table><br />';
966: return $result;
967: # $request->print($result);
968: }
969:
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 ''; }
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' : '');
984: # $ENV{'form.vProb'} = $ENV{'form.vProb'} ne '' ? $ENV{'form.vProb'} : 'yes';
985:
986: # header info
987: if ($counter == 0) {
988: &sub_page_js($request);
989: &sub_page_kw_js($request);
990: $request->print('<h3> <font color="#339933">Submission Record</font></h3>'."\n".
991: '<font size=+1> <b>Problem: </b>'.
992: &Apache::lonnet::metadata($url,'title').'</font>'."\n");
993:
994: # option to display problem, only once else it cause problems
995: # with the form later since the problem has a form.
996: if ($ENV{'form.vProb'} eq 'yes' or !$ENV{'form.vProb'}) {
997: $request->print(&show_problem($request,$symb,$uname,$udom,0,1));
998: }
999:
1000: # kwclr is the only variable that is guaranteed to be non blank
1001: # if this subroutine has been called once.
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';
1016:
1017: }
1018:
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);
1048:
1049: if ($ENV{'form.handgrade'} eq 'yes' && $ENV{'form.showgrading'} eq 'yes') {
1050: $request->print(<<KEYWORDS);
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
1057: }
1058: }
1059:
1060: if ($ENV{'form.vProb'} eq 'all') {
1061: $request->print('<br /><br /><br />') if ($counter > 0);
1062: $request->print(&show_problem($request,$symb,$uname,$udom,1,1));
1063: }
1064:
1065: my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname);
1066: my ($partlist,$handgrade) = &response_type($url);
1067:
1068: # Display student info
1069: $request->print(($counter == 0 ? '' : '<br />'));
1070: my $result='<table border="0" width=100%><tr><td bgcolor="#777777">'."\n".
1071: '<table border="0" width=100%><tr bgcolor="#edffff"><td>'."\n";
1072:
1073: $result.='<b>Fullname: </b>'.$ENV{'form.fullname'}.
1074: '<font color="#999999"> Username: '.$uname.'</font>'.
1075: '<font color="#999999"> Domain: '.$udom.'</font><br />'."\n";
1076: $result.='<input type="hidden" name="name'.$counter.
1077: '" value="'.$ENV{'form.fullname'}.'" />'."\n";
1078:
1079: # If this is handgraded, then check for collaborators
1080: my @col_fullnames;
1081: my ($classlist,$fullname);
1082: if ($ENV{'form.handgrade'} eq 'yes') {
1083: my @col_list;
1084: ($classlist,undef,$fullname) = &getclasslist('all','0');
1085: for (keys (%$handgrade)) {
1086: my $ncol = &Apache::lonnet::EXT('resource.'.$_.
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: }
1136: }
1137: }
1138: $request->print($result."\n");
1139:
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
1146: if ($ENV{'form.lastSub'} =~ /^(lastonly|hdgrade)$/) {
1147: if ($ENV{'form.'.$uname.':'.$udom.':submitted_by'}) {
1148: my $submitby=''.
1149: '<b>Collaborative submission by: </b>'.
1150: '<a href="javascript:viewSubmitter(\''.
1151: $ENV{'form.'.$uname.':'.$udom.':submitted_by'}.
1152: '\')"; TARGET=_self>'.
1153: $$fullname{$ENV{'form.'.$uname.':'.$udom.':submitted_by'}}.'</a>';
1154: $request->print($submitby);
1155: } else {
1156: my ($string,$timestamp)=
1157: &get_last_submission (%record);
1158: my $lastsubonly=''.
1159: ($$timestamp eq '' ? '' : '<b>Date Submitted:</b> '.
1160: $$timestamp).'';
1161: if ($$timestamp eq '') {
1162: $lastsubonly.='<tr><td bgcolor="#ffffe6">'.$$string[0].'</td></tr>'."\n";
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);
1169: $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part '.
1170: $partid.'</b> <font color="#999999">( ID '.$respid.
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>'.
1177: &keywords_highlight($subval).'</td></tr>'."\n"
1178: if ($ENV{'form.lastSub'} eq 'lastonly' ||
1179: ($ENV{'form.lastSub'} eq 'hdgrade' &&
1180: $$handgrade{$part} =~ /:yes$/));
1181: }
1182: }
1183: }
1184: }
1185: $lastsubonly.='</td></tr>'."\n";
1186: $request->print($lastsubonly);
1187: }
1188: } else {
1189: $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,
1190: $ENV{'request.course.id'},
1191: $last,'.submission',
1192: 'Apache::grades::keywords_highlight'));
1193: }
1194:
1195: # return if view submission with no grading option
1196: if ($ENV{'form.showgrading'} eq '') {
1197: $request->print('</td></tr></table></td></tr></table></form>'."\n");
1198: return;
1199: }
1200:
1201: # Grading options
1202: $result='<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n".
1203: '<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".
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>'.
1216: '<br /> (Message will be sent when you click on Save & Next below.)'."\n"
1217: if ($ENV{'form.handgrade'} eq 'yes');
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:
1229: $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));
1230:
1231: # $request->print($result);
1232: }
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");
1242:
1243: # print end of form
1244: if ($counter == $total) {
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" '.
1249: 'onClick="javascript:checksubmit(this.form,\'Save & Next\','.
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" '.
1262: 'onClick="javascript:checksubmit(this.form,\'Next\');" TARGET=_self> '."\n".
1263: '<input type="button" value="Previous" '.
1264: 'onClick="javascript:checksubmit(this.form,\'Previous\');" TARGET=_self> ';
1265: $endform.='(Next and Previous do not save the scores.)'."\n"
1266: if ($ENV{'form.handgrade'} eq 'yes');
1267: $endform.='</td><tr></table></form>';
1268: $endform.=&show_grading_menu_form($symb,$url);
1269: $request->print($endform);
1270: }
1271: return '';
1272: }
1273:
1274: #--- Retrieve the last submission for all the parts
1275: sub get_last_submission {
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.':'.$_};
1284: # if ($returnhash{$version.':'.$_} =~ /(SUBMITTED|DRAFT)$/) {
1285: $timestamp = scalar(localtime($returnhash{$version.':timestamp'}));
1286: # }
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{$_}));
1295: }
1296: }
1297: }
1298: @string = $string[0] eq '' ? 'Nothing submitted - no attempts.' : @string;
1299: return \@string,\$timestamp;
1300: }
1301:
1302: #--- High light keywords, with style choosen by user.
1303: sub keywords_highlight {
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'};
1307: (my $styleoff = $styleon) =~ s/\</\<\//;
1308: my @keylist = split(/[,\s+]/,$ENV{'form.keywords'});
1309: foreach (@keylist) {
1310: $string =~ s/\b\Q$_\E(\b|\.)/\<font color\=$ENV{'form.kwclr'} $size\>$styleon$_$styleoff\<\/font\>/gi;
1311: }
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;
1317: return $string;
1318: }
1319:
1320: #--- Called from submission routine
1321: sub processHandGrade {
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:
1329: if ($button eq 'Save & Next') {
1330: my $ctr = 0;
1331: while ($ctr < $ngrade) {
1332: my ($uname,$udom) = split(/:/,$ENV{'form.unamedom'.$ctr});
1333: my ($errorflag) = &saveHandGrade($request,$url,$symb,$uname,$udom,$ctr);
1334: if ($errorflag eq 'no_score') {
1335: $ctr++;
1336: next;
1337: }
1338:
1339: my $includemsg = $ENV{'form.includemsg'.$ctr};
1340: my ($subject,$message,$msgstatus) = ('','','');
1341: if ($includemsg =~ /savemsg|newmsg\Q$ctr\E/) {
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
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+$//;
1372: my (@keywords) = sort(split(/\s+/,$ENV{'form.keywords'}));
1373: $ENV{'form.keywords'} = join(' ',@keywords);
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:
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
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:
1407: # Called by Save & Refresh from Highlight Attribute Window
1408: if ($ENV{'form.refresh'} eq 'on') {
1409: my $ctr = 0;
1410: $ENV{'form.NTSTU'}=$ngrade;
1411: while ($ctr < $ngrade) {
1412: ($ENV{'form.student'},$ENV{'form.userdom'}) = split(/:/,$ENV{'form.unamedom'.$ctr});
1413: &submission($request,$ctr,$ngrade-1);
1414: $ctr++;
1415: }
1416: return '';
1417: }
1418:
1419: # Get the next/previous one or group of students
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: }
1428:
1429: my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'0');
1430: my (@parsedlist,@nextlist);
1431: my ($nextflg) = 0;
1432: foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
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') {
1448: my (%status) = &student_gradeStatus($ENV{'form.url'},$symb,$udom,$uname,$partlist) ;
1449: my $statusflg = '';
1450: foreach (keys(%status)) {
1451: $statusflg = 1 if ($status{$_} ne 'nothing');
1452: my ($foo,$partid,$foo1) = split(/\./);
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: }
1460:
1461: $ctr = 0;
1462: my $total = scalar(@nextlist)-1;
1463:
1464: foreach (sort @nextlist) {
1465: my ($uname,$udom,$submitter) = split(/:/);
1466: $ENV{'form.student'} = $uname;
1467: $ENV{'form.userdom'} = $udom;
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 '';
1482: }
1483:
1484: #---- Save the score and award for each student, if changed
1485: sub saveHandGrade {
1486: my ($request,$url,$symb,$stuname,$domain,$newflg,$submitter) = @_;
1487: my %record=&Apache::lonnet::restore($symb,$ENV{'request.course.id'},$domain,$stuname);
1488: my %newrecord;
1489: foreach (split(/:/,$ENV{'form.partlist'.$newflg})) {
1490: if ($ENV{'form.GD_SEL'.$newflg.'_'.$_} eq 'excused') {
1491: if ($record{'resource.'.$_.'.solved'} ne 'excused') {
1492: $newrecord{'resource.'.$_.'.solved'} = 'excused';
1493: if (exists($record{'resource.'.$_.'.awarded'})) {
1494: $newrecord{'resource.'.$_.'.awarded'} = '';
1495: }
1496: }
1497: } else {
1498: my $pts = ($ENV{'form.GD_BOX'.$newflg.'_'.$_} ne '' ?
1499: $ENV{'form.GD_BOX'.$newflg.'_'.$_} :
1500: $ENV{'form.RADVAL'.$newflg.'_'.$_});
1501: return 'no_score' if ($pts eq '' && $ENV{'form.GD_SEL'.$newflg.'_'.$_} eq '');
1502: my $wgt = $ENV{'form.WGT'.$newflg.'_'.$_} eq '' ? 1 :
1503: $ENV{'form.WGT'.$newflg.'_'.$_};
1504: my $partial= $pts/$wgt;
1505: $newrecord{'resource.'.$_.'.awarded'} = $partial
1506: if ($record{'resource.'.$_.'.awarded'} ne $partial);
1507: my $reckey = 'resource.'.$_.'.solved';
1508: if ($partial == 0) {
1509: $newrecord{$reckey} = 'incorrect_by_override'
1510: if ($record{$reckey} ne 'incorrect_by_override');
1511: } else {
1512: $newrecord{$reckey} = 'correct_by_override'
1513: if ($record{$reckey} ne 'correct_by_override');
1514: }
1515: $newrecord{'resource.'.$_.'.submitted_by'} = $submitter
1516: if ($submitter && ($record{'resource.'.$_.'.submitted_by'} ne $submitter));
1517: }
1518: }
1519:
1520: if (scalar(keys(%newrecord)) > 0) {
1521: $newrecord{'resource.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}";
1522: &Apache::lonnet::cstore(\%newrecord,$symb,
1523: $ENV{'request.course.id'},$domain,$stuname);
1524: }
1525: return '';
1526: }
1527:
1528: #--------------------------------------------------------------------------------------
1529: #
1530: #-------------------------- Next few routines handles grading by section or whole class
1531: #
1532: #--- Javascript to handle grading by section or whole class
1533: sub viewgrades_js {
1534: my ($request) = shift;
1535:
1536: $request->print(<<VIEWJAVASCRIPT);
1537: <script type="text/javascript" language="javascript">
1538: function writePoint(partid,weight,point) {
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: }
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: }
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: }
1571:
1572: } else {
1573: textbox.value = point;
1574: }
1575: for (i=0;i<document.classgrade.total.value;i++) {
1576: var user = eval("document.classgrade.ctr"+i+".value");
1577: var scorename = eval("document.classgrade.GD_"+user+
1578: "_"+partid+"_awarded");
1579: var saveval = eval("document.classgrade.GD_"+user+
1580: "_"+partid+"_solved_s.value");
1581: var selname = eval("document.classgrade.GD_"+user+"_"+partid+"_solved");
1582: if (saveval != "correct") {
1583: scorename.value = point;
1584: if (selname[0].selected != true) {
1585: selname[0].selected = true;
1586: }
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);
1595: var radioButton = eval("document.classgrade.RADVAL_"+partid);
1596: var textbox = eval("document.classgrade.TEXTVAL_"+partid);
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++) {
1605: var user = eval("document.classgrade.ctr"+i+".value");
1606: var scorename = eval("document.classgrade.GD_"+user+
1607: "_"+partid+"_awarded");
1608: var saveval = eval("document.classgrade.GD_"+user+
1609: "_"+partid+"_solved_s.value");
1610: var selname = eval("document.classgrade.GD_"+user+
1611: "_"+partid+"_solved");
1612: if (saveval != "correct") {
1613: scorename.value = "";
1614: selname[1].selected = true;
1615: }
1616: }
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+
1621: "_"+partid+"_awarded");
1622: var saveval = eval("document.classgrade.GD_"+user+
1623: "_"+partid+"_solved_s.value");
1624: var selname = eval("document.classgrade.GD_"+user+
1625: "_"+partid+"_solved");
1626: if (saveval != "correct") {
1627: scorename.value = eval("document.classgrade.GD_"+user+
1628: "_"+partid+"_awarded_s.value");;
1629: selname[0].selected = true;
1630: }
1631: }
1632: }
1633: }
1634:
1635: function changeSelect(partid,user) {
1636: var selval = eval("document.classgrade.GD_"+user+'_'+partid+"_solved");
1637: var textbox = eval("document.classgrade.GD_"+user+'_'+partid+"_awarded");
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: }
1654: selval[0].selected = true;
1655: }
1656:
1657: function changeOneScore(partid,user) {
1658: var selval = eval("document.classgrade.GD_"+user+'_'+partid+"_solved");
1659: if (selval[1].selected) {
1660: var boxval = eval("document.classgrade.GD_"+user+'_'+partid+"_awarded");
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++) {
1679: var user = eval("document.classgrade.ctr"+i+".value");
1680: var resetscore = eval("document.classgrade.GD_"+user+
1681: "_"+partid+"_awarded");
1682: resetscore.value = eval("document.classgrade.GD_"+user+
1683: "_"+partid+"_awarded_s.value");
1684:
1685: var saveselval = eval("document.classgrade.GD_"+user+
1686: "_"+partid+"_solved_s.value");
1687:
1688: var selname = eval("document.classgrade.GD_"+user+"_"+partid+"_solved");
1689: if (saveselval == "excused") {
1690: if (selname[1].selected == false) { selname[1].selected = true;}
1691: } else {
1692: if (selname[0].selected == false) {selname[0].selected = true};
1693: }
1694: }
1695: }
1696: }
1697:
1698: </script>
1699: VIEWJAVASCRIPT
1700: }
1701:
1702: #--- show scores for a section or whole class w/ option to change/update a score
1703: sub viewgrades {
1704: my ($request) = shift;
1705: &viewgrades_js($request);
1706:
1707: my ($symb,$url) = ($ENV{'form.symb'},$ENV{'form.url'});
1708: my $result='<h3><font color="#339933">Manual Grading</font></h3>';
1709:
1710: $result.='<font size=+1><b>Problem: </b>'.&Apache::lonnet::metadata($ENV{'form.url'},'title')
1711: .'</font>'."\n";
1712:
1713: #view individual student submission form - called using Javascript viewOneStudent
1714: $result.=&jscriptNform($url,$symb);
1715:
1716: #beginning of class grading form
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".
1720: '<input type="hidden" name="command" value="editgrades" />'."\n".
1721: '<input type="hidden" name="section" value="'.$ENV{'form.section'}.'" />'."\n";
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>';
1738: #radio buttons/text box for assigning points for a section or class.
1739: #handles different parts of a problem
1740: my ($partlist,$handgrade) = &response_type($ENV{'form.url'});
1741: my %weight = ();
1742: my $ctsparts = 0;
1743: $result.='<table border="0">';
1744: my %seen = ();
1745: for (sort keys(%$handgrade)) {
1746: my ($partid,$respid) = split (/_/,$_,2);
1747: next if $seen{$partid};
1748: $seen{$partid}++;
1749: my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_});
1750: my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);
1751: $weight{$partid} = $wgt eq '' ? '1' : $wgt;
1752:
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>';
1758: $result.='<table border="0"><tr>';
1759: my $ctr = 0;
1760: while ($ctr<=$weight{$partid}) { # display radio buttons in a nice table 10 across
1761: $result.= '<td><input type="radio" name="RADVAL_'.$partid.'" '.
1762: 'onclick="javascript:writePoint(\''.$partid.'\','.$weight{$partid}.
1763: ','.$ctr.')" />'.$ctr."</td>\n";
1764: $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
1765: $ctr++;
1766: }
1767: $result.='</tr></table>';
1768: $result.= '</td><td><b> or </b><input type="text" name="TEXTVAL_'.
1769: $partid.'" size="4" '.'onChange="javascript:writePoint(\''.
1770: $partid.'\','.$weight{$partid}.',\'textval\')" /> /'.
1771: $weight{$partid}.' (problem weight)</td>'."\n";
1772: $result.= '</td><td><select name="SELVAL_'.$partid.'"'.
1773: 'onChange="javascript:writeRadText(\''.$partid.'\','.
1774: $weight{$partid}.')"> '.
1775: '<option selected="on"> </option>'.
1776: '<option>excused</option></select></td></tr>'."\n";
1777: $ctsparts++;
1778: }
1779: $result.='</table>'.'</td></tr></table>'.'</td></tr></table>'."\n".
1780: '<input type="hidden" name="totalparts" value="'.$ctsparts.'" />';
1781: $result.='<input type="button" value="Reset" '.
1782: 'onClick="javascript:resetEntry('.$ctsparts.');" TARGET=_self> ';
1783: $result.='<input type="button" value="Submit Changes" '.
1784: 'onClick="javascript:submit();" TARGET=_self />'."\n";
1785:
1786: #table listing all the students in a section/class
1787: #header of table
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: }
1796: $result.= '<table border=0><tr><td bgcolor="#777777">'."\n".
1797: '<table border=0><tr bgcolor="#deffff">'.
1798: '<td><b>Fullname</b></td><td><b>Username</b></td><td><b>Domain</b></td>'."\n";
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/) {
1804: my ($partid) = &split_part_type($part);
1805: $result.='<td><b>Score Part '.$partid.'<br />(weight = '.
1806: $weight{$partid}.')</b></td>'."\n";
1807: next;
1808: }
1809: $display =~ s|Problem Status|Grade Status<br />|;
1810: $result.='<td><b>'.$display.'</b></td>'."\n";
1811: }
1812: $result.='</tr>';
1813:
1814: #get info for each student
1815: #list all the students - with points and grade status
1816: my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'0');
1817: my $ctr = 0;
1818: foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) {
1819: my ($uname,$udom) = split(/:/);
1820: $result.='<input type="hidden" name="ctr'.$ctr.'" value="'.$uname.'" />'."\n";
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";
1827: $result.='<input type="button" value="Submit Changes" '.
1828: 'onClick="javascript:submit();" TARGET=_self /></form>'."\n";
1829: $result.=&show_grading_menu_form($symb,$url);
1830: return $result;
1831: }
1832:
1833: #--- call by previous routine to display each student
1834: sub viewstudentgrade {
1835: my ($url,$symb,$courseid,$student,$fullname,$parts,$weight) = @_;
1836: my ($uname,$udom) = split(/:/,$student);
1837: my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);
1838: my $result='<tr bgcolor="#ffffdd"><td>'.
1839: '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom.
1840: '\')"; TARGET=_self>'.$fullname.'</a>'.
1841: '</td><td>'.$uname.'</td><td align="middle">'.$udom.'</td>'."\n";
1842: foreach my $apart (@$parts) {
1843: my ($part,$type) = &split_part_type($apart);
1844: my $score=$record{"resource.$part.$type"};
1845: if ($type eq 'awarded') {
1846: my $pts = $score eq '' ? '' : $score*$$weight{$part};
1847: $result.='<input type="hidden" name="'.
1848: 'GD_'.$uname.'_'.$part.'_awarded_s" value="'.$pts.'" />'."\n";
1849: $result.='<td align="middle"><input type="text" name="'.
1850: 'GD_'.$uname.'_'.$part.'_awarded" '.
1851: 'onChange="javascript:changeSelect(\''.$part.'\',\''.$uname.
1852: '\')" value="'.$pts.'" size="4" /></td>'."\n";
1853: } elsif ($type eq 'solved') {
1854: my ($status,$foo)=split(/_/,$score,2);
1855: $status = 'nothing' if ($status eq '');
1856: $result.='<input type="hidden" name="'.'GD_'.$uname.'_'.
1857: $part.'_solved_s" value="'.$status.'" />'."\n";
1858: $result.='<td align="middle"><select name="'.
1859: 'GD_'.$uname.'_'.$part.'_solved" '.
1860: 'onChange="javascript:changeOneScore(\''.$part.'\',\''.$uname.'\')" >'."\n";
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');
1864: $result.=$optsel;
1865: $result.="</select></td>\n";
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";
1873: }
1874: }
1875: $result.='</tr>';
1876: return $result;
1877: }
1878:
1879: #--- change scores for all the students in a section/class
1880: # record does not get update if unchanged
1881: sub editgrades {
1882: my ($request) = @_;
1883:
1884: my $symb=$ENV{'form.symb'};
1885: my $url =$ENV{'form.url'};
1886: my $title='<h3><font color="#339933">Current Grade Status</font></h3>';
1887: $title.='<font size=+1><b>Problem: </b>'.
1888: &Apache::lonnet::metadata($ENV{'form.url'},'title').'</font><br />'."\n";
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";
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: );
1902: my ($classlist,undef,$fullname) = &getclasslist($ENV{'form.section'},'0');
1903:
1904: my (@partid);
1905: my %weight = ();
1906: my %columns = ();
1907: my ($i,$ctr,$count,$rec_update) = (0,0,0,0);
1908:
1909: my (@parts) = sort(&getpartlist($url));
1910: my $header;
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++;
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.
1935: '</b> (Weight = '.$weight{$partid}.')</td>';
1936:
1937: }
1938: $result .= '</tr><tr bgcolor="#deffff">';
1939: $result .= $header;
1940: $result .= '</tr>'."\n";
1941:
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;
1947: my (undef,$udom) = split(/:/,$userdom[0]);
1948:
1949: $result .= '<tr bgcolor="#ffffde"><td>'.$user.' </td><td>'.
1950: $$fullname{$userdom[0]}.' </td>';
1951: foreach (@partid) {
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;
1960: my $score;
1961: if ($partial eq '') {
1962: $score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_solved_s'}};
1963: } elsif ($partial > 0) {
1964: $score = 'correct_by_override';
1965: } elsif ($partial == 0) {
1966: $score = 'incorrect_by_override';
1967: }
1968: $score = 'excused' if (($ENV{'form.GD_'.$user.'_'.$_.'_solved'} eq 'excused') &&
1969: ($score ne 'excused'));
1970: $result .= '<td align="center">'.$old_aw.' </td>'.
1971: '<td align="center">'.$awarded.
1972: ($score eq 'excused' ? $score : '').' </td>';
1973:
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: }
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;
2009: }
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:
2019: #------------- end of section for handling grading by section/class ---------
2020: #
2021: #----------------------------------------------------------------------------
2022:
2023:
2024: #----------------------------------------------------------------------------
2025: #
2026: #-------------------------- Next few routines handles grading by csv upload
2027: #
2028: #--- Javascript to handle csv upload
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:
2102: sub csvuploadmap_header {
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: }
2110:
2111: my $result='<table border="0">';
2112: $result.='<tr><td colspan=3><font size=+1><b>Problem: </b>'.&Apache::lonnet::metadata($url,'title')
2113: .'</font></td></tr>';
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>';
2125: $request->print(<<ENDPICK);
2126: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload">
2127: <h3><font color="#339933">Uploading Class Grades</font></h3>
2128: $result
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
2150: return '';
2151:
2152: }
2153:
2154: sub csvupload_fields {
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);
2167: }
2168:
2169: sub csvuploadmap_footer {
2170: my ($request,$i,$keyfields) =@_;
2171: $request->print(<<ENDPICK);
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 {
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);
2187: } else {
2188: $datatoken=$ENV{'form.datatoken'};
2189: &Apache::loncommon::load_tmp_file($request);
2190: }
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);
2196:
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 '';
2213: }
2214:
2215: sub csvuploadassign {
2216: my ($request)= @_;
2217: my ($symb,$url)=&get_symb_and_url($request);
2218: if (!$symb) {return '';}
2219: &Apache::loncommon::load_tmp_file($request);
2220: my @gradedata = &Apache::loncommon::upfile_record_sep();
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: }
2233: }
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 '';
2271: }
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 '';}
2295: my $result='<h3> <font color="#339933">Select a Grading Method</font></h3>';
2296: $result.='<table border="0">';
2297: $result.='<tr><td colspan=3><font size=+1><b>Problem: </b>'.
2298: &Apache::lonnet::metadata($ENV{'form.url'},'title').'</font></td></tr>';
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 />';
2313: $result.=&gradeByPage_form($symb,$url,$resptype,$hdgrade).'<br />';
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)=@_;
2323: my ($classlist,$sections,undef) = &getclasslist('all','0');
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";
2326: $result.=' <b>Grade Entire Section or Class</b></td></tr>'."\n";
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";
2333: if (ref($sections)) {
2334: foreach (sort (@$sections)) {
2335: $result.= '<option>'.$_.'</option>'."\n";
2336: }
2337: }
2338: $result.='<option selected="on">all</select>'."<br />\n";
2339: $result.=' <input type="button" onClick="submit();" value="Grade" /></form>'."\n";
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 '';}
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";
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
2370: <br /> <input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Grades" />
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";
2384: $result.=' <b>';
2385: $result.=($handgrade eq 'yes' ? 'View/Grade' : 'View').' an Individual Student\'s Submission</b></td></tr>'."\n";
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";
2394:
2395: $result.=' <b>Select section:</b> <select name="section">'."\n";
2396: if (ref($sections)) {
2397: foreach (sort (@$sections)) {$result.='<option>'.$_.'</option>'."\n";}
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 />';
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";
2416: $result.='</td></tr></table>'."\n";
2417: $result.='</td></tr></table>'."\n";
2418: return $result;
2419: }
2420:
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) {
2458: var user = radioSelection(formname.student);
2459: if (user == null) {
2460: alert("Please select the student you wish to grade.");
2461: return;
2462: }
2463: var ptr = pullDownSelection(formname.selectpage);
2464: formname.page.value = eval("formname.page"+ptr+".value");
2465: formname.title.value = eval("formname.title"+ptr+".value");
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: }
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: }
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";
2501: $result.=' <b>Problems from:</b> <select name="selectpage">'."\n";
2502: my ($titles,$symbx) = &getSymbMap();
2503: my ($curpage,$type,$mapId) = ($symb =~ /(.*?\.(page|sequence))___(\d+)___/);
2504: my $ctr=0;
2505: foreach (@$titles) {
2506: my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/);
2507: $result.='<option value="'.$ctr.'" '.
2508: ($$symbx{$_} =~ /$curpage$/ ? 'selected="on"' : '').
2509: '>'.$showtitle.'</option>'."\n";
2510: $ctr++;
2511: }
2512: $result.= '</select>'."<br>\n";
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";
2522:
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";
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>');
2548: $studentTable.='<input type="radio" name="student" value="'.$student.'" /> '.$$fullname{$student}.
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";
2557: $studentTable.='<br /> <input type="button" '.
2558: 'onClick="javascript:checkPickOne(this.form);"value="Submit" /></form>'."\n";
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()) {
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: }
2615: }
2616: $curRes = $iterator->next();
2617: }
2618:
2619: $navmap->untieHashes();
2620: return \@titles,\%symbx;
2621: }
2622:
2623: sub displayPage {
2624: my ($request) = shift;
2625:
2626: my ($symb,$url) = &get_symb_and_url();
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');
2632: my ($uname,$udom) = split(/:/,$ENV{'form.student'});
2633:
2634: my $result='<h3><font color="#339933"> '.$ENV{'form.title'}.'</font></h3>';
2635: $result.='<h3> Student: '.$$fullname{$ENV{'form.student'}}.
2636: '<font color="#999999"> ('.$uname.($udom eq $cdom ? '':':'.$udom).')</font></h3>'."\n";
2637:
2638: &sub_page_js($request);
2639: $request->print($result);
2640:
2641: my $navmap = Apache::lonnavmaps::navmap-> new(
2642: $ENV{'request.course.fn'}.'.db',
2643: $ENV{'request.course.fn'}.'_parms.db',1, 1);
2644: my ($mapUrl, $id, $resUrl) = split(/___/, $ENV{'form.page'});
2645: my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
2646:
2647: my $iterator = $navmap->getIterator($map->map_start(),
2648: $map->map_finish());
2649:
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);
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();
2677: if (scalar(@{$parts}) > 1) { shift @{$parts}; }
2678: my $title = $curRes->compTitle();
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>';
2746:
2747: }
2748: $curRes = $iterator->next();
2749: $ctr++;
2750: }
2751: $navmap->init();
2752:
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";
2776:
2777: $request->print($result);
2778:
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());
2787:
2788: my $studentTable='<table border="0"><tr><td bgcolor="#777777">'.
2789: '<table border="0"><tr bgcolor="#e6ffff">'.
2790: '<td align="center"><b> No </b></td>'.
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>';
2858:
2859: }
2860: $curRes = $iterator->next();
2861: $ctr++;
2862: }
2863: $navmap->init();
2864:
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'});
2870: $request->print($studentTable);
2871:
2872: return '';
2873: }
2874:
2875:
2876: #--- Form to input a receipt number ---
2877: sub verifyReceipt_form {
2878: my ($symb,$url) = @_;
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:
2890: my $hostver=unpack("%32C*",$Apache::lonnet::perlvar{'lonHostID'});
2891:
2892: $result.='<table width=100% border=0><tr><td bgcolor=#777777>'."\n";
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";
2896: $result.='<form action="/adm/grades" method="post" name="verifyform">'."\n";
2897: $result.=' <tt>'.$hostver.'-<input type="text" name="receipt" size="4"></tt><br />'."\n";
2898: $result.=' <input type="button" onClick="javascript:checkEntry(this.form);"'.
2899: ' value="Verify Receipt">'."\n";
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;
2911: }
2912:
2913: sub handler {
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;
2922: return '' if $request->header_only;
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 {
2951: $request->print('<h3>Not authorized: '.$token.'</h3>');
2952: }
2953: } else {
2954: $request->print('<h3>Not a valid DocID: '.$token.'</h3>');
2955: }
2956: } else {
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') {
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);
2971: } elsif ($command eq 'gradeByPage') {
2972: &updateGradeByPage($request);
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: }
3002: } else {
3003: $request->print("Unknown action: $command:");
3004: }
3005: }
3006: &send_footer($request);
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>");
3018: $request->print(&Apache::loncommon::bodytag('Grading'));
3019: }
3020:
3021: sub send_footer {
3022: my ($request)= @_;
3023: $request->print('</body>');
3024: $request->print(&Apache::lontexconvert::footer());
3025: }
3026:
3027: 1;
3028:
3029: __END__;
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>