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