![]() ![]() | ![]() |
will handle manual grading by page/sequence
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__;