![]() ![]() | ![]() |
- sort case insensitive the saved Code sets
1: # The LearningOnline Network with CAPA 2: # The LON-CAPA Grading handler 3: # 4: # $Id: grades.pm,v 1.225 2004/11/13 09:32:49 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: 29: package Apache::grades; 30: use strict; 31: use Apache::style; 32: use Apache::lonxml; 33: use Apache::lonnet; 34: use Apache::loncommon; 35: use Apache::lonhtmlcommon; 36: use Apache::lonnavmaps; 37: use Apache::lonhomework; 38: use Apache::loncoursedata; 39: use Apache::lonmsg qw(:user_normal_msg); 40: use Apache::Constants qw(:common); 41: use Apache::lonlocal; 42: use String::Similarity; 43: 44: my %oldessays=(); 45: my %perm=(); 46: 47: # ----- These first few routines are general use routines.---- 48: # 49: # --- Retrieve the parts from the metadata file.--- 50: sub getpartlist { 51: my ($url,$symb) = @_; 52: my $partorder = &Apache::lonnet::metadata($url, 'partorder'); 53: my @parts; 54: if ($partorder) { 55: for my $part (split (/,/,$partorder)) { 56: if (!&Apache::loncommon::check_if_partid_hidden($part,$symb)) { 57: push(@parts, $part); 58: } 59: } 60: } else { 61: my $metadata = &Apache::lonnet::metadata($url, 'packages'); 62: foreach (split(/\,/,$metadata)) { 63: if ($_ =~ /^part_(.*)$/) { 64: if (!&Apache::loncommon::check_if_partid_hidden($1,$symb)) { 65: push(@parts, $1); 66: } 67: } 68: } 69: } 70: my @stores; 71: foreach my $part (@parts) { 72: my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys')); 73: foreach my $key (@metakeys) { 74: if ($key =~ m/^stores_\Q$part\E_/) { push(@stores,$key); } 75: } 76: } 77: return @stores; 78: } 79: 80: # --- Get the symbolic name of a problem and the url 81: sub get_symb_and_url { 82: my ($request,$silent) = @_; 83: (my $url=$ENV{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--; 84: my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url))); 85: if ($symb eq '') { 86: if (!$silent) { 87: $request->print("Unable to handle ambiguous references:$url:."); 88: return (); 89: } 90: } 91: return ($symb,$url); 92: } 93: 94: #--- Format fullname, username:domain if different for display 95: #--- Use anywhere where the student names are listed 96: sub nameUserString { 97: my ($type,$fullname,$uname,$udom) = @_; 98: if ($type eq 'header') { 99: return '<b> Fullname </b><font color="#999999">(Username)</font> '; 100: } else { 101: return ' '.$fullname.'<font color="#999999"> ('.$uname. 102: ($ENV{'user.domain'} eq $udom ? '' : ' ('.$udom.')').')</font>'; 103: } 104: } 105: 106: #--- Get the partlist and the response type for a given problem. --- 107: #--- Indicate if a response type is coded handgraded or not. --- 108: sub response_type { 109: my ($url,$symb) = shift; 110: $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url))) if ($symb eq ''); 111: my $allkeys = &Apache::lonnet::metadata($url,'keys'); 112: my %vPart; 113: foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) { 114: $vPart{$partid}=1; 115: } 116: my %seen = (); 117: my (@partlist,%handgrade,%responseType); 118: foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) { 119: if (/^\w+response_.*/) { 120: my ($responsetype,$part) = split(/_/,$_,2); 121: my ($partid,$respid) = split(/_/,$part); 122: if (&Apache::loncommon::check_if_partid_hidden($partid,$symb)) { 123: next; 124: } 125: if (%vPart && !exists($vPart{$partid})) { 126: next; 127: } 128: $responsetype =~ s/response$//; # make it compatible w/ navmaps - should move to that!! 129: my ($value) = &Apache::lonnet::EXT('resource.'.$part.'.handgrade',$symb); 130: $handgrade{$part} = ($value eq 'yes' ? 'yes' : 'no'); 131: if (!exists($responseType{$partid})) { $responseType{$partid}={}; } 132: $responseType{$partid}->{$respid}=$responsetype; 133: next if ($seen{$partid} > 0); 134: $seen{$partid}++; 135: push @partlist,$partid; 136: } 137: } 138: return \@partlist,\%handgrade,\%responseType; 139: } 140: 141: sub get_display_part { 142: my ($partID,$url,$symb)=@_; 143: if (!defined($symb) || $symb eq '') { 144: $symb=$ENV{'form.symb'}; 145: if ($symb eq '') { $symb=&Apache::lonnet::symbread($url) } 146: } 147: my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',$symb); 148: if (defined($display) and $display ne '') { 149: $display.= " (<font color=\"#999900\">id $partID</font>)"; 150: } else { 151: $display=$partID; 152: } 153: return $display; 154: } 155: #--- Show resource title 156: #--- and parts and response type 157: sub showResourceInfo { 158: my ($url,$probTitle,$checkboxes) = @_; 159: my $col=3; 160: if ($checkboxes) { $col=4; } 161: my $result ='<table border="0">'. 162: '<tr><td colspan="'.$col.'"><font size="+1"><b>'.&mt('Current Resource').': </b>'. 163: $probTitle.'</font></td></tr>'."\n"; 164: my ($partlist,$handgrade,$responseType) = &response_type($url); 165: my %resptype = (); 166: my $hdgrade='no'; 167: my %partsseen; 168: for my $part_resID (sort keys(%$handgrade)) { 169: my $handgrade=$$handgrade{$part_resID}; 170: my ($partID,$resID) = split(/_/,$part_resID); 171: my $responsetype = $responseType->{$partID}->{$resID}; 172: $hdgrade = $handgrade if ($handgrade eq 'yes'); 173: $result.='<tr>'; 174: if ($checkboxes) { 175: if (exists($partsseen{$partID})) { 176: $result.="<td> </td>"; 177: } else { 178: $result.="<td><input type='checkbox' name='vPart' value='$partID' checked='on' /></td>"; 179: } 180: $partsseen{$partID}=1; 181: } 182: my $display_part=&get_display_part($partID,$url); 183: $result.='<td><b>Part: </b>'.$display_part.' <font color="#999999">'. 184: $resID.'</font></td>'. 185: '<td><b>Type: </b>'.$responsetype.'</td></tr>'; 186: # '<td><b>Handgrade: </b>'.$handgrade.'</td></tr>'; 187: } 188: $result.='</table>'."\n"; 189: return $result,$responseType,$hdgrade,$partlist,$handgrade; 190: } 191: 192: 193: sub get_order { 194: my ($partid,$respid,$symb,$uname,$udom)=@_; 195: my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb); 196: $url=&Apache::lonnet::clutter($url); 197: my $subresult=&Apache::lonnet::ssi($url, 198: ('grade_target' => 'analyze'), 199: ('grade_domain' => $udom), 200: ('grade_symb' => $symb), 201: ('grade_courseid' => 202: $ENV{'request.course.id'}), 203: ('grade_username' => $uname)); 204: (undef,$subresult)=split(/_HASH_REF__/,$subresult,2); 205: my %analyze=&Apache::lonnet::str2hash($subresult); 206: return ($analyze{"$partid.$respid.shown"}); 207: } 208: #--- Clean response type for display 209: #--- Currently filters option/rank/radiobutton/match/essay response types only. 210: sub cleanRecord { 211: my ($answer,$response,$symb,$partid,$respid,$record,$order,$version) = @_; 212: my $grayFont = '<font color="#999999">'; 213: if ($response =~ /^(option|rank)$/) { 214: my %answer=&Apache::lonnet::str2hash($answer); 215: my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"}); 216: my ($toprow,$bottomrow); 217: foreach my $foil (@$order) { 218: if ($grading{$foil} == 1) { 219: $toprow.='<td><b>'.$answer{$foil}.' </b></td>'; 220: } else { 221: $toprow.='<td><i>'.$answer{$foil}.' </i></td>'; 222: } 223: $bottomrow.='<td>'.$grayFont.$foil.'</font> </td>'; 224: } 225: return '<blockquote><table border="1">'. 226: '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'. 227: '<tr valign="top"><td>'.$grayFont.'Option ID</font></td>'. 228: $grayFont.$bottomrow.'</tr>'.'</table></blockquote>'; 229: } elsif ($response eq 'match') { 230: my %answer=&Apache::lonnet::str2hash($answer); 231: my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"}); 232: my @items=&Apache::lonnet::str2array($record->{$version."resource.$partid.$respid.submissionitems"}); 233: my ($toprow,$middlerow,$bottomrow); 234: foreach my $foil (@$order) { 235: my $item=shift(@items); 236: if ($grading{$foil} == 1) { 237: $toprow.='<td><b>'.$item.' </b></td>'; 238: $middlerow.='<td><b>'.$grayFont.$answer{$foil}.' </font></b></td>'; 239: } else { 240: $toprow.='<td><i>'.$item.' </i></td>'; 241: $middlerow.='<td><i>'.$grayFont.$answer{$foil}.' </font></i></td>'; 242: } 243: $bottomrow.='<td>'.$grayFont.$foil.'</font> </td>'; 244: } 245: return '<blockquote><table border="1">'. 246: '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'. 247: '<tr valign="top"><td>'.$grayFont.'Item ID</font></td>'. 248: $middlerow.'</tr>'. 249: '<tr valign="top"><td>'.$grayFont.'Option ID</font></td>'. 250: $bottomrow.'</tr>'.'</table></blockquote>'; 251: } elsif ($response eq 'radiobutton') { 252: my %answer=&Apache::lonnet::str2hash($answer); 253: my ($toprow,$bottomrow); 254: my $correct=($order->[0])+1; 255: for (my $i=1;$i<=$#$order;$i++) { 256: my $foil=$order->[$i]; 257: if (exists($answer{$foil})) { 258: if ($i == $correct) { 259: $toprow.='<td><b>true</b></td>'; 260: } else { 261: $toprow.='<td><i>true</i></td>'; 262: } 263: } else { 264: $toprow.='<td>false</td>'; 265: } 266: $bottomrow.='<td>'.$grayFont.$foil.'</font> </td>'; 267: } 268: return '<blockquote><table border="1">'. 269: '<tr valign="top"><td>Answer</td>'.$toprow.'</tr>'. 270: '<tr valign="top"><td>'.$grayFont.'Option ID</font></td>'. 271: $grayFont.$bottomrow.'</tr>'.'</table></blockquote>'; 272: } elsif ($response eq 'essay') { 273: if (! exists ($ENV{'form.'.$symb})) { 274: my (%keyhash) = &Apache::lonnet::dump('nohist_handgrade', 275: $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}, 276: $ENV{'course.'.$ENV{'request.course.id'}.'.num'}); 277: 278: my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'}; 279: $ENV{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : ''; 280: $ENV{'form.kwclr'} = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red'; 281: $ENV{'form.kwsize'} = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0'; 282: $ENV{'form.kwstyle'} = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : ''; 283: $ENV{'form.'.$symb} = 1; # so that we don't have to read it from disk for multiple sub of the same prob. 284: } 285: $answer =~ s-\n-<br />-g; 286: return '<br /><br /><blockquote><tt>'.&keywords_highlight($answer).'</tt></blockquote>'; 287: } 288: return $answer; 289: } 290: 291: #-- A couple of common js functions 292: sub commonJSfunctions { 293: my $request = shift; 294: $request->print(<<COMMONJSFUNCTIONS); 295: <script type="text/javascript" language="javascript"> 296: function radioSelection(radioButton) { 297: var selection=null; 298: if (radioButton.length > 1) { 299: for (var i=0; i<radioButton.length; i++) { 300: if (radioButton[i].checked) { 301: return radioButton[i].value; 302: } 303: } 304: } else { 305: if (radioButton.checked) return radioButton.value; 306: } 307: return selection; 308: } 309: 310: function pullDownSelection(selectOne) { 311: var selection=""; 312: if (selectOne.length > 1) { 313: for (var i=0; i<selectOne.length; i++) { 314: if (selectOne[i].selected) { 315: return selectOne[i].value; 316: } 317: } 318: } else { 319: // only one value it must be the selected one 320: return selectOne.value; 321: } 322: } 323: </script> 324: COMMONJSFUNCTIONS 325: } 326: 327: #--- Dumps the class list with usernames,list of sections, 328: #--- section, ids and fullnames for each user. 329: sub getclasslist { 330: my ($getsec,$filterlist) = @_; 331: $getsec = $getsec eq '' ? 'all' : $getsec; 332: my $classlist=&Apache::loncoursedata::get_classlist(); 333: # Bail out if we were unable to get the classlist 334: return if (! defined($classlist)); 335: # 336: my %sections; 337: my %fullnames; 338: foreach my $student (keys(%$classlist)) { 339: my $end = 340: $classlist->{$student}->[&Apache::loncoursedata::CL_END()]; 341: my $start = 342: $classlist->{$student}->[&Apache::loncoursedata::CL_START()]; 343: my $id = 344: $classlist->{$student}->[&Apache::loncoursedata::CL_ID()]; 345: my $section = 346: $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()]; 347: my $fullname = 348: $classlist->{$student}->[&Apache::loncoursedata::CL_FULLNAME()]; 349: my $status = 350: $classlist->{$student}->[&Apache::loncoursedata::CL_STATUS()]; 351: # filter students according to status selected 352: if ($filterlist && $ENV{'form.Status'} ne 'Any') { 353: if ($ENV{'form.Status'} ne $status) { 354: delete ($classlist->{$student}); 355: next; 356: } 357: } 358: $section = ($section ne '' ? $section : 'none'); 359: if (&canview($section)) { 360: if ($getsec eq 'all' || $getsec eq $section) { 361: $sections{$section}++; 362: $fullnames{$student}=$fullname; 363: } else { 364: delete($classlist->{$student}); 365: } 366: } else { 367: delete($classlist->{$student}); 368: } 369: } 370: my %seen = (); 371: my @sections = sort(keys(%sections)); 372: return ($classlist,\@sections,\%fullnames); 373: } 374: 375: sub canmodify { 376: my ($sec)=@_; 377: if ($perm{'mgr'}) { 378: if (!defined($perm{'mgr_section'})) { 379: # can modify whole class 380: return 1; 381: } else { 382: if ($sec eq $perm{'mgr_section'}) { 383: #can modify the requested section 384: return 1; 385: } else { 386: # can't modify the request section 387: return 0; 388: } 389: } 390: } 391: #can't modify 392: return 0; 393: } 394: 395: sub canview { 396: my ($sec)=@_; 397: if ($perm{'vgr'}) { 398: if (!defined($perm{'vgr_section'})) { 399: # can modify whole class 400: return 1; 401: } else { 402: if ($sec eq $perm{'vgr_section'}) { 403: #can modify the requested section 404: return 1; 405: } else { 406: # can't modify the request section 407: return 0; 408: } 409: } 410: } 411: #can't modify 412: return 0; 413: } 414: 415: #--- Retrieve the grade status of a student for all the parts 416: sub student_gradeStatus { 417: my ($url,$symb,$udom,$uname,$partlist) = @_; 418: my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname); 419: my %partstatus = (); 420: foreach (@$partlist) { 421: my ($status,undef) = split(/_/,$record{"resource.$_.solved"},2); 422: $status = 'nothing' if ($status eq ''); 423: $partstatus{$_} = $status; 424: my $subkey = "resource.$_.submitted_by"; 425: $partstatus{$subkey} = $record{$subkey} if ($record{$subkey} ne ''); 426: } 427: return %partstatus; 428: } 429: 430: # hidden form and javascript that calls the form 431: # Use by verifyscript and viewgrades 432: # Shows a student's view of problem and submission 433: sub jscriptNform { 434: my ($url,$symb) = @_; 435: my $jscript='<script type="text/javascript" language="javascript">'."\n". 436: ' function viewOneStudent(user,domain) {'."\n". 437: ' document.onestudent.student.value = user;'."\n". 438: ' document.onestudent.userdom.value = domain;'."\n". 439: ' document.onestudent.submit();'."\n". 440: ' }'."\n". 441: '</script>'."\n"; 442: $jscript.= '<form action="/adm/grades" method="post" name="onestudent">'."\n". 443: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n". 444: '<input type="hidden" name="url" value="'.$url.'" />'."\n". 445: '<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."\n". 446: '<input type="hidden" name="probTitle" value="'.$ENV{'form.probTitle'}.'" />'."\n". 447: '<input type="hidden" name="Status" value="'.$ENV{'form.Status'}.'" />'."\n". 448: '<input type="hidden" name="command" value="submission" />'."\n". 449: '<input type="hidden" name="student" value="" />'."\n". 450: '<input type="hidden" name="userdom" value="" />'."\n". 451: '</form>'."\n"; 452: return $jscript; 453: } 454: 455: #------------------ End of general use routines -------------------- 456: 457: # 458: # Find most similar essay 459: # 460: 461: sub most_similar { 462: my ($uname,$udom,$uessay)=@_; 463: 464: # ignore spaces and punctuation 465: 466: $uessay=~s/\W+/ /gs; 467: 468: # these will be returned. Do not care if not at least 50 percent similar 469: my $limit=0.6; 470: my $sname=''; 471: my $sdom=''; 472: my $scrsid=''; 473: my $sessay=''; 474: # go through all essays ... 475: foreach my $tkey (keys %oldessays) { 476: my ($tname,$tdom,$tcrsid)=split(/\./,$tkey); 477: # ... except the same student 478: if (($tname ne $uname) || ($tdom ne $udom)) { 479: my $tessay=$oldessays{$tkey}; 480: $tessay=~s/\W+/ /gs; 481: # String similarity gives up if not even limit 482: my $tsimilar=&String::Similarity::similarity($uessay,$tessay,$limit); 483: # Found one 484: if ($tsimilar>$limit) { 485: $limit=$tsimilar; 486: $sname=$tname; 487: $sdom=$tdom; 488: $scrsid=$tcrsid; 489: $sessay=$oldessays{$tkey}; 490: } 491: } 492: } 493: if ($limit>0.6) { 494: return ($sname,$sdom,$scrsid,$sessay,$limit); 495: } else { 496: return ('','','','',0); 497: } 498: } 499: 500: #------------------------------------------------------------------- 501: 502: #------------------------------------ Receipt Verification Routines 503: # 504: #--- Check whether a receipt number is valid.--- 505: sub verifyreceipt { 506: my $request = shift; 507: 508: my $courseid = $ENV{'request.course.id'}; 509: my $receipt = &Apache::lonnet::recprefix($courseid).'-'. 510: $ENV{'form.receipt'}; 511: $receipt =~ s/[^\-\d]//g; 512: my $url = $ENV{'form.url'}; 513: my $symb = $ENV{'form.symb'}; 514: unless ($symb) { 515: $symb = &Apache::lonnet::symbread($url); 516: } 517: 518: my $title.='<h3><font color="#339933">Verifying Submission Receipt '. 519: $receipt.'</h3></font>'."\n". 520: '<font size=+1><b>Resource: </b>'.$ENV{'form.probTitle'}.'</font><br><br>'."\n"; 521: 522: my ($string,$contents,$matches) = ('','',0); 523: my (undef,undef,$fullname) = &getclasslist('all','0'); 524: 525: my $receiptparts=0; 526: if ($ENV{"course.$courseid.receiptalg"} eq 'receipt2') { $receiptparts=1; } 527: my $parts=['0']; 528: if ($receiptparts) { ($parts)=&response_type($url,$symb); } 529: foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) { 530: my ($uname,$udom)=split(/\:/); 531: foreach my $part (@$parts) { 532: if ($receipt eq &Apache::lonnet::ireceipt($uname,$udom,$courseid,$symb,$part)) { 533: $contents.='<tr bgcolor="#ffffe6"><td> '."\n". 534: '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom. 535: '\')"; TARGET=_self>'.$$fullname{$_}.'</a> </td>'."\n". 536: '<td> '.$uname.' </td>'. 537: '<td> '.$udom.' </td>'; 538: if ($receiptparts) { 539: $contents.='<td> '.$part.' </td>'; 540: } 541: $contents.='</tr>'."\n"; 542: 543: $matches++; 544: } 545: } 546: } 547: if ($matches == 0) { 548: $string = $title.'No match found for the above receipt.'; 549: } else { 550: $string = &jscriptNform($url,$symb).$title. 551: 'The above receipt matches the following student'. 552: ($matches <= 1 ? '.' : 's.')."\n". 553: '<table border="0"><tr><td bgcolor="#777777">'."\n". 554: '<table border="0"><tr bgcolor="#e6ffff">'."\n". 555: '<td><b> Fullname </b></td>'."\n". 556: '<td><b> Username </b></td>'."\n". 557: '<td><b> Domain </b></td>'; 558: if ($receiptparts) { 559: $string.='<td> Problem Part </td>'; 560: } 561: $string.='</tr>'."\n".$contents. 562: '</table></td></tr></table>'."\n"; 563: } 564: return $string.&show_grading_menu_form($symb,$url); 565: } 566: 567: #--- This is called by a number of programs. 568: #--- Called from the Grading Menu - View/Grade an individual student 569: #--- Also called directly when one clicks on the subm button 570: # on the problem page. 571: sub listStudents { 572: my ($request) = shift; 573: 574: my ($symb,$url) = &get_symb_and_url($request); 575: my $cdom = $ENV{"course.$ENV{'request.course.id'}.domain"}; 576: my $cnum = $ENV{"course.$ENV{'request.course.id'}.num"}; 577: my $getsec = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'}; 578: my $submitonly= $ENV{'form.submitonly'} eq '' ? 'all' : $ENV{'form.submitonly'}; 579: 580: my $viewgrade = $ENV{'form.showgrading'} eq 'yes' ? 'View/Grade/Regrade' : 'View'; 581: $ENV{'form.probTitle'} = $ENV{'form.probTitle'} eq '' ? 582: &Apache::lonnet::gettitle($symb) : $ENV{'form.probTitle'}; 583: 584: my $result='<h3><font color="#339933"> '.$viewgrade. 585: ' Submissions for a Student or a Group of Students</font></h3>'; 586: 587: my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($url,$ENV{'form.probTitle'},($ENV{'form.showgrading'} eq 'yes')); 588: 589: $request->print(<<LISTJAVASCRIPT); 590: <script type="text/javascript" language="javascript"> 591: function checkSelect(checkBox) { 592: var ctr=0; 593: var sense=""; 594: if (checkBox.length > 1) { 595: for (var i=0; i<checkBox.length; i++) { 596: if (checkBox[i].checked) { 597: ctr++; 598: } 599: } 600: sense = "a student or group of students"; 601: } else { 602: if (checkBox.checked) { 603: ctr = 1; 604: } 605: sense = "the student"; 606: } 607: if (ctr == 0) { 608: alert("Please select "+sense+" before clicking on the Next button."); 609: return false; 610: } 611: document.gradesub.submit(); 612: } 613: 614: function reLoadList(formname) { 615: if (formname.saveStatusOld.value == pullDownSelection(formname.Status)) {return;} 616: formname.command.value = 'submission'; 617: formname.submit(); 618: } 619: </script> 620: LISTJAVASCRIPT 621: 622: &commonJSfunctions($request); 623: $request->print($result); 624: 625: my $checkhdgrade = ($ENV{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked' : ''; 626: my $checklastsub = $checkhdgrade eq '' ? 'checked' : ''; 627: my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'. 628: "\n".$table. 629: ' <b>View Problem Text: </b><input type="radio" name="vProb" value="no" checked="on" /> no '."\n". 630: '<input type="radio" name="vProb" value="yes" /> one student '."\n". 631: '<input type="radio" name="vProb" value="all" /> all students <br />'."\n". 632: ' <b>View Answer: </b><input type="radio" name="vAns" value="no" /> no '."\n". 633: '<input type="radio" name="vAns" value="yes" /> one student '."\n". 634: '<input type="radio" name="vAns" value="all" checked="on" /> all students <br />'."\n". 635: ' <b>Submissions: </b>'."\n"; 636: if ($ENV{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) { 637: $gradeTable.='<input type="radio" name="lastSub" value="hdgrade" '.$checkhdgrade.' /> essay part only'."\n"; 638: } 639: 640: my $saveStatus = $ENV{'form.Status'} eq '' ? 'Active' : $ENV{'form.Status'}; 641: $ENV{'form.Status'} = $saveStatus; 642: 643: $gradeTable.='<input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> last submission only'."\n". 644: '<input type="radio" name="lastSub" value="last" /> last submission & parts info'."\n". 645: '<input type="radio" name="lastSub" value="datesub" /> by dates and submissions'."\n". 646: '<input type="radio" name="lastSub" value="all" /> all details'."\n". 647: '<input type="hidden" name="section" value="'.$getsec.'" />'."\n". 648: '<input type="hidden" name="submitonly" value="'.$submitonly.'" />'."\n". 649: '<input type="hidden" name="handgrade" value="'.$ENV{'form.handgrade'}.'" /><br />'."\n". 650: '<input type="hidden" name="showgrading" value="'.$ENV{'form.showgrading'}.'" /><br />'."\n". 651: '<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."\n". 652: '<input type="hidden" name="probTitle" value="'.$ENV{'form.probTitle'}.'" />'."\n". 653: '<input type="hidden" name="url" value="'.$url.'" />'."\n". 654: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n". 655: '<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n"; 656: 657: if (exists($ENV{'form.gradingMenu'}) && exists($ENV{'form.Status'})) { 658: $gradeTable.='<input type="hidden" name="Status" value="'.$ENV{'form.Status'}.'" />'."\n"; 659: } else { 660: $gradeTable.='<b>Student Status:</b> '. 661: &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);').'<br />'; 662: } 663: 664: $gradeTable.='To '.lc($viewgrade).' a submission or a group of submissions, click on the check box(es) '. 665: 'next to the student\'s name(s). Then click on the Next button.<br />'."\n". 666: '<input type="hidden" name="command" value="processGroup" />'."\n"; 667: $gradeTable.='<input type="button" '."\n". 668: 'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n". 669: 'value="Next->" />'."\n"; 670: $gradeTable.='<input type="checkbox" name="checkPlag" checked="on">Check For Plagiarism</input>'; 671: my (undef, undef, $fullname) = &getclasslist($getsec,'1'); 672: $gradeTable.='<table border="0"><tr><td bgcolor="#777777">'. 673: '<table border="0"><tr bgcolor="#e6ffff">'; 674: my $loop = 0; 675: while ($loop < 2) { 676: $gradeTable.='<td><b> No.</b> </td><td><b> Select </b></td>'. 677: '<td>'.&nameUserString('header').'</td>'; 678: if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { 679: foreach (sort(@$partlist)) { 680: my $display_part=&get_display_part((split(/_/))[0],$url,$symb); 681: $gradeTable.='<td><b> Part: '.$display_part. 682: ' Status </b></td>'; 683: } 684: } 685: $loop++; 686: # $gradeTable.='<td></td>' if ($loop%2 ==1); 687: } 688: $gradeTable.='</tr>'."\n"; 689: 690: my $ctr = 0; 691: foreach my $student (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) { 692: my ($uname,$udom) = split(/:/,$student); 693: my %status = (); 694: if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { 695: (%status) =&student_gradeStatus($url,$symb,$udom,$uname,$partlist); 696: my $submitted = 0; 697: my $graded = 0; 698: foreach (keys(%status)) { 699: $submitted = 1 if ($status{$_} ne 'nothing'); 700: $graded = 1 if ($status{$_} !~ /^correct/); 701: 702: my ($foo,$partid,$foo1) = split(/\./,$_); 703: if ($status{'resource.'.$partid.'.submitted_by'} ne '') { 704: $submitted = 0; 705: my ($part)=split(/\./,$partid); 706: $gradeTable.='<input type="hidden" name="'. 707: $student.':'.$part.':submitted_by" value="'. 708: $status{'resource.'.$partid.'.submitted_by'}.'" />'; 709: } 710: } 711: next if (!$submitted && ($submitonly eq 'yes' || 712: $submitonly eq 'incorrect' || 713: $submitonly eq 'graded')); 714: next if (!$graded && ($submitonly eq 'graded' || 715: $submitonly eq 'incorrect')); 716: } 717: 718: $ctr++; 719: if ( $perm{'vgr'} eq 'F' ) { 720: $gradeTable.='<tr bgcolor="#ffffe6">' if ($ctr%2 ==1); 721: $gradeTable.='<td align="right">'.$ctr.' </td>'. 722: '<td align="center"><input type=checkbox name="stuinfo" value="'. 723: $student.':'.$$fullname{$student}.' "></td>'."\n". 724: '<td>'.&nameUserString(undef,$$fullname{$student},$uname,$udom).'</td>'."\n"; 725: 726: if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { 727: foreach (sort keys(%status)) { 728: next if (/^resource.*?submitted_by$/); 729: $gradeTable.='<td align="middle"> '.$status{$_}.' </td>'."\n"; 730: } 731: } 732: # $gradeTable.='<td></td>' if ($ctr%2 ==1); 733: $gradeTable.='</tr>'."\n" if ($ctr%2 ==0); 734: } 735: } 736: if ($ctr%2 ==1) { 737: $gradeTable.='<td> </td><td> </td><td> </td>'; 738: if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { 739: foreach (@$partlist) { 740: $gradeTable.='<td> </td>'; 741: } 742: } 743: $gradeTable.='</tr>'; 744: } 745: 746: $gradeTable.='</table></td></tr></table>'. 747: '<input type="button" '. 748: 'onClick="javascript:checkSelect(this.form.stuinfo);" '. 749: 'value="Next->" /></form>'."\n"; 750: if ($ctr == 0) { 751: my $num_students=(scalar(keys(%$fullname))); 752: if ($num_students eq 0) { 753: $gradeTable='<br /> <font color="red">There are no students currently enrolled.</font>'; 754: } else { 755: my $submissions='submissions'; 756: if ($submitonly eq 'incorrect') { $submissions = 'incorrect submissions'; } 757: if ($submitonly eq 'graded' ) { $submissions = 'ungraded submissions'; } 758: $gradeTable='<br /> <font color="red">'. 759: 'No '.$submissions.' found for this resource for any students. ('.$num_students. 760: ' students checked for '.$submissions.')</font><br />'; 761: } 762: } elsif ($ctr == 1) { 763: $gradeTable =~ s/type=checkbox/type=checkbox checked/; 764: } 765: $gradeTable.=&show_grading_menu_form($symb,$url); 766: $request->print($gradeTable); 767: return ''; 768: } 769: 770: #---- Called from the listStudents routine 771: # Displays the submissions for one student or a group of students 772: sub processGroup { 773: my ($request) = shift; 774: my $ctr = 0; 775: my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo'); 776: my $total = scalar(@stuchecked)-1; 777: 778: foreach (@stuchecked) { 779: my ($uname,$udom,$fullname) = split(/:/); 780: $ENV{'form.student'} = $uname; 781: $ENV{'form.userdom'} = $udom; 782: $ENV{'form.fullname'} = $fullname; 783: &submission($request,$ctr,$total); 784: $ctr++; 785: } 786: return ''; 787: } 788: 789: #------------------------------------------------------------------------------------ 790: # 791: #-------------------------- Next few routines handles grading by student, essentially 792: # handles essay response type problem/part 793: # 794: #--- Javascript to handle the submission page functionality --- 795: sub sub_page_js { 796: my $request = shift; 797: $request->print(<<SUBJAVASCRIPT); 798: <script type="text/javascript" language="javascript"> 799: function updateRadio(formname,id,weight) { 800: var gradeBox = formname["GD_BOX"+id]; 801: var radioButton = formname["RADVAL"+id]; 802: var oldpts = formname["oldpts"+id].value; 803: var pts = checkSolved(formname,id) == 'update' ? gradeBox.value : oldpts; 804: gradeBox.value = pts; 805: var resetbox = false; 806: if (isNaN(pts) || pts < 0) { 807: alert("A number equal or greater than 0 is expected. Entered value = "+pts); 808: for (var i=0; i<radioButton.length; i++) { 809: if (radioButton[i].checked) { 810: gradeBox.value = i; 811: resetbox = true; 812: } 813: } 814: if (!resetbox) { 815: formtextbox.value = ""; 816: } 817: return; 818: } 819: 820: if (pts > weight) { 821: var resp = confirm("You entered a value ("+pts+ 822: ") greater than the weight for the part. Accept?"); 823: if (resp == false) { 824: gradeBox.value = oldpts; 825: return; 826: } 827: } 828: 829: for (var i=0; i<radioButton.length; i++) { 830: radioButton[i].checked=false; 831: if (pts == i && pts != "") { 832: radioButton[i].checked=true; 833: } 834: } 835: updateSelect(formname,id); 836: formname["stores"+id].value = "0"; 837: } 838: 839: function writeBox(formname,id,pts) { 840: var gradeBox = formname["GD_BOX"+id]; 841: if (checkSolved(formname,id) == 'update') { 842: gradeBox.value = pts; 843: } else { 844: var oldpts = formname["oldpts"+id].value; 845: gradeBox.value = oldpts; 846: var radioButton = formname["RADVAL"+id]; 847: for (var i=0; i<radioButton.length; i++) { 848: radioButton[i].checked=false; 849: if (i == oldpts) { 850: radioButton[i].checked=true; 851: } 852: } 853: } 854: formname["stores"+id].value = "0"; 855: updateSelect(formname,id); 856: return; 857: } 858: 859: function clearRadBox(formname,id) { 860: if (checkSolved(formname,id) == 'noupdate') { 861: updateSelect(formname,id); 862: return; 863: } 864: gradeSelect = formname["GD_SEL"+id]; 865: for (var i=0; i<gradeSelect.length; i++) { 866: if (gradeSelect[i].selected) { 867: var selectx=i; 868: } 869: } 870: var stores = formname["stores"+id]; 871: if (selectx == stores.value) { return }; 872: var gradeBox = formname["GD_BOX"+id]; 873: gradeBox.value = ""; 874: var radioButton = formname["RADVAL"+id]; 875: for (var i=0; i<radioButton.length; i++) { 876: radioButton[i].checked=false; 877: } 878: stores.value = selectx; 879: } 880: 881: function checkSolved(formname,id) { 882: if (formname["solved"+id].value == "correct_by_student" && formname.overRideScore.value == 'no') { 883: var reply = confirm("This problem has been graded correct by the computer. Do you want to change the score?"); 884: if (!reply) {return "noupdate";} 885: formname.overRideScore.value = 'yes'; 886: } 887: return "update"; 888: } 889: 890: function updateSelect(formname,id) { 891: formname["GD_SEL"+id][0].selected = true; 892: return; 893: } 894: 895: //=========== Check that a point is assigned for all the parts ============ 896: function checksubmit(formname,val,total,parttot) { 897: formname.gradeOpt.value = val; 898: if (val == "Save & Next") { 899: for (i=0;i<=total;i++) { 900: for (j=0;j<parttot;j++) { 901: var partid = formname["partid"+i+"_"+j].value; 902: if (formname["GD_SEL"+i+"_"+partid][0].selected) { 903: var points = formname["GD_BOX"+i+"_"+partid].value; 904: if (points == "") { 905: var name = formname["name"+i].value; 906: var studentID = (name != '' ? name : formname["unamedom"+i].value); 907: var resp = confirm("You did not assign a score for "+studentID+ 908: ", part "+partid+". Continue?"); 909: if (resp == false) { 910: formname["GD_BOX"+i+"_"+partid].focus(); 911: return false; 912: } 913: } 914: } 915: 916: } 917: } 918: 919: } 920: if (val == "Grade Student") { 921: formname.showgrading.value = "yes"; 922: if (formname.Status.value == "") { 923: formname.Status.value = "Active"; 924: } 925: formname.studentNo.value = total; 926: } 927: formname.submit(); 928: } 929: 930: //======= Check that a score is assigned for all the problems (page/sequence grading only) ========= 931: function checkSubmitPage(formname,total) { 932: noscore = new Array(100); 933: var ptr = 0; 934: for (i=1;i<total;i++) { 935: var partid = formname["q_"+i].value; 936: if (formname["GD_SEL"+i+"_"+partid][0].selected) { 937: var points = formname["GD_BOX"+i+"_"+partid].value; 938: var status = formname["solved"+i+"_"+partid].value; 939: if (points == "" && status != "correct_by_student") { 940: noscore[ptr] = i; 941: ptr++; 942: } 943: } 944: } 945: if (ptr != 0) { 946: var sense = ptr == 1 ? ": " : "s: "; 947: var prolist = ""; 948: if (ptr == 1) { 949: prolist = noscore[0]; 950: } else { 951: var i = 0; 952: while (i < ptr-1) { 953: prolist += noscore[i]+", "; 954: i++; 955: } 956: prolist += "and "+noscore[i]; 957: } 958: var resp = confirm("You did not assign any score for the following problem"+sense+prolist+". Continue?"); 959: if (resp == false) { 960: return false; 961: } 962: } 963: 964: formname.submit(); 965: } 966: </script> 967: SUBJAVASCRIPT 968: } 969: 970: #--- javascript for essay type problem -- 971: sub sub_page_kw_js { 972: my $request = shift; 973: my $iconpath = $request->dir_config('lonIconsURL'); 974: &commonJSfunctions($request); 975: my $docopen=&Apache::lonhtmlcommon::javascript_docopen(); 976: $request->print(<<SUBJAVASCRIPT); 977: <script type="text/javascript" language="javascript"> 978: 979: //===================== Show list of keywords ==================== 980: function keywords(formname) { 981: var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",formname.keywords.value); 982: if (nret==null) return; 983: formname.keywords.value = nret; 984: 985: if (formname.keywords.value != "") { 986: formname.refresh.value = "on"; 987: formname.submit(); 988: } 989: return; 990: } 991: 992: //===================== Script to view submitted by ================== 993: function viewSubmitter(submitter) { 994: document.SCORE.refresh.value = "on"; 995: document.SCORE.NCT.value = "1"; 996: document.SCORE.unamedom0.value = submitter; 997: document.SCORE.submit(); 998: return; 999: } 1000: 1001: //===================== Script to add keyword(s) ================== 1002: function getSel() { 1003: if (document.getSelection) txt = document.getSelection(); 1004: else if (document.selection) txt = document.selection.createRange().text; 1005: else return; 1006: var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," "); 1007: if (cleantxt=="") { 1008: alert("Please select a word or group of words from document and then click this link."); 1009: return; 1010: } 1011: var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt); 1012: if (nret==null) return; 1013: document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret; 1014: if (document.SCORE.keywords.value != "") { 1015: document.SCORE.refresh.value = "on"; 1016: document.SCORE.submit(); 1017: } 1018: return; 1019: } 1020: 1021: //====================== Script for composing message ============== 1022: // preload images 1023: img1 = new Image(); 1024: img1.src = "$iconpath/mailbkgrd.gif"; 1025: img2 = new Image(); 1026: img2.src = "$iconpath/mailto.gif"; 1027: 1028: function msgCenter(msgform,usrctr,fullname) { 1029: var Nmsg = msgform.savemsgN.value; 1030: savedMsgHeader(Nmsg,usrctr,fullname); 1031: var subject = msgform.msgsub.value; 1032: var msgchk = document.SCORE["includemsg"+usrctr].value; 1033: re = /msgsub/; 1034: var shwsel = ""; 1035: if (re.test(msgchk)) { shwsel = "checked" } 1036: subject = (document.SCORE.shownSub.value == 0 ? checkEntities(subject) : subject); 1037: displaySubject(checkEntities(subject),shwsel); 1038: for (var i=1; i<=Nmsg; i++) { 1039: var testmsg = "savemsg"+i+","; 1040: re = new RegExp(testmsg,"g"); 1041: shwsel = ""; 1042: if (re.test(msgchk)) { shwsel = "checked" } 1043: var message = document.SCORE["savemsg"+i].value; 1044: message = (document.SCORE["shownOnce"+i].value == 0 ? checkEntities(message) : message); 1045: displaySavedMsg(i,message,shwsel); //I do not get it. w/o checkEntities on saved messages, 1046: //any < is already converted to <, etc. However, only once!! 1047: } 1048: newmsg = document.SCORE["newmsg"+usrctr].value; 1049: shwsel = ""; 1050: re = /newmsg/; 1051: if (re.test(msgchk)) { shwsel = "checked" } 1052: newMsg(newmsg,shwsel); 1053: msgTail(); 1054: return; 1055: } 1056: 1057: function checkEntities(strx) { 1058: if (strx.length == 0) return strx; 1059: var orgStr = ["&", "<", ">", '"']; 1060: var newStr = ["&", "<", ">", """]; 1061: var counter = 0; 1062: while (counter < 4) { 1063: strx = strReplace(strx,orgStr[counter],newStr[counter]); 1064: counter++; 1065: } 1066: return strx; 1067: } 1068: 1069: function strReplace(strx, orgStr, newStr) { 1070: return strx.split(orgStr).join(newStr); 1071: } 1072: 1073: function savedMsgHeader(Nmsg,usrctr,fullname) { 1074: var height = 70*Nmsg+250; 1075: var scrollbar = "no"; 1076: if (height > 600) { 1077: height = 600; 1078: scrollbar = "yes"; 1079: } 1080: var xpos = (screen.width-600)/2; 1081: xpos = (xpos < 0) ? '0' : xpos; 1082: var ypos = (screen.height-height)/2-30; 1083: ypos = (ypos < 0) ? '0' : ypos; 1084: 1085: pWin = window.open('', 'MessageCenter', 'resizable=yes,toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height); 1086: pWin.focus(); 1087: pDoc = pWin.document; 1088: pDoc.$docopen; 1089: pDoc.write("<html><head>"); 1090: pDoc.write("<title>Message Central</title>"); 1091: 1092: pDoc.write("<script language=javascript>"); 1093: pDoc.write("function checkInput() {"); 1094: pDoc.write(" opener.document.SCORE.msgsub.value = opener.checkEntities(document.msgcenter.msgsub.value);"); 1095: pDoc.write(" var nmsg = opener.document.SCORE.savemsgN.value;"); 1096: pDoc.write(" var usrctr = document.msgcenter.usrctr.value;"); 1097: pDoc.write(" var newval = opener.document.SCORE[\\"newmsg\\"+usrctr];"); 1098: pDoc.write(" newval.value = opener.checkEntities(document.msgcenter.newmsg.value);"); 1099: 1100: pDoc.write(" var msgchk = \\"\\";"); 1101: pDoc.write(" if (document.msgcenter.subchk.checked) {"); 1102: pDoc.write(" msgchk = \\"msgsub,\\";"); 1103: pDoc.write(" }"); 1104: pDoc.write(" var includemsg = 0;"); 1105: pDoc.write(" for (var i=1; i<=nmsg; i++) {"); 1106: pDoc.write(" var opnmsg = opener.document.SCORE[\\"savemsg\\"+i];"); 1107: pDoc.write(" var frmmsg = document.msgcenter[\\"msg\\"+i];"); 1108: pDoc.write(" opnmsg.value = opener.checkEntities(frmmsg.value);"); 1109: pDoc.write(" var showflg = opener.document.SCORE[\\"shownOnce\\"+i];"); 1110: pDoc.write(" showflg.value = \\"1\\";"); 1111: pDoc.write(" var chkbox = document.msgcenter[\\"msgn\\"+i];"); 1112: pDoc.write(" if (chkbox.checked) {"); 1113: pDoc.write(" msgchk += \\"savemsg\\"+i+\\",\\";"); 1114: pDoc.write(" includemsg = 1;"); 1115: pDoc.write(" }"); 1116: pDoc.write(" }"); 1117: pDoc.write(" if (document.msgcenter.newmsgchk.checked) {"); 1118: pDoc.write(" msgchk += \\"newmsg\\"+usrctr;"); 1119: pDoc.write(" includemsg = 1;"); 1120: pDoc.write(" }"); 1121: pDoc.write(" imgformname = opener.document.SCORE[\\"mailicon\\"+usrctr];"); 1122: pDoc.write(" imgformname.src = \\"$iconpath/\\"+((includemsg) ? \\"mailto.gif\\" : \\"mailbkgrd.gif\\");"); 1123: pDoc.write(" var includemsg = opener.document.SCORE[\\"includemsg\\"+usrctr];"); 1124: pDoc.write(" includemsg.value = msgchk;"); 1125: 1126: pDoc.write(" self.close()"); 1127: 1128: pDoc.write("}"); 1129: 1130: pDoc.write("<"); 1131: pDoc.write("/script>"); 1132: 1133: pDoc.write("</head><body bgcolor=white>"); 1134: 1135: pDoc.write("<form action=\\"inactive\\" name=\\"msgcenter\\">"); 1136: pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">"); 1137: pDoc.write("<font color=\\"green\\" size=+1> Compose Message for \"+fullname+\"</font><br><br>"); 1138: 1139: pDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">"); 1140: pDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">"); 1141: pDoc.write("<td><b>Type</b></td><td><b>Include</b></td><td><b>Message</td></tr>"); 1142: } 1143: function displaySubject(msg,shwsel) { 1144: pDoc = pWin.document; 1145: pDoc.write("<tr bgcolor=\\"#ffffdd\\">"); 1146: pDoc.write("<td>Subject</td>"); 1147: pDoc.write("<td align=\\"center\\"><input name=\\"subchk\\" type=\\"checkbox\\"" +shwsel+"></td>"); 1148: pDoc.write("<td><input name=\\"msgsub\\" type=\\"text\\" value=\\""+msg+"\\"size=\\"60\\" maxlength=\\"80\\"></td></tr>"); 1149: } 1150: 1151: function displaySavedMsg(ctr,msg,shwsel) { 1152: pDoc = pWin.document; 1153: pDoc.write("<tr bgcolor=\\"#ffffdd\\">"); 1154: pDoc.write("<td align=\\"center\\">"+ctr+"</td>"); 1155: pDoc.write("<td align=\\"center\\"><input name=\\"msgn"+ctr+"\\" type=\\"checkbox\\"" +shwsel+"></td>"); 1156: pDoc.write("<td><textarea name=\\"msg"+ctr+"\\" cols=\\"60\\" rows=\\"3\\">"+msg+"</textarea></td></tr>"); 1157: } 1158: 1159: function newMsg(newmsg,shwsel) { 1160: pDoc = pWin.document; 1161: pDoc.write("<tr bgcolor=\\"#ffffdd\\">"); 1162: pDoc.write("<td align=\\"center\\">New</td>"); 1163: pDoc.write("<td align=\\"center\\"><input name=\\"newmsgchk\\" type=\\"checkbox\\"" +shwsel+"></td>"); 1164: pDoc.write("<td><textarea name=\\"newmsg\\" cols=\\"60\\" rows=\\"3\\" onchange=\\"javascript:this.form.newmsgchk.checked=true\\" >"+newmsg+"</textarea></td></tr>"); 1165: } 1166: 1167: function msgTail() { 1168: pDoc = pWin.document; 1169: pDoc.write("</table>"); 1170: pDoc.write("</td></tr></table> "); 1171: pDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:checkInput()\\"> "); 1172: pDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br><br>"); 1173: pDoc.write("</form>"); 1174: pDoc.write("</body></html>"); 1175: pDoc.close(); 1176: } 1177: 1178: //====================== Script for keyword highlight options ============== 1179: function kwhighlight() { 1180: var kwclr = document.SCORE.kwclr.value; 1181: var kwsize = document.SCORE.kwsize.value; 1182: var kwstyle = document.SCORE.kwstyle.value; 1183: var redsel = ""; 1184: var grnsel = ""; 1185: var blusel = ""; 1186: if (kwclr=="red") {var redsel="checked"}; 1187: if (kwclr=="green") {var grnsel="checked"}; 1188: if (kwclr=="blue") {var blusel="checked"}; 1189: var sznsel = ""; 1190: var sz1sel = ""; 1191: var sz2sel = ""; 1192: if (kwsize=="0") {var sznsel="checked"}; 1193: if (kwsize=="+1") {var sz1sel="checked"}; 1194: if (kwsize=="+2") {var sz2sel="checked"}; 1195: var synsel = ""; 1196: var syisel = ""; 1197: var sybsel = ""; 1198: if (kwstyle=="") {var synsel="checked"}; 1199: if (kwstyle=="<i>") {var syisel="checked"}; 1200: if (kwstyle=="<b>") {var sybsel="checked"}; 1201: highlightCentral(); 1202: highlightbody('red','red',redsel,'0','normal',sznsel,'','normal',synsel); 1203: highlightbody('green','green',grnsel,'+1','+1',sz1sel,'<i>','italic',syisel); 1204: highlightbody('blue','blue',blusel,'+2','+2',sz2sel,'<b>','bold',sybsel); 1205: highlightend(); 1206: return; 1207: } 1208: 1209: function highlightCentral() { 1210: // if (window.hwdWin) window.hwdWin.close(); 1211: var xpos = (screen.width-400)/2; 1212: xpos = (xpos < 0) ? '0' : xpos; 1213: var ypos = (screen.height-330)/2-30; 1214: ypos = (ypos < 0) ? '0' : ypos; 1215: 1216: hwdWin = window.open('', 'KeywordHighlightCentral', 'resizeable=yes,toolbar=no,location=no,scrollbars=no,width=400,height=300,screenx='+xpos+',screeny='+ypos); 1217: hwdWin.focus(); 1218: var hDoc = hwdWin.document; 1219: hDoc.$docopen; 1220: hDoc.write("<html><head>"); 1221: hDoc.write("<title>Highlight Central</title>"); 1222: 1223: hDoc.write("<script language=javascript>"); 1224: hDoc.write("function updateChoice(flag) {"); 1225: hDoc.write(" opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);"); 1226: hDoc.write(" opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);"); 1227: hDoc.write(" opener.document.SCORE.kwstyle.value = opener.radioSelection(document.hlCenter.kwdstyle);"); 1228: hDoc.write(" opener.document.SCORE.refresh.value = \\"on\\";"); 1229: hDoc.write(" if (opener.document.SCORE.keywords.value!=\\"\\"){"); 1230: hDoc.write(" opener.document.SCORE.submit();"); 1231: hDoc.write(" }"); 1232: hDoc.write(" self.close()"); 1233: hDoc.write("}"); 1234: 1235: hDoc.write("<"); 1236: hDoc.write("/script>"); 1237: 1238: hDoc.write("</head><body bgcolor=white>"); 1239: 1240: hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">"); 1241: hDoc.write("<font color=\\"green\\" size=+1> Keyword Highlight Options</font><br><br>"); 1242: 1243: hDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">"); 1244: hDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">"); 1245: hDoc.write("<td><b>Text Color</b></td><td><b>Font Size</b></td><td><b>Font Style</td></tr>"); 1246: } 1247: 1248: function highlightbody(clrval,clrtxt,clrsel,szval,sztxt,szsel,syval,sytxt,sysel) { 1249: var hDoc = hwdWin.document; 1250: hDoc.write("<tr bgcolor=\\"#ffffdd\\">"); 1251: hDoc.write("<td align=\\"left\\">"); 1252: hDoc.write("<input name=\\"kwdclr\\" type=\\"radio\\" value=\\""+clrval+"\\" "+clrsel+"> "+clrtxt+"</td>"); 1253: hDoc.write("<td align=\\"left\\">"); 1254: hDoc.write("<input name=\\"kwdsize\\" type=\\"radio\\" value=\\""+szval+"\\" "+szsel+"> "+sztxt+"</td>"); 1255: hDoc.write("<td align=\\"left\\">"); 1256: hDoc.write("<input name=\\"kwdstyle\\" type=\\"radio\\" value=\\""+syval+"\\" "+sysel+"> "+sytxt+"</td>"); 1257: hDoc.write("</tr>"); 1258: } 1259: 1260: function highlightend() { 1261: var hDoc = hwdWin.document; 1262: hDoc.write("</table>"); 1263: hDoc.write("</td></tr></table> "); 1264: hDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:updateChoice(1)\\"> "); 1265: hDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br><br>"); 1266: hDoc.write("</form>"); 1267: hDoc.write("</body></html>"); 1268: hDoc.close(); 1269: } 1270: 1271: </script> 1272: SUBJAVASCRIPT 1273: } 1274: 1275: #--- displays the grading box, used in essay type problem and grading by page/sequence 1276: sub gradeBox { 1277: my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_; 1278: 1279: my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL'). 1280: '/check.gif" height="16" border="0" />'; 1281: 1282: my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb,$udom,$uname); 1283: my $wgtmsg = ($wgt > 0 ? '(problem weight)' : 1284: '<font color="red">problem weight assigned by computer</font>'); 1285: $wgt = ($wgt > 0 ? $wgt : '1'); 1286: my $score = ($$record{'resource.'.$partid.'.awarded'} eq '' ? 1287: '' : $$record{'resource.'.$partid.'.awarded'}*$wgt); 1288: my $result='<input type="hidden" name="WGT'.$counter.'_'.$partid.'" value="'.$wgt.'" />'."\n"; 1289: 1290: my $display_part=&get_display_part($partid,undef,$symb); 1291: $result.='<table border="0"><tr><td>'. 1292: '<b>Part: </b>'.$display_part.' <b>Points: </b></td><td>'."\n"; 1293: 1294: my $ctr = 0; 1295: $result.='<table border="0"><tr>'."\n"; # display radio buttons in a nice table 10 across 1296: while ($ctr<=$wgt) { 1297: $result.= '<td><nobr><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '. 1298: 'onclick="javascript:writeBox(this.form,\''.$counter.'_'.$partid.'\','. 1299: $ctr.')" value="'.$ctr.'" '. 1300: ($score eq $ctr ? 'checked':'').' /> '.$ctr."</nobr></td>\n"; 1301: $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : ''); 1302: $ctr++; 1303: } 1304: $result.='</tr></table>'; 1305: 1306: $result.='</td><td> <b>or</b> </td>'."\n"; 1307: $result.='<td><input type="text" name="GD_BOX'.$counter.'_'.$partid.'"'. 1308: ($score ne ''? ' value = "'.$score.'"':'').' size="4" '. 1309: 'onChange="javascript:updateRadio(this.form,\''.$counter.'_'.$partid.'\','. 1310: $wgt.')" /></td>'."\n"; 1311: $result.='<td>/'.$wgt.' '.$wgtmsg. 1312: ($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? ' '.$checkIcon : ''). 1313: ' </td><td>'."\n"; 1314: 1315: $result.='<select name="GD_SEL'.$counter.'_'.$partid.'" '. 1316: 'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n"; 1317: if ($$record{'resource.'.$partid.'.solved'} eq 'excused') { 1318: $result.='<option> </option>'. 1319: '<option selected="on">excused</option>'; 1320: } else { 1321: $result.='<option selected="on"> </option>'. 1322: '<option>excused</option>'; 1323: } 1324: $result.='<option>reset status</option></select>'."\n"; 1325: $result.="  \n"; 1326: $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n". 1327: '<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n". 1328: '<input type="hidden" name="solved'.$counter.'_'.$partid.'" value="'. 1329: $$record{'resource.'.$partid.'.solved'}.'" />'."\n"; 1330: $result.='</td></tr></table>'."\n"; 1331: return $result; 1332: } 1333: 1334: sub show_problem { 1335: my ($request,$symb,$uname,$udom,$removeform,$viewon,$mode) = @_; 1336: my $rendered; 1337: if ($mode eq 'both' or $mode eq 'text') { 1338: $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom, 1339: $ENV{'request.course.id'}); 1340: } 1341: if ($removeform) { 1342: $rendered=~s|<form(.*?)>||g; 1343: $rendered=~s|</form>||g; 1344: $rendered=~s|name="submit"|name="would_have_been_submit"|g; 1345: } 1346: my $companswer; 1347: if ($mode eq 'both' or $mode eq 'answer') { 1348: $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom, 1349: $ENV{'request.course.id'}); 1350: } 1351: if ($removeform) { 1352: $companswer=~s|<form(.*?)>||g; 1353: $companswer=~s|</form>||g; 1354: $companswer=~s|name="submit"|name="would_have_been_submit"|g; 1355: } 1356: my $result.='<table border="0" width="100%"><tr><td bgcolor="#777777">'; 1357: $result.='<table border="0" width="100%">'; 1358: if ($viewon) { 1359: $result.='<tr><td bgcolor="#e6ffff"><b> '; 1360: if ($mode eq 'both' or $mode eq 'text') { 1361: $result.='View of the problem - '; 1362: } else { 1363: $result.='Correct answer: '; 1364: } 1365: $result.=$ENV{'form.fullname'}.'</b></td></tr>'; 1366: } 1367: if ($mode eq 'both') { 1368: $result.='<tr><td bgcolor="#ffffff">'.$rendered.'<br />'; 1369: $result.='<b>Correct answer:</b><br />'.$companswer; 1370: } elsif ($mode eq 'text') { 1371: $result.='<tr><td bgcolor="#ffffff">'.$rendered; 1372: } elsif ($mode eq 'answer') { 1373: $result.='<tr><td bgcolor="#ffffff">'.$companswer; 1374: } 1375: $result.='</td></tr></table>'; 1376: $result.='</td></tr></table><br />'; 1377: return $result; 1378: } 1379: 1380: # --------------------------- show submissions of a student, option to grade 1381: sub submission { 1382: my ($request,$counter,$total) = @_; 1383: 1384: (my $url=$ENV{'form.url'})=~s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--; 1385: my ($uname,$udom) = ($ENV{'form.student'},$ENV{'form.userdom'}); 1386: $udom = ($udom eq '' ? $ENV{'user.domain'} : $udom); #has form.userdom changed for a student? 1387: my $usec = &Apache::lonnet::getsection($udom,$uname,$ENV{'request.course.id'}); 1388: $ENV{'form.fullname'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $ENV{'form.fullname'} eq ''; 1389: 1390: my $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url))); 1391: if ($symb eq '') { $request->print("Unable to handle ambiguous references:$url:."); return ''; } 1392: 1393: if (!&canview($usec)) { 1394: $request->print('<font color="red">Unable to view requested student.('. 1395: $uname.'@'.$udom.' in section '.$usec.' in course id '. 1396: $ENV{'request.course.id'}.')</font>'); 1397: $request->print(&show_grading_menu_form($symb,$url)); 1398: return; 1399: } 1400: 1401: if (!$ENV{'form.lastSub'}) { $ENV{'form.lastSub'} = 'datesub'; } 1402: if (!$ENV{'form.vProb'}) { $ENV{'form.vProb'} = 'yes'; } 1403: if (!$ENV{'form.vAns'}) { $ENV{'form.vAns'} = 'yes'; } 1404: my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : ''); 1405: my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL'). 1406: '/check.gif" height="16" border="0" />'; 1407: 1408: # header info 1409: if ($counter == 0) { 1410: &sub_page_js($request); 1411: &sub_page_kw_js($request) if ($ENV{'form.handgrade'} eq 'yes'); 1412: $ENV{'form.probTitle'} = $ENV{'form.probTitle'} eq '' ? 1413: &Apache::lonnet::gettitle($symb) : $ENV{'form.probTitle'}; 1414: 1415: $request->print('<h3> <font color="#339933">Submission Record</font></h3>'."\n". 1416: '<font size=+1> <b>Resource: </b>'.$ENV{'form.probTitle'}.'</font>'."\n"); 1417: 1418: if ($ENV{'form.handgrade'} eq 'no') { 1419: my $checkMark='<br /><br /> <b>Note:</b> Part(s) graded correct by the computer is marked with a '. 1420: $checkIcon.' symbol.'."\n"; 1421: $request->print($checkMark); 1422: } 1423: 1424: # option to display problem, only once else it cause problems 1425: # with the form later since the problem has a form. 1426: if ($ENV{'form.vProb'} eq 'yes' or $ENV{'form.vAns'} eq 'yes') { 1427: my $mode; 1428: if ($ENV{'form.vProb'} eq 'yes' && $ENV{'form.vAns'} eq 'yes') { 1429: $mode='both'; 1430: } elsif ($ENV{'form.vProb'} eq 'yes') { 1431: $mode='text'; 1432: } elsif ($ENV{'form.vAns'} eq 'yes') { 1433: $mode='answer'; 1434: } 1435: $request->print(&show_problem($request,$symb,$uname,$udom,0,1,$mode)); 1436: } 1437: 1438: # kwclr is the only variable that is guaranteed to be non blank 1439: # if this subroutine has been called once. 1440: my %keyhash = (); 1441: if ($ENV{'form.kwclr'} eq '' && $ENV{'form.handgrade'} eq 'yes') { 1442: %keyhash = &Apache::lonnet::dump('nohist_handgrade', 1443: $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}, 1444: $ENV{'course.'.$ENV{'request.course.id'}.'.num'}); 1445: 1446: my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'}; 1447: $ENV{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : ''; 1448: $ENV{'form.kwclr'} = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red'; 1449: $ENV{'form.kwsize'} = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0'; 1450: $ENV{'form.kwstyle'} = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : ''; 1451: $ENV{'form.msgsub'} = $keyhash{$symb.'_subject'} ne '' ? 1452: $keyhash{$symb.'_subject'} : $ENV{'form.probTitle'}; 1453: $ENV{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0'; 1454: } 1455: my $overRideScore = $ENV{'form.overRideScore'} eq '' ? 'no' : $ENV{'form.overRideScore'}; 1456: 1457: $request->print('<form action="/adm/grades" method="post" name="SCORE">'."\n". 1458: '<input type="hidden" name="command" value="handgrade" />'."\n". 1459: '<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."\n". 1460: '<input type="hidden" name="Status" value="'.$ENV{'form.Status'}.'" />'."\n". 1461: '<input type="hidden" name="overRideScore" value="'.$overRideScore.'" />'."\n". 1462: '<input type="hidden" name="probTitle" value="'.$ENV{'form.probTitle'}.'" />'."\n". 1463: '<input type="hidden" name="refresh" value="off" />'."\n". 1464: '<input type="hidden" name="studentNo" value="" />'."\n". 1465: '<input type="hidden" name="gradeOpt" value="" />'."\n". 1466: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n". 1467: '<input type="hidden" name="url" value="'.$url.'" />'."\n". 1468: '<input type="hidden" name="showgrading" value="'.$ENV{'form.showgrading'}.'" />'."\n". 1469: '<input type="hidden" name="vProb" value="'.$ENV{'form.vProb'}.'" />'."\n". 1470: '<input type="hidden" name="vAns" value="'.$ENV{'form.vAns'}.'" />'."\n". 1471: '<input type="hidden" name="lastSub" value="'.$ENV{'form.lastSub'}.'" />'."\n". 1472: '<input type="hidden" name="section" value="'.$ENV{'form.section'}.'">'."\n". 1473: '<input type="hidden" name="submitonly" value="'.$ENV{'form.submitonly'}.'">'."\n". 1474: '<input type="hidden" name="handgrade" value="'.$ENV{'form.handgrade'}.'">'."\n". 1475: '<input type="hidden" name="NCT"'. 1476: ' value="'.($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : $total+1).'" />'."\n"); 1477: if ($ENV{'form.handgrade'} eq 'yes') { 1478: $request->print('<input type="hidden" name="keywords" value="'.$ENV{'form.keywords'}.'" />'."\n". 1479: '<input type="hidden" name="kwclr" value="'.$ENV{'form.kwclr'}.'" />'."\n". 1480: '<input type="hidden" name="kwsize" value="'.$ENV{'form.kwsize'}.'" />'."\n". 1481: '<input type="hidden" name="kwstyle" value="'.$ENV{'form.kwstyle'}.'" />'."\n". 1482: '<input type="hidden" name="msgsub" value="'.$ENV{'form.msgsub'}.'" />'."\n". 1483: '<input type="hidden" name="shownSub" value="0" />'."\n". 1484: '<input type="hidden" name="savemsgN" value="'.$ENV{'form.savemsgN'}.'" />'."\n"); 1485: foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) { 1486: $request->print('<input type="hidden" name="vPart" value="'.$partid.'" />'."\n"); 1487: } 1488: } 1489: 1490: my ($cts,$prnmsg) = (1,''); 1491: while ($cts <= $ENV{'form.savemsgN'}) { 1492: $prnmsg.='<input type="hidden" name="savemsg'.$cts.'" value="'. 1493: (!exists($keyhash{$symb.'_savemsg'.$cts}) ? 1494: &Apache::lonfeedback::clear_out_html($ENV{'form.savemsg'.$cts}) : 1495: &Apache::lonfeedback::clear_out_html($keyhash{$symb.'_savemsg'.$cts})). 1496: '" />'."\n". 1497: '<input type="hidden" name="shownOnce'.$cts.'" value="0" />'."\n"; 1498: $cts++; 1499: } 1500: $request->print($prnmsg); 1501: 1502: if ($ENV{'form.handgrade'} eq 'yes' && $ENV{'form.showgrading'} eq 'yes') { 1503: # 1504: # Print out the keyword options line 1505: # 1506: $request->print(<<KEYWORDS); 1507: <b>Keyword Options:</b> 1508: <a href="javascript:keywords(document.SCORE)"; TARGET=_self>List</a> 1509: <a href="#" onMouseDown="javascript:getSel(); return false" 1510: CLASS="page">Paste Selection to List</a> 1511: <a href="javascript:kwhighlight()"; TARGET=_self>Highlight Attribute</a><br /><br /> 1512: KEYWORDS 1513: # 1514: # Load the other essays for similarity check 1515: # 1516: my $essayurl=&Apache::lonnet::declutter($url); 1517: my ($adom,$aname,$apath)=($essayurl=~/^(\w+)\/(\w+)\/(.*)$/); 1518: $apath=&Apache::lonnet::escape($apath); 1519: $apath=~s/\W/\_/gs; 1520: %oldessays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname); 1521: } 1522: } 1523: 1524: if ($ENV{'form.vProb'} eq 'all' or $ENV{'form.vAns'} eq 'all') { 1525: $request->print('<br /><br /><br />') if ($counter > 0); 1526: my $mode; 1527: if ($ENV{'form.vProb'} eq 'all' && $ENV{'form.vAns'} eq 'all') { 1528: $mode='both'; 1529: } elsif ($ENV{'form.vProb'} eq 'all' ) { 1530: $mode='text'; 1531: } elsif ($ENV{'form.vAns'} eq 'all') { 1532: $mode='answer'; 1533: } 1534: $request->print(&show_problem($request,$symb,$uname,$udom,1,1,$mode)); 1535: } 1536: 1537: my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname); 1538: 1539: my ($partlist,$handgrade,$responseType) = &response_type($url,$symb); 1540: 1541: # Display student info 1542: $request->print(($counter == 0 ? '' : '<br />')); 1543: my $result='<table border="0" width=100%><tr><td bgcolor="#777777">'."\n". 1544: '<table border="0" width=100%><tr bgcolor="#edffff"><td>'."\n"; 1545: 1546: $result.='<b>Fullname: </b>'.&nameUserString(undef,$ENV{'form.fullname'},$uname,$udom).'<br />'."\n"; 1547: $result.='<input type="hidden" name="name'.$counter. 1548: '" value="'.$ENV{'form.fullname'}.'" />'."\n"; 1549: 1550: # If any part of the problem is an essay-response (handgraded), then check for collaborators 1551: my @col_fullnames; 1552: my ($classlist,$fullname); 1553: if ($ENV{'form.handgrade'} eq 'yes') { 1554: ($classlist,undef,$fullname) = &getclasslist('all','0'); 1555: for (keys (%$handgrade)) { 1556: my $ncol = &Apache::lonnet::EXT('resource.'.$_. 1557: '.maxcollaborators', 1558: $symb,$udom,$uname); 1559: next if ($ncol <= 0); 1560: s/\_/\./g; 1561: next if ($record{'resource.'.$_.'.collaborators'} eq ''); 1562: my @goodcollaborators = (); 1563: my @badcollaborators = (); 1564: foreach (split(/,?\s+/,$record{'resource.'.$_.'.collaborators'})) { 1565: $_ =~ s/[\$\^\(\)]//g; 1566: next if ($_ eq ''); 1567: my ($co_name,$co_dom) = split /\@|:/,$_; 1568: $co_dom = $udom if (! defined($co_dom) || $co_dom =~ /^domain$/i); 1569: next if ($co_name eq $uname && $co_dom eq $udom); 1570: # Doing this grep allows 'fuzzy' specification 1571: my @Matches = grep /^$co_name:$co_dom$/i,keys %$classlist; 1572: if (! scalar(@Matches)) { 1573: push @badcollaborators,$_; 1574: } else { 1575: push @goodcollaborators, @Matches; 1576: } 1577: } 1578: if (scalar(@goodcollaborators) != 0) { 1579: $result.='<b>Collaborators: </b>'; 1580: foreach (@goodcollaborators) { 1581: my ($lastname,$givenn) = split(/,/,$$fullname{$_}); 1582: push @col_fullnames, $givenn.' '.$lastname; 1583: $result.=$$fullname{$_}.' '; 1584: } 1585: $result.='<br />'."\n"; 1586: my ($part)=split(/\./,$_); 1587: $result.='<input type="hidden" name="collaborator'.$counter. 1588: '" value="'.$part.':'.(join ':',@goodcollaborators).'" />'. 1589: "\n"; 1590: } 1591: if (scalar(@badcollaborators) > 0) { 1592: $result.='<table border="0"><tr bgcolor="#ffbbbb"><td>'; 1593: $result.='This student has submitted '; 1594: $result.=(scalar(@badcollaborators) == 1) ? 'an invalid collaborator' : 'invalid collaborators'; 1595: $result .= ': '.join(', ',@badcollaborators); 1596: $result .= '</td></tr></table>'; 1597: } 1598: if (scalar(@badcollaborators > $ncol)) { 1599: $result .= '<table border="0"><tr bgcolor="#ffbbbb"><td>'; 1600: $result .= 'This student has submitted too many '. 1601: 'collaborators. Maximum is '.$ncol.'.'; 1602: $result .= '</td></tr></table>'; 1603: } 1604: } 1605: } 1606: $request->print($result."\n"); 1607: 1608: # print student answer/submission 1609: # Options are (1) Handgaded submission only 1610: # (2) Last submission, includes submission that is not handgraded 1611: # (for multi-response type part) 1612: # (3) Last submission plus the parts info 1613: # (4) The whole record for this student 1614: if ($ENV{'form.lastSub'} =~ /^(lastonly|hdgrade)$/) { 1615: my ($string,$timestamp)= &get_last_submission(\%record); 1616: my $lastsubonly=''. 1617: ($$timestamp eq '' ? '' : '<b>Date Submitted:</b> '. 1618: $$timestamp)."</td></tr>\n"; 1619: if ($$timestamp eq '') { 1620: $lastsubonly.='<tr><td bgcolor="#ffffe6">'.$$string[0]; 1621: } else { 1622: my %seenparts; 1623: for my $part (sort keys(%$handgrade)) { 1624: my ($partid,$respid) = split(/_/,$part); 1625: my $display_part=&get_display_part($partid,$url,$symb); 1626: if ($ENV{"form.$uname:$udom:$partid:submitted_by"}) { 1627: if (exists($seenparts{$partid})) { next; } 1628: $seenparts{$partid}=1; 1629: my $submitby='<b>Part:</b> '.$display_part. 1630: ' <b>Collaborative submission by:</b> '. 1631: '<a href="javascript:viewSubmitter(\''. 1632: $ENV{"form.$uname:$udom:$partid:submitted_by"}. 1633: '\')"; TARGET=_self>'. 1634: $$fullname{$ENV{"form.$uname:$udom:$partid:submitted_by"}}.'</a><br />'; 1635: $request->print($submitby); 1636: next; 1637: } 1638: my $responsetype = $responseType->{$partid}->{$respid}; 1639: if (!exists($record{"resource.$partid.$respid.submission"})) { 1640: $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part:</b> '. 1641: $display_part.' <font color="#999999">( ID '.$respid. 1642: ' )</font> '. 1643: '<font color="red">Nothing submitted - no attempts</font><br /><br />'; 1644: next; 1645: } 1646: foreach (@$string) { 1647: my ($partid,$respid) = /^resource\.([^\.]*)\.([^\.]*)\.submission/; 1648: if ($part ne ($partid.'_'.$respid)) { next; } 1649: my ($ressub,$subval) = split(/:/,$_,2); 1650: # Similarity check 1651: my $similar=''; 1652: if($ENV{'form.checkPlag'}){ 1653: my ($oname,$odom,$ocrsid,$oessay,$osim)= 1654: &most_similar($uname,$udom,$subval); 1655: if ($osim) { 1656: $osim=int($osim*100.0); 1657: $similar="<hr /><h3><font color=\"#FF0000\">Essay". 1658: " is $osim% similar to an essay by ". 1659: &Apache::loncommon::plainname($oname,$odom). 1660: '</font></h3><blockquote><i>'. 1661: &keywords_highlight($oessay). 1662: '</i></blockquote><hr />'; 1663: } 1664: } 1665: my $order=&get_order($partid,$respid,$symb,$uname,$udom); 1666: if ($ENV{'form.lastSub'} eq 'lastonly' || 1667: ($ENV{'form.lastSub'} eq 'hdgrade' && 1668: $$handgrade{$part} eq 'yes')) { 1669: my $display_part=&get_display_part($partid,$url,$symb); 1670: $lastsubonly.='<tr><td bgcolor="#ffffe6"><b>Part:</b> '. 1671: $display_part.' <font color="#999999">( ID '.$respid. 1672: ' )</font> '; 1673: if ($record{"resource.$partid.$respid.uploadedurl"}) { 1674: &Apache::lonnet::allowuploaded('/adm/grades', 1675: $record{"resource.$partid.$respid.uploadedurl"}); 1676: $lastsubonly.='<a href="'.$record{"resource.$partid.$respid.uploadedurl"}.'" target="lonGRDs"><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 />'; 1677: } 1678: $lastsubonly.='<b>Submitted Answer: </b>'. 1679: &cleanRecord($subval,$responsetype,$symb,$partid, 1680: $respid,\%record,$order); 1681: if ($similar) {$lastsubonly.="<br /><br />$similar\n";} 1682: } 1683: } 1684: } 1685: } 1686: $lastsubonly.='</td></tr><tr bgcolor="#ffffff"><td>'."\n"; 1687: $request->print($lastsubonly); 1688: } elsif ($ENV{'form.lastSub'} eq 'datesub') { 1689: my (undef,$responseType,undef,$parts) = &showResourceInfo($url); 1690: $request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom)); 1691: } elsif ($ENV{'form.lastSub'} =~ /^(last|all)$/) { 1692: $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom, 1693: $ENV{'request.course.id'}, 1694: $last,'.submission', 1695: 'Apache::grades::keywords_highlight')); 1696: } 1697: 1698: $request->print('<input type="hidden" name="unamedom'.$counter.'" value="'.$uname.':' 1699: .$udom.'" />'."\n"); 1700: 1701: # return if view submission with no grading option 1702: if ($ENV{'form.showgrading'} eq '' || (!&canmodify($usec))) { 1703: my $toGrade.='<input type="button" value="Grade Student" '. 1704: 'onClick="javascript:checksubmit(this.form,\'Grade Student\',\'' 1705: .$counter.'\');" TARGET=_self> '."\n" if (&canmodify($usec)); 1706: $toGrade.='</td></tr></table></td></tr></table>'."\n"; 1707: if (($ENV{'form.command'} eq 'submission') || 1708: ($ENV{'form.command'} eq 'processGroup' && $counter == $total)) { 1709: $toGrade.='</form>'.&show_grading_menu_form($symb,$url) 1710: } 1711: $request->print($toGrade); 1712: return; 1713: } else { 1714: $request->print('</td></tr></table></td></tr></table>'."\n"); 1715: } 1716: 1717: # essay grading message center 1718: if ($ENV{'form.handgrade'} eq 'yes') { 1719: my ($lastname,$givenn) = split(/,/,$ENV{'form.fullname'}); 1720: my $msgfor = $givenn.' '.$lastname; 1721: if (scalar(@col_fullnames) > 0) { 1722: my $lastone = pop @col_fullnames; 1723: $msgfor .= ', '.(join ', ',@col_fullnames).' and '.$lastone.'.'; 1724: } 1725: $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript 1726: $result='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n". 1727: '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n"; 1728: $result.=' <a href="javascript:msgCenter(document.SCORE,'.$counter. 1729: ',\''.$msgfor.'\')"; TARGET=_self>'. 1730: 'Compose Message to student'.(scalar(@col_fullnames) >= 1 ? 's' : '').'</a> '. 1731: '<img src="'.$request->dir_config('lonIconsURL'). 1732: '/mailbkgrd.gif" width="14" height="10" name="mailicon'.$counter.'" />'."\n". 1733: '<br /> (Message will be sent when you click on Save & Next below.)'."\n" 1734: if ($ENV{'form.handgrade'} eq 'yes'); 1735: $request->print($result); 1736: } 1737: 1738: my %seen = (); 1739: my @partlist; 1740: my @gradePartRespid; 1741: for (sort keys(%$handgrade)) { 1742: my ($partid,$respid) = split(/_/); 1743: next if ($seen{$partid} > 0); 1744: $seen{$partid}++; 1745: next if ($$handgrade{$_} =~ /:no$/ && $ENV{'form.lastSub'} =~ /^(hdgrade)$/); 1746: push @partlist,$partid; 1747: push @gradePartRespid,$partid.'.'.$respid; 1748: 1749: $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record)); 1750: } 1751: $result='<input type="hidden" name="partlist'.$counter. 1752: '" value="'.(join ":",@partlist).'" />'."\n"; 1753: $result.='<input type="hidden" name="gradePartRespid'. 1754: '" value="'.(join ":",@gradePartRespid).'" />'."\n" if ($counter == 0); 1755: my $ctr = 0; 1756: while ($ctr < scalar(@partlist)) { 1757: $result.='<input type="hidden" name="partid'.$counter.'_'.$ctr.'" value="'. 1758: $partlist[$ctr].'" />'."\n"; 1759: $ctr++; 1760: } 1761: $request->print($result.'</td></tr></table></td></tr></table>'."\n"); 1762: 1763: # print end of form 1764: if ($counter == $total) { 1765: my $endform='<table border="0"><tr><td>'."\n"; 1766: $endform.='<input type="button" value="Save & Next" '. 1767: 'onClick="javascript:checksubmit(this.form,\'Save & Next\','. 1768: $total.','.scalar(@partlist).');" TARGET=_self> '."\n"; 1769: my $ntstu ='<select name="NTSTU">'. 1770: '<option>1</option><option>2</option>'. 1771: '<option>3</option><option>5</option>'. 1772: '<option>7</option><option>10</option></select>'."\n"; 1773: my $nsel = ($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : '1'); 1774: $ntstu =~ s/<option>$nsel</<option selected="on">$nsel</; 1775: $endform.=$ntstu.'student(s) '; 1776: $endform.='<input type="button" value="Previous" '. 1777: 'onClick="javascript:checksubmit(this.form,\'Previous\');" TARGET=_self> '."\n". 1778: '<input type="button" value="Next" '. 1779: 'onClick="javascript:checksubmit(this.form,\'Next\');" TARGET=_self> '; 1780: $endform.='(Next and Previous (student) do not save the scores.)'."\n" ; 1781: $endform.='</td><tr></table></form>'; 1782: $endform.=&show_grading_menu_form($symb,$url); 1783: $request->print($endform); 1784: } 1785: return ''; 1786: } 1787: 1788: #--- Retrieve the last submission for all the parts 1789: sub get_last_submission { 1790: my ($returnhash)=@_; 1791: my (@string,$timestamp); 1792: if ($$returnhash{'version'}) { 1793: my %lasthash=(); 1794: my ($version); 1795: for ($version=1;$version<=$$returnhash{'version'};$version++) { 1796: foreach (sort(split(/\:/,$$returnhash{$version.':keys'}))) { 1797: $lasthash{$_}=$$returnhash{$version.':'.$_}; 1798: $timestamp = scalar(localtime($$returnhash{$version.':timestamp'})); 1799: } 1800: } 1801: foreach ((keys %lasthash)) { 1802: if ($_ =~ /\.submission$/) { 1803: my ($partid,$foo) = split(/submission$/,$_); 1804: my $draft = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ? 1805: '<font color="red">Draft Copy</font> ' : ''; 1806: push @string, (join(':',$_,$draft.$lasthash{$_})); 1807: } 1808: } 1809: } 1810: @string = $string[0] eq '' ? '<font color="red">Nothing submitted - no attempts.</font>' : @string; 1811: return \@string,\$timestamp; 1812: } 1813: 1814: #--- High light keywords, with style choosen by user. 1815: sub keywords_highlight { 1816: my $string = shift; 1817: my $size = $ENV{'form.kwsize'} eq '0' ? '' : 'size='.$ENV{'form.kwsize'}; 1818: my $styleon = $ENV{'form.kwstyle'} eq '' ? '' : $ENV{'form.kwstyle'}; 1819: (my $styleoff = $styleon) =~ s/\</\<\//; 1820: my @keylist = split(/[,\s+]/,$ENV{'form.keywords'}); 1821: foreach (@keylist) { 1822: $string =~ s/\b\Q$_\E(\b|\.)/<font color\=$ENV{'form.kwclr'} $size\>$styleon$_$styleoff<\/font>/gi; 1823: } 1824: return $string; 1825: } 1826: 1827: #--- Called from submission routine 1828: sub processHandGrade { 1829: my ($request) = shift; 1830: my $url = $ENV{'form.url'}; 1831: my $symb = $ENV{'form.symb'}; 1832: my $button = $ENV{'form.gradeOpt'}; 1833: my $ngrade = $ENV{'form.NCT'}; 1834: my $ntstu = $ENV{'form.NTSTU'}; 1835: if ($button eq 'Save & Next') { 1836: my $ctr = 0; 1837: while ($ctr < $ngrade) { 1838: my ($uname,$udom) = split(/:/,$ENV{'form.unamedom'.$ctr}); 1839: my ($errorflag,$pts,$wgt) = &saveHandGrade($request,$url,$symb,$uname,$udom,$ctr); 1840: if ($errorflag eq 'no_score') { 1841: $ctr++; 1842: next; 1843: } 1844: if ($errorflag eq 'not_allowed') { 1845: $request->print("<font color=\"red\">Not allowed to modify grades for $uname:$udom</font>"); 1846: $ctr++; 1847: next; 1848: } 1849: my $includemsg = $ENV{'form.includemsg'.$ctr}; 1850: my ($subject,$message,$msgstatus) = ('','',''); 1851: if ($includemsg =~ /savemsg|newmsg\Q$ctr\E/) { 1852: $subject = $ENV{'form.msgsub'} if ($includemsg =~ /^msgsub/); 1853: my (@msgnum) = split(/,/,$includemsg); 1854: foreach (@msgnum) { 1855: $message.=$ENV{'form.'.$_} if ($_ =~ /savemsg|newmsg/ && $_ ne ''); 1856: } 1857: $message =&Apache::lonfeedback::clear_out_html($message); 1858: $message.="\n\nPoint".($pts > 1 ? 's':'').' awarded = '.$pts.' out of '.$wgt; 1859: $message.=" for <a href=\"". 1860: &Apache::lonnet::clutter($url). 1861: "?symb=$symb\">$ENV{'form.probTitle'}</a>"; 1862: $msgstatus = &Apache::lonmsg::user_normal_msg ($uname,$udom, 1863: $ENV{'form.msgsub'},$message); 1864: } 1865: if ($ENV{'form.collaborator'.$ctr}) { 1866: my @collabstrs=&Apache::loncommon::get_env_multiple("form.collaborator$ctr"); 1867: foreach my $collabstr (@collabstrs) { 1868: my ($part,@collaborators) = split(/:/,$collabstr); 1869: foreach (@collaborators) { 1870: my ($errorflag,$pts,$wgt) = 1871: &saveHandGrade($request,$url,$symb,$_,$udom,$ctr, 1872: $ENV{'form.unamedom'.$ctr},$part); 1873: if ($errorflag eq 'not_allowed') { 1874: $request->print("<font color=\"red\">Not allowed to modify grades for $_:$udom</font>"); 1875: next; 1876: } else { 1877: if ($message ne '') { 1878: $msgstatus = &Apache::lonmsg::user_normal_msg($_,$udom,$ENV{'form.msgsub'},$message); 1879: } 1880: 1881: } 1882: } 1883: } 1884: } 1885: $ctr++; 1886: } 1887: } 1888: 1889: if ($ENV{'form.handgrade'} eq 'yes') { 1890: # Keywords sorted in alphabatical order 1891: my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'}; 1892: my %keyhash = (); 1893: $ENV{'form.keywords'} =~ s/,\s{0,}|\s+/ /g; 1894: $ENV{'form.keywords'} =~ s/^\s+|\s+$//; 1895: my (@keywords) = sort(split(/\s+/,$ENV{'form.keywords'})); 1896: $ENV{'form.keywords'} = join(' ',@keywords); 1897: $keyhash{$symb.'_keywords'} = $ENV{'form.keywords'}; 1898: $keyhash{$symb.'_subject'} = $ENV{'form.msgsub'}; 1899: $keyhash{$loginuser.'_kwclr'} = $ENV{'form.kwclr'}; 1900: $keyhash{$loginuser.'_kwsize'} = $ENV{'form.kwsize'}; 1901: $keyhash{$loginuser.'_kwstyle'} = $ENV{'form.kwstyle'}; 1902: 1903: # message center - Order of message gets changed. Blank line is eliminated. 1904: # New messages are saved in ENV for the next student. 1905: # All messages are saved in nohist_handgrade.db 1906: my ($ctr,$idx) = (1,1); 1907: while ($ctr <= $ENV{'form.savemsgN'}) { 1908: if ($ENV{'form.savemsg'.$ctr} ne '') { 1909: $keyhash{$symb.'_savemsg'.$idx} = $ENV{'form.savemsg'.$ctr}; 1910: $idx++; 1911: } 1912: $ctr++; 1913: } 1914: $ctr = 0; 1915: while ($ctr < $ngrade) { 1916: if ($ENV{'form.newmsg'.$ctr} ne '') { 1917: $keyhash{$symb.'_savemsg'.$idx} = $ENV{'form.newmsg'.$ctr}; 1918: $ENV{'form.savemsg'.$idx} = $ENV{'form.newmsg'.$ctr}; 1919: $idx++; 1920: } 1921: $ctr++; 1922: } 1923: $ENV{'form.savemsgN'} = --$idx; 1924: $keyhash{$symb.'_savemsgN'} = $ENV{'form.savemsgN'}; 1925: my $putresult = &Apache::lonnet::put 1926: ('nohist_handgrade',\%keyhash, 1927: $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}, 1928: $ENV{'course.'.$ENV{'request.course.id'}.'.num'}); 1929: } 1930: # Called by Save & Refresh from Highlight Attribute Window 1931: my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'1'); 1932: if ($ENV{'form.refresh'} eq 'on') { 1933: my ($ctr,$total) = (0,0); 1934: while ($ctr < $ngrade) { 1935: $total++ if $ENV{'form.unamedom'.$ctr} ne ''; 1936: $ctr++; 1937: } 1938: $ENV{'form.NTSTU'}=$ngrade; 1939: $ctr = 0; 1940: while ($ctr < $total) { 1941: my $processUser = $ENV{'form.unamedom'.$ctr}; 1942: ($ENV{'form.student'},$ENV{'form.userdom'}) = split(/:/,$processUser); 1943: $ENV{'form.fullname'} = $$fullname{$processUser}; 1944: &submission($request,$ctr,$total-1); 1945: $ctr++; 1946: } 1947: return ''; 1948: } 1949: 1950: # Go directly to grade student - from submission or link from chart page 1951: if ($button eq 'Grade Student') { 1952: (undef,undef,$ENV{'form.handgrade'},undef,undef) = &showResourceInfo($url); 1953: my $processUser = $ENV{'form.unamedom'.$ENV{'form.studentNo'}}; 1954: ($ENV{'form.student'},$ENV{'form.userdom'}) = split(/:/,$processUser); 1955: $ENV{'form.fullname'} = $$fullname{$processUser}; 1956: &submission($request,0,0); 1957: return ''; 1958: } 1959: 1960: # Get the next/previous one or group of students 1961: my $firststu = $ENV{'form.unamedom0'}; 1962: my $laststu = $ENV{'form.unamedom'.($ngrade-1)}; 1963: my $ctr = 2; 1964: while ($laststu eq '') { 1965: $laststu = $ENV{'form.unamedom'.($ngrade-$ctr)}; 1966: $ctr++; 1967: $laststu = $firststu if ($ctr > $ngrade); 1968: } 1969: 1970: my (@parsedlist,@nextlist); 1971: my ($nextflg) = 0; 1972: foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) { 1973: if ($nextflg == 1 && $button =~ /Next$/) { 1974: push @parsedlist,$_; 1975: } 1976: $nextflg = 1 if ($_ eq $laststu); 1977: if ($button eq 'Previous') { 1978: last if ($_ eq $firststu); 1979: push @parsedlist,$_; 1980: } 1981: } 1982: $ctr = 0; 1983: @parsedlist = reverse @parsedlist if ($button eq 'Previous'); 1984: my ($partlist) = &response_type($url); 1985: foreach my $student (@parsedlist) { 1986: my $submitonly=$ENV{'form.submitonly'}; 1987: my ($uname,$udom) = split(/:/,$student); 1988: if ($submitonly =~ /^(yes|graded|incorrect)$/) { 1989: # my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname); 1990: my %status=&student_gradeStatus($url,$symb,$udom,$uname,$partlist); 1991: my $submitted = 0; 1992: my $graded = 1; 1993: foreach (keys(%status)) { 1994: $submitted = 1 if ($status{$_} ne 'nothing'); 1995: $graded = 0 if ($status{$_} =~ /^correct/); 1996: my ($foo,$partid,$foo1) = split(/\./,$_); 1997: if ($status{'resource.'.$partid.'.submitted_by'} ne '') { 1998: $submitted = 0; 1999: } 2000: } 2001: next if (!$submitted && ($submitonly eq 'yes' || 2002: $submitonly eq 'incorrect' || 2003: $submitonly eq 'graded')); 2004: next if (!$graded && ($submitonly eq 'graded' || 2005: $submitonly eq 'incorrect')); 2006: } 2007: push @nextlist,$student if ($ctr < $ntstu); 2008: last if ($ctr == $ntstu); 2009: $ctr++; 2010: } 2011: 2012: $ctr = 0; 2013: my $total = scalar(@nextlist)-1; 2014: 2015: foreach (sort @nextlist) { 2016: my ($uname,$udom,$submitter) = split(/:/); 2017: $ENV{'form.student'} = $uname; 2018: $ENV{'form.userdom'} = $udom; 2019: $ENV{'form.fullname'} = $$fullname{$_}; 2020: &submission($request,$ctr,$total); 2021: $ctr++; 2022: } 2023: if ($total < 0) { 2024: my $the_end = '<h3><font color="red">LON-CAPA User Message</font></h3><br />'."\n"; 2025: $the_end.='<b>Message: </b> No more students for this section or class.<br /><br />'."\n"; 2026: $the_end.='Click on the button below to return to the grading menu.<br /><br />'."\n"; 2027: $the_end.=&show_grading_menu_form ($symb,$url); 2028: $request->print($the_end); 2029: } 2030: return ''; 2031: } 2032: 2033: #---- Save the score and award for each student, if changed 2034: sub saveHandGrade { 2035: my ($request,$url,$symb,$stuname,$domain,$newflg,$submitter,$part) = @_; 2036: my $usec = &Apache::lonnet::getsection($domain,$stuname, 2037: $ENV{'request.course.id'}); 2038: if (!&canmodify($usec)) { return('not_allowed'); } 2039: my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$domain,$stuname); 2040: my %newrecord = (); 2041: my ($pts,$wgt) = ('',''); 2042: foreach (split(/:/,$ENV{'form.partlist'.$newflg})) { 2043: #collaborator may vary for different parts 2044: if ($submitter && $_ ne $part) { next; } 2045: my $dropMenu = $ENV{'form.GD_SEL'.$newflg.'_'.$_}; 2046: if ($dropMenu eq 'excused') { 2047: if ($record{'resource.'.$_.'.solved'} ne 'excused') { 2048: $newrecord{'resource.'.$_.'.solved'} = 'excused'; 2049: if (exists($record{'resource.'.$_.'.awarded'})) { 2050: $newrecord{'resource.'.$_.'.awarded'} = ''; 2051: } 2052: $newrecord{'resource.'.$_.'.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}"; 2053: } 2054: } elsif ($dropMenu eq 'reset status' 2055: && exists($record{'resource.'.$_.'.solved'})) { #don't bother if no old records -> no attempts 2056: foreach my $key (keys (%record)) { 2057: if ($key=~/^resource\.\Q$_\E\./) { $newrecord{$key} = ''; } 2058: } 2059: $newrecord{'resource.'.$_.'.regrader'}= 2060: "$ENV{'user.name'}:$ENV{'user.domain'}"; 2061: } elsif ($dropMenu eq '') { 2062: $pts = ($ENV{'form.GD_BOX'.$newflg.'_'.$_} ne '' ? 2063: $ENV{'form.GD_BOX'.$newflg.'_'.$_} : 2064: $ENV{'form.RADVAL'.$newflg.'_'.$_}); 2065: if ($pts eq '' && $ENV{'form.GD_SEL'.$newflg.'_'.$_} eq '') { 2066: next; 2067: } 2068: $wgt = $ENV{'form.WGT'.$newflg.'_'.$_} eq '' ? 1 : 2069: $ENV{'form.WGT'.$newflg.'_'.$_}; 2070: my $partial= $pts/$wgt; 2071: if ($partial eq $record{'resource.'.$_.'.awarded'}) { 2072: #do not update score for part if not changed. 2073: next; 2074: } 2075: if ($record{'resource.'.$_.'.awarded'} ne $partial) { 2076: $newrecord{'resource.'.$_.'.awarded'} = $partial; 2077: } 2078: my $reckey = 'resource.'.$_.'.solved'; 2079: if ($partial == 0) { 2080: if ($record{$reckey} ne 'incorrect_by_override') { 2081: $newrecord{$reckey} = 'incorrect_by_override'; 2082: } 2083: } else { 2084: if ($record{$reckey} ne 'correct_by_override') { 2085: $newrecord{$reckey} = 'correct_by_override'; 2086: } 2087: } 2088: if ($submitter && 2089: ($record{'resource.'.$_.'.submitted_by'} ne $submitter)) { 2090: $newrecord{'resource.'.$_.'.submitted_by'} = $submitter; 2091: } 2092: $newrecord{'resource.'.$_.'.regrader'}= 2093: "$ENV{'user.name'}:$ENV{'user.domain'}"; 2094: } 2095: } 2096: if (scalar(keys(%newrecord)) > 0) { 2097: &Apache::lonnet::cstore(\%newrecord,$symb, 2098: $ENV{'request.course.id'},$domain,$stuname); 2099: } 2100: return '',$pts,$wgt; 2101: } 2102: 2103: #-------------------------------------------------------------------------------------- 2104: # 2105: #-------------------------- Next few routines handles grading by section or whole class 2106: # 2107: #--- Javascript to handle grading by section or whole class 2108: sub viewgrades_js { 2109: my ($request) = shift; 2110: 2111: $request->print(<<VIEWJAVASCRIPT); 2112: <script type="text/javascript" language="javascript"> 2113: function writePoint(partid,weight,point) { 2114: var radioButton = document.classgrade["RADVAL_"+partid]; 2115: var textbox = document.classgrade["TEXTVAL_"+partid]; 2116: if (point == "textval") { 2117: point = document.classgrade["TEXTVAL_"+partid].value; 2118: if (isNaN(point) || parseFloat(point) < 0) { 2119: alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point)); 2120: var resetbox = false; 2121: for (var i=0; i<radioButton.length; i++) { 2122: if (radioButton[i].checked) { 2123: textbox.value = i; 2124: resetbox = true; 2125: } 2126: } 2127: if (!resetbox) { 2128: textbox.value = ""; 2129: } 2130: return; 2131: } 2132: if (parseFloat(point) > parseFloat(weight)) { 2133: var resp = confirm("You entered a value ("+parseFloat(point)+ 2134: ") greater than the weight for the part. Accept?"); 2135: if (resp == false) { 2136: textbox.value = ""; 2137: return; 2138: } 2139: } 2140: for (var i=0; i<radioButton.length; i++) { 2141: radioButton[i].checked=false; 2142: if (parseFloat(point) == i) { 2143: radioButton[i].checked=true; 2144: } 2145: } 2146: 2147: } else { 2148: textbox.value = parseFloat(point); 2149: } 2150: for (i=0;i<document.classgrade.total.value;i++) { 2151: var user = document.classgrade["ctr"+i].value; 2152: var scorename = document.classgrade["GD_"+user+"_"+partid+"_awarded"]; 2153: var saveval = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value; 2154: var selname = document.classgrade["GD_"+user+"_"+partid+"_solved"]; 2155: if (saveval != "correct") { 2156: scorename.value = point; 2157: if (selname[0].selected != true) { 2158: selname[0].selected = true; 2159: } 2160: } 2161: } 2162: document.classgrade["SELVAL_"+partid][0].selected = true; 2163: } 2164: 2165: function writeRadText(partid,weight) { 2166: var selval = document.classgrade["SELVAL_"+partid]; 2167: var radioButton = document.classgrade["RADVAL_"+partid]; 2168: var textbox = document.classgrade["TEXTVAL_"+partid]; 2169: if (selval[1].selected || selval[2].selected) { 2170: for (var i=0; i<radioButton.length; i++) { 2171: radioButton[i].checked=false; 2172: 2173: } 2174: textbox.value = ""; 2175: 2176: for (i=0;i<document.classgrade.total.value;i++) { 2177: var user = document.classgrade["ctr"+i].value; 2178: var scorename = document.classgrade["GD_"+user+"_"+partid+"_awarded"]; 2179: var saveval = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value; 2180: var selname = document.classgrade["GD_"+user+"_"+partid+"_solved"]; 2181: if (saveval != "correct") { 2182: scorename.value = ""; 2183: if (selval[1].selected) { 2184: selname[1].selected = true; 2185: } else { 2186: selname[2].selected = true; 2187: if (Number(document.classgrade["GD_"+user+"_"+partid+"_tries"].value)) 2188: {document.classgrade["GD_"+user+"_"+partid+"_tries"].value = '0';} 2189: } 2190: } 2191: } 2192: } else { 2193: for (i=0;i<document.classgrade.total.value;i++) { 2194: var user = document.classgrade["ctr"+i].value; 2195: var scorename = document.classgrade["GD_"+user+"_"+partid+"_awarded"]; 2196: var saveval = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value; 2197: var selname = document.classgrade["GD_"+user+"_"+partid+"_solved"]; 2198: if (saveval != "correct") { 2199: scorename.value = document.classgrade["GD_"+user+"_"+partid+"_awarded_s"].value; 2200: selname[0].selected = true; 2201: } 2202: } 2203: } 2204: } 2205: 2206: function changeSelect(partid,user) { 2207: var selval = document.classgrade["GD_"+user+'_'+partid+"_solved"]; 2208: var textbox = document.classgrade["GD_"+user+'_'+partid+"_awarded"]; 2209: var point = textbox.value; 2210: var weight = document.classgrade["weight_"+partid].value; 2211: 2212: if (isNaN(point) || parseFloat(point) < 0) { 2213: alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point)); 2214: textbox.value = ""; 2215: return; 2216: } 2217: if (parseFloat(point) > parseFloat(weight)) { 2218: var resp = confirm("You entered a value ("+parseFloat(point)+ 2219: ") greater than the weight of the part. Accept?"); 2220: if (resp == false) { 2221: textbox.value = ""; 2222: return; 2223: } 2224: } 2225: selval[0].selected = true; 2226: } 2227: 2228: function changeOneScore(partid,user) { 2229: var selval = document.classgrade["GD_"+user+'_'+partid+"_solved"]; 2230: if (selval[1].selected || selval[2].selected) { 2231: document.classgrade["GD_"+user+'_'+partid+"_awarded"].value = ""; 2232: if (selval[2].selected) { 2233: document.classgrade["GD_"+user+'_'+partid+"_tries"].value = "0"; 2234: } 2235: } 2236: } 2237: 2238: function resetEntry(numpart) { 2239: for (ctpart=0;ctpart<numpart;ctpart++) { 2240: var partid = document.classgrade["partid_"+ctpart].value; 2241: var radioButton = document.classgrade["RADVAL_"+partid]; 2242: var textbox = document.classgrade["TEXTVAL_"+partid]; 2243: var selval = document.classgrade["SELVAL_"+partid]; 2244: for (var i=0; i<radioButton.length; i++) { 2245: radioButton[i].checked=false; 2246: 2247: } 2248: textbox.value = ""; 2249: selval[0].selected = true; 2250: 2251: for (i=0;i<document.classgrade.total.value;i++) { 2252: var user = document.classgrade["ctr"+i].value; 2253: var resetscore = document.classgrade["GD_"+user+"_"+partid+"_awarded"]; 2254: resetscore.value = document.classgrade["GD_"+user+"_"+partid+"_awarded_s"].value; 2255: var resettries = document.classgrade["GD_"+user+"_"+partid+"_tries"]; 2256: resettries.value = document.classgrade["GD_"+user+"_"+partid+"_tries_s"].value; 2257: var saveselval = document.classgrade["GD_"+user+"_"+partid+"_solved_s"].value; 2258: var selname = document.classgrade["GD_"+user+"_"+partid+"_solved"]; 2259: if (saveselval == "excused") { 2260: if (selname[1].selected == false) { selname[1].selected = true;} 2261: } else { 2262: if (selname[0].selected == false) {selname[0].selected = true}; 2263: } 2264: } 2265: } 2266: } 2267: 2268: </script> 2269: VIEWJAVASCRIPT 2270: } 2271: 2272: #--- show scores for a section or whole class w/ option to change/update a score 2273: sub viewgrades { 2274: my ($request) = shift; 2275: &viewgrades_js($request); 2276: 2277: my ($symb,$url) = ($ENV{'form.symb'},$ENV{'form.url'}); 2278: #need to make sure we have the correct data for later EXT calls, 2279: #thus invalidate the cache 2280: &Apache::lonnet::devalidatecourseresdata( 2281: $ENV{'course.'.$ENV{'request.course.id'}.'.num'}, 2282: $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}); 2283: &Apache::lonnet::clear_EXT_cache_status(); 2284: 2285: my $result='<h3><font color="#339933">'.&mt('Manual Grading').'</font></h3>'; 2286: $result.='<font size=+1><b>Current Resource: </b>'.$ENV{'form.probTitle'}.'</font>'."\n"; 2287: 2288: #view individual student submission form - called using Javascript viewOneStudent 2289: $result.=&jscriptNform($url,$symb); 2290: 2291: #beginning of class grading form 2292: $result.= '<form action="/adm/grades" method="post" name="classgrade">'."\n". 2293: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n". 2294: '<input type="hidden" name="url" value="'.$url.'" />'."\n". 2295: '<input type="hidden" name="command" value="editgrades" />'."\n". 2296: '<input type="hidden" name="section" value="'.$ENV{'form.section'}.'" />'."\n". 2297: '<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."\n". 2298: '<input type="hidden" name="Status" value="'.$ENV{'form.Status'}.'" />'."\n". 2299: '<input type="hidden" name="probTitle" value="'.$ENV{'form.probTitle'}.'" />'."\n"; 2300: 2301: my $sectionClass; 2302: if ($ENV{'form.section'} eq 'all') { 2303: $sectionClass='Class </h3>'; 2304: } elsif ($ENV{'form.section'} eq 'none') { 2305: $sectionClass='Students in no Section </h3>'; 2306: } else { 2307: $sectionClass='Students in Section '.$ENV{'form.section'}.'</h3>'; 2308: } 2309: $result.='<h3>Assign Common Grade To '.$sectionClass; 2310: $result.= '<table border=0><tr><td bgcolor="#777777">'."\n". 2311: '<table border=0><tr bgcolor="#ffffdd"><td>'; 2312: #radio buttons/text box for assigning points for a section or class. 2313: #handles different parts of a problem 2314: my ($partlist,$handgrade) = &response_type($url,$symb); 2315: my %weight = (); 2316: my $ctsparts = 0; 2317: $result.='<table border="0">'; 2318: my %seen = (); 2319: for (sort keys(%$handgrade)) { 2320: my ($partid,$respid) = split (/_/,$_,2); 2321: next if $seen{$partid}; 2322: $seen{$partid}++; 2323: my $handgrade=$$handgrade{$_}; 2324: my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb); 2325: $weight{$partid} = $wgt eq '' ? '1' : $wgt; 2326: 2327: $result.='<input type="hidden" name="partid_'. 2328: $ctsparts.'" value="'.$partid.'" />'."\n"; 2329: $result.='<input type="hidden" name="weight_'. 2330: $partid.'" value="'.$weight{$partid}.'" />'."\n"; 2331: my $display_part=&get_display_part($partid,$url,$symb); 2332: $result.='<tr><td><b>Part:</b> '.$display_part.' <b>Point:</b> </td><td>'; 2333: $result.='<table border="0"><tr>'; 2334: my $ctr = 0; 2335: while ($ctr<=$weight{$partid}) { # display radio buttons in a nice table 10 across 2336: $result.= '<td><input type="radio" name="RADVAL_'.$partid.'" '. 2337: 'onclick="javascript:writePoint(\''.$partid.'\','.$weight{$partid}. 2338: ','.$ctr.')" />'.$ctr."</td>\n"; 2339: $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : ''); 2340: $ctr++; 2341: } 2342: $result.='</tr></table>'; 2343: $result.= '</td><td><b> or </b><input type="text" name="TEXTVAL_'. 2344: $partid.'" size="4" '.'onChange="javascript:writePoint(\''. 2345: $partid.'\','.$weight{$partid}.',\'textval\')" /> /'. 2346: $weight{$partid}.' (problem weight)</td>'."\n"; 2347: $result.= '</td><td><select name="SELVAL_'.$partid.'"'. 2348: 'onChange="javascript:writeRadText(\''.$partid.'\','. 2349: $weight{$partid}.')"> '. 2350: '<option selected="on"> </option>'. 2351: '<option>excused</option>'. 2352: '<option>reset status</option></select></td></tr>'."\n"; 2353: $ctsparts++; 2354: } 2355: $result.='</table>'.'</td></tr></table>'.'</td></tr></table>'."\n". 2356: '<input type="hidden" name="totalparts" value="'.$ctsparts.'" />'; 2357: $result.='<input type="button" value="Reset" '. 2358: 'onClick="javascript:resetEntry('.$ctsparts.');" TARGET=_self>'; 2359: 2360: #table listing all the students in a section/class 2361: #header of table 2362: $result.= '<h3>Assign Grade to Specific Students in '.$sectionClass; 2363: $result.= '<table border=0><tr><td bgcolor="#777777">'."\n". 2364: '<table border=0><tr bgcolor="#deffff"><td> <b>No.</b> </td>'. 2365: '<td>'.&nameUserString('header')."</td>\n"; 2366: my (@parts) = sort(&getpartlist($url,$symb)); 2367: foreach my $part (@parts) { 2368: my $display=&Apache::lonnet::metadata($url,$part.'.display'); 2369: $display =~ s|^Number of Attempts|Tries<br />|; # makes the column narrower 2370: if (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); } 2371: my ($partid) = &split_part_type($part); 2372: my $display_part=&get_display_part($partid,$url,$symb); 2373: if ($display =~ /^Partial Credit Factor/) { 2374: $result.='<td><b>Score Part:</b> '.$display_part. 2375: ' <br /><b>(weight = '.$weight{$partid}.')</b></td>'."\n"; 2376: next; 2377: } else { 2378: $display =~s/\[Part: \Q$partid\E\]/Part:<\/b> $display_part/; 2379: } 2380: $display =~ s|Problem Status|Grade Status<br />|; 2381: $result.='<td><b>'.$display.'</td>'."\n"; 2382: } 2383: $result.='</tr>'; 2384: 2385: #get info for each student 2386: #list all the students - with points and grade status 2387: my (undef,undef,$fullname) = &getclasslist($ENV{'form.section'},'1'); 2388: my $ctr = 0; 2389: foreach (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) { 2390: my $uname = $_; 2391: $uname=~s/:/_/; 2392: $result.='<input type="hidden" name="ctr'.$ctr.'" value="'.$uname.'" />'."\n"; 2393: $ctr++; 2394: $result.=&viewstudentgrade($url,$symb,$ENV{'request.course.id'}, 2395: $_,$$fullname{$_},\@parts,\%weight,$ctr); 2396: } 2397: $result.='</table></td></tr></table>'; 2398: $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n"; 2399: $result.='<input type="button" value="Save" '. 2400: 'onClick="javascript:submit();" TARGET=_self /></form>'."\n"; 2401: if (scalar(%$fullname) eq 0) { 2402: my $colspan=3+scalar(@parts); 2403: $result='<font color="red">There are no students in section "'.$ENV{'form.section'}. 2404: '" with enrollment status "'.$ENV{'form.Status'}.'" to modify or grade.</font>'; 2405: } 2406: $result.=&show_grading_menu_form($symb,$url); 2407: return $result; 2408: } 2409: 2410: #--- call by previous routine to display each student 2411: sub viewstudentgrade { 2412: my ($url,$symb,$courseid,$student,$fullname,$parts,$weight,$ctr) = @_; 2413: my ($uname,$udom) = split(/:/,$student); 2414: $student=~s/:/_/; 2415: my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname); 2416: my $result='<tr bgcolor="#ffffdd"><td align="right">'.$ctr.' </td><td> '. 2417: '<a href="javascript:viewOneStudent(\''.$uname.'\',\''.$udom. 2418: '\')"; TARGET=_self>'.$fullname.'</a> '. 2419: '<font color="#999999">('.$uname.($ENV{'user.domain'} eq $udom ? '' : ':'.$udom).')</font></td>'."\n"; 2420: foreach my $apart (@$parts) { 2421: my ($part,$type) = &split_part_type($apart); 2422: my $score=$record{"resource.$part.$type"}; 2423: if ($type eq 'awarded') { 2424: my $pts = $score eq '' ? '' : $score*$$weight{$part}; 2425: $result.='<input type="hidden" name="'. 2426: 'GD_'.$student.'_'.$part.'_awarded_s" value="'.$pts.'" />'."\n"; 2427: $result.='<td align="middle"><input type="text" name="'. 2428: 'GD_'.$student.'_'.$part.'_awarded" '. 2429: 'onChange="javascript:changeSelect(\''.$part.'\',\''.$student. 2430: '\')" value="'.$pts.'" size="4" /></td>'."\n"; 2431: } elsif ($type eq 'solved') { 2432: my ($status,$foo)=split(/_/,$score,2); 2433: $status = 'nothing' if ($status eq ''); 2434: $result.='<input type="hidden" name="'.'GD_'.$student.'_'. 2435: $part.'_solved_s" value="'.$status.'" />'."\n"; 2436: $result.='<td align="middle"> <select name="'. 2437: 'GD_'.$student.'_'.$part.'_solved" '. 2438: 'onChange="javascript:changeOneScore(\''.$part.'\',\''.$student.'\')" >'."\n"; 2439: $result.= (($status eq 'excused') ? '<option> </option><option selected="on">excused</option>' 2440: : '<option selected="on"> </option><option>excused</option>')."\n"; 2441: $result.='<option>reset status</option>'; 2442: $result.="</select> </td>\n"; 2443: } else { 2444: $result.='<input type="hidden" name="'. 2445: 'GD_'.$student.'_'.$part.'_'.$type.'_s" value="'.$score.'" />'. 2446: "\n"; 2447: $result.='<td align="middle"><input type="text" name="'. 2448: 'GD_'.$student.'_'.$part.'_'.$type.'" '. 2449: 'value="'.$score.'" size="4" /></td>'."\n"; 2450: } 2451: } 2452: $result.='</tr>'; 2453: return $result; 2454: } 2455: 2456: #--- change scores for all the students in a section/class 2457: # record does not get update if unchanged 2458: sub editgrades { 2459: my ($request) = @_; 2460: 2461: my $symb=$ENV{'form.symb'}; 2462: my $url =$ENV{'form.url'}; 2463: my $title='<h3><font color="#339933">Current Grade Status</font></h3>'; 2464: $title.='<font size=+1><b>Current Resource: </b>'.$ENV{'form.probTitle'}.'</font><br />'."\n"; 2465: $title.='<font size=+1><b>Section: </b>'.$ENV{'form.section'}.'</font>'."\n"; 2466: 2467: my $result= '<table border="0"><tr><td bgcolor="#777777">'."\n"; 2468: $result.= '<table border="0"><tr bgcolor="#deffff">'. 2469: '<td rowspan=2 valign="center"> <b>No.</b> </td>'. 2470: '<td rowspan=2 valign="center">'.&nameUserString('header')."</td>\n"; 2471: 2472: my %scoreptr = ( 2473: 'correct' =>'correct_by_override', 2474: 'incorrect'=>'incorrect_by_override', 2475: 'excused' =>'excused', 2476: 'ungraded' =>'ungraded_attempted', 2477: 'nothing' => '', 2478: ); 2479: my ($classlist,undef,$fullname) = &getclasslist($ENV{'form.section'},'0'); 2480: 2481: my (@partid); 2482: my %weight = (); 2483: my %columns = (); 2484: my ($i,$ctr,$count,$rec_update) = (0,0,0,0); 2485: 2486: my (@parts) = sort(&getpartlist($url,$symb)); 2487: my $header; 2488: while ($ctr < $ENV{'form.totalparts'}) { 2489: my $partid = $ENV{'form.partid_'.$ctr}; 2490: push @partid,$partid; 2491: $weight{$partid} = $ENV{'form.weight_'.$partid}; 2492: $ctr++; 2493: } 2494: foreach my $partid (@partid) { 2495: $header .= '<td align="center"> <b>Old Score</b> </td>'. 2496: '<td align="center"> <b>New Score</b> </td>'; 2497: $columns{$partid}=2; 2498: foreach my $stores (@parts) { 2499: my ($part,$type) = &split_part_type($stores); 2500: if ($part !~ m/^\Q$partid\E/) { next;} 2501: if ($type eq 'awarded' || $type eq 'solved') { next; } 2502: my $display=&Apache::lonnet::metadata($url,$stores.'.display'); 2503: $display =~ s/\[Part: (\w)+\]//; 2504: $display =~ s/Number of Attempts/Tries/; 2505: $header .= '<td align="center"> <b>Old '.$display.'</b> </td>'. 2506: '<td align="center"> <b>New '.$display.'</b> </td>'; 2507: $columns{$partid}+=2; 2508: } 2509: } 2510: foreach my $partid (@partid) { 2511: my $display_part=&get_display_part($partid,$url,$symb); 2512: $result .= '<td colspan="'.$columns{$partid}. 2513: '" align="center"><b>Part:</b> '.$display_part. 2514: ' (Weight = '.$weight{$partid}.')</td>'; 2515: 2516: } 2517: $result .= '</tr><tr bgcolor="#deffff">'; 2518: $result .= $header; 2519: $result .= '</tr>'."\n"; 2520: my $noupdate; 2521: my ($updateCtr,$noupdateCtr) = (1,1); 2522: for ($i=0; $i<$ENV{'form.total'}; $i++) { 2523: my $line; 2524: my $user = $ENV{'form.ctr'.$i}; 2525: my $usercolon = $user; 2526: $usercolon =~s/_/:/; 2527: my ($uname,$udom)=split(/_/,$user); 2528: my %newrecord; 2529: my $updateflag = 0; 2530: $line .= '<td>'.&nameUserString(undef,$$fullname{$usercolon},$uname,$udom).'</td>'; 2531: my $usec=$classlist->{"$uname:$udom"}[5]; 2532: if (!&canmodify($usec)) { 2533: my $numcols=scalar(@partid)*4+2; 2534: $noupdate.=$line."<td colspan=\"$numcols\"><font color=\"red\">Not allowed to modify student</font></td></tr>"; 2535: next; 2536: } 2537: foreach (@partid) { 2538: my $old_aw = $ENV{'form.GD_'.$user.'_'.$_.'_awarded_s'}; 2539: my $old_part_pcr = $old_aw/($weight{$_} ne '0' ? $weight{$_}:1); 2540: my $old_part = $old_aw eq '' ? '' : $old_part_pcr; 2541: my $old_score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_solved_s'}}; 2542: 2543: my $awarded = $ENV{'form.GD_'.$user.'_'.$_.'_awarded'}; 2544: my $pcr = $awarded/($weight{$_} ne '0' ? $weight{$_} : 1); 2545: my $partial = $awarded eq '' ? '' : $pcr; 2546: my $score; 2547: if ($partial eq '') { 2548: $score = $scoreptr{$ENV{'form.GD_'.$user.'_'.$_.'_solved_s'}}; 2549: } elsif ($partial > 0) { 2550: $score = 'correct_by_override'; 2551: } elsif ($partial == 0) { 2552: $score = 'incorrect_by_override'; 2553: } 2554: my $dropMenu = $ENV{'form.GD_'.$user.'_'.$_.'_solved'}; 2555: $score = 'excused' if (($dropMenu eq 'excused') && ($score ne 'excused')); 2556: 2557: if ($dropMenu eq 'reset status' && 2558: $old_score ne '') { # ignore if no previous attempts => nothing to reset 2559: $newrecord{'resource.'.$_.'.tries'} = 0; 2560: $newrecord{'resource.'.$_.'.solved'} = ''; 2561: $newrecord{'resource.'.$_.'.award'} = ''; 2562: $newrecord{'resource.'.$_.'.awarded'} = 0; 2563: $newrecord{'resource.'.$_.'.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}"; 2564: $updateflag = 1; 2565: } elsif (!($old_part eq $partial && $old_score eq $score)) { 2566: $updateflag = 1; 2567: $newrecord{'resource.'.$_.'.awarded'} = $partial if $partial ne ''; 2568: $newrecord{'resource.'.$_.'.solved'} = $score; 2569: $rec_update++; 2570: } 2571: 2572: $line .= '<td align="center">'.$old_aw.' </td>'. 2573: '<td align="center">'.$awarded. 2574: ($score eq 'excused' ? $score : '').' </td>'; 2575: 2576: 2577: my $partid=$_; 2578: foreach my $stores (@parts) { 2579: my ($part,$type) = &split_part_type($stores); 2580: if ($part !~ m/^\Q$partid\E/) { next;} 2581: if ($type eq 'awarded' || $type eq 'solved') { next; } 2582: my $old_aw = $ENV{'form.GD_'.$user.'_'.$part.'_'.$type.'_s'}; 2583: my $awarded = $ENV{'form.GD_'.$user.'_'.$part.'_'.$type}; 2584: if ($awarded ne '' && $awarded ne $old_aw) { 2585: $newrecord{'resource.'.$part.'.'.$type}= $awarded; 2586: $newrecord{'resource.'.$part.'.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}"; 2587: $updateflag=1; 2588: } 2589: $line .= '<td align="center">'.$old_aw.' </td>'. 2590: '<td align="center">'.$awarded.' </td>'; 2591: } 2592: } 2593: $line.='</tr>'."\n"; 2594: if ($updateflag) { 2595: $count++; 2596: &Apache::lonnet::cstore(\%newrecord,$symb,$ENV{'request.course.id'}, 2597: $udom,$uname); 2598: $result.='<tr bgcolor="#ffffde"><td align="right"> '.$updateCtr.' </td>'.$line; 2599: $updateCtr++; 2600: } else { 2601: $noupdate.='<tr bgcolor="#ffffde"><td align="right"> '.$noupdateCtr.' </td>'.$line; 2602: $noupdateCtr++; 2603: } 2604: } 2605: if ($noupdate) { 2606: # my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3; 2607: my $numcols=scalar(@partid)*4+2; 2608: $result .= '<tr bgcolor="#ffffff"><td align="center" colspan="'.$numcols.'">No Changes Occurred For the Students Below</td></tr><tr bgcolor="#ffffde">'.$noupdate; 2609: } 2610: $result .= '</table></td></tr></table>'."\n". 2611: &show_grading_menu_form ($symb,$url); 2612: my $msg = '<br /><b>Number of records updated = '.$rec_update. 2613: ' for '.$count.' student'.($count <= 1 ? '' : 's').'.</b><br />'. 2614: '<b>Total number of students = '.$ENV{'form.total'}.'</b><br />'; 2615: return $title.$msg.$result; 2616: } 2617: 2618: sub split_part_type { 2619: my ($partstr) = @_; 2620: my ($temp,@allparts)=split(/_/,$partstr); 2621: my $type=pop(@allparts); 2622: my $part=join('.',@allparts); 2623: return ($part,$type); 2624: } 2625: 2626: #------------- end of section for handling grading by section/class --------- 2627: # 2628: #---------------------------------------------------------------------------- 2629: 2630: 2631: #---------------------------------------------------------------------------- 2632: # 2633: #-------------------------- Next few routines handles grading by csv upload 2634: # 2635: #--- Javascript to handle csv upload 2636: sub csvupload_javascript_reverse_associate { 2637: return(<<ENDPICK); 2638: function verify(vf) { 2639: var foundsomething=0; 2640: var founduname=0; 2641: var founddomain=0; 2642: for (i=0;i<=vf.nfields.value;i++) { 2643: tw=eval('vf.f'+i+'.selectedIndex'); 2644: if (i==0 && tw!=0) { founduname=1; } 2645: if (i==1 && tw!=0) { founddomain=1; } 2646: if (i!=0 && i!=1 && tw!=0) { foundsomething=1; } 2647: } 2648: if (founduname==0 || founddomain==0) { 2649: alert('You need to specify at both the username and domain'); 2650: return; 2651: } 2652: if (foundsomething==0) { 2653: alert('You need to specify at least one grading field'); 2654: return; 2655: } 2656: vf.submit(); 2657: } 2658: function flip(vf,tf) { 2659: var nw=eval('vf.f'+tf+'.selectedIndex'); 2660: var i; 2661: for (i=0;i<=vf.nfields.value;i++) { 2662: //can not pick the same destination field for both name and domain 2663: if (((i ==0)||(i ==1)) && 2664: ((tf==0)||(tf==1)) && 2665: (i!=tf) && 2666: (eval('vf.f'+i+'.selectedIndex')==nw)) { 2667: eval('vf.f'+i+'.selectedIndex=0;') 2668: } 2669: } 2670: } 2671: ENDPICK 2672: } 2673: 2674: sub csvupload_javascript_forward_associate { 2675: return(<<ENDPICK); 2676: function verify(vf) { 2677: var foundsomething=0; 2678: var founduname=0; 2679: var founddomain=0; 2680: for (i=0;i<=vf.nfields.value;i++) { 2681: tw=eval('vf.f'+i+'.selectedIndex'); 2682: if (tw==1) { founduname=1; } 2683: if (tw==2) { founddomain=1; } 2684: if (tw>2) { foundsomething=1; } 2685: } 2686: if (founduname==0 || founddomain==0) { 2687: alert('You need to specify at both the username and domain'); 2688: return; 2689: } 2690: if (foundsomething==0) { 2691: alert('You need to specify at least one grading field'); 2692: return; 2693: } 2694: vf.submit(); 2695: } 2696: function flip(vf,tf) { 2697: var nw=eval('vf.f'+tf+'.selectedIndex'); 2698: var i; 2699: //can not pick the same destination field twice 2700: for (i=0;i<=vf.nfields.value;i++) { 2701: if ((i!=tf) && (eval('vf.f'+i+'.selectedIndex')==nw)) { 2702: eval('vf.f'+i+'.selectedIndex=0;') 2703: } 2704: } 2705: } 2706: ENDPICK 2707: } 2708: 2709: sub csvuploadmap_header { 2710: my ($request,$symb,$url,$datatoken,$distotal)= @_; 2711: my $javascript; 2712: if ($ENV{'form.upfile_associate'} eq 'reverse') { 2713: $javascript=&csvupload_javascript_reverse_associate(); 2714: } else { 2715: $javascript=&csvupload_javascript_forward_associate(); 2716: } 2717: 2718: my ($result) = &showResourceInfo($url,$ENV{'form.probTitle'}); 2719: 2720: $request->print(<<ENDPICK); 2721: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload"> 2722: <h3><font color="#339933">Uploading Class Grades</font></h3> 2723: $result 2724: <hr> 2725: <h3>Identify fields</h3> 2726: Total number of records found in file: $distotal <hr /> 2727: Enter as many fields as you can. The system will inform you and bring you back 2728: to this page if the data selected is insufficient to run your class.<hr /> 2729: <input type="button" value="Reverse Association" onClick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" /> 2730: <input type="hidden" name="associate" value="" /> 2731: <input type="hidden" name="phase" value="three" /> 2732: <input type="hidden" name="datatoken" value="$datatoken" /> 2733: <input type="hidden" name="fileupload" value="$ENV{'form.fileupload'}" /> 2734: <input type="hidden" name="upfiletype" value="$ENV{'form.upfiletype'}" /> 2735: <input type="hidden" name="upfile_associate" 2736: value="$ENV{'form.upfile_associate'}" /> 2737: <input type="hidden" name="symb" value="$symb" /> 2738: <input type="hidden" name="url" value="$url" /> 2739: <input type="hidden" name="saveState" value="$ENV{'form.saveState'}" /> 2740: <input type="hidden" name="probTitle" value="$ENV{'form.probTitle'}" /> 2741: <input type="hidden" name="command" value="csvuploadassign" /> 2742: <hr /> 2743: <script type="text/javascript" language="Javascript"> 2744: $javascript 2745: </script> 2746: ENDPICK 2747: return ''; 2748: 2749: } 2750: 2751: sub csvupload_fields { 2752: my ($url,$symb) = @_; 2753: my (@parts) = &getpartlist($url,$symb); 2754: my @fields=(['username','Student Username'],['domain','Student Domain']); 2755: foreach my $part (sort(@parts)) { 2756: my @datum; 2757: my $display=&Apache::lonnet::metadata($url,$part.'.display'); 2758: my $name=$part; 2759: if (!$display) { $display = $name; } 2760: @datum=($name,$display); 2761: push(@fields,\@datum); 2762: } 2763: return (@fields); 2764: } 2765: 2766: sub csvuploadmap_footer { 2767: my ($request,$i,$keyfields) =@_; 2768: $request->print(<<ENDPICK); 2769: </table> 2770: <input type="hidden" name="nfields" value="$i" /> 2771: <input type="hidden" name="keyfields" value="$keyfields" /> 2772: <input type="button" onClick="javascript:verify(this.form)" value="Assign Grades" /><br /> 2773: </form> 2774: ENDPICK 2775: } 2776: 2777: sub upcsvScores_form { 2778: my ($request) = shift; 2779: my ($symb,$url)=&get_symb_and_url($request); 2780: if (!$symb) {return '';} 2781: my $result =<<CSVFORMJS; 2782: <script type="text/javascript" language="javascript"> 2783: function checkUpload(formname) { 2784: if (formname.upfile.value == "") { 2785: alert("Please use the browse button to select a file from your local directory."); 2786: return false; 2787: } 2788: formname.submit(); 2789: } 2790: </script> 2791: CSVFORMJS 2792: $ENV{'form.probTitle'} = &Apache::lonnet::gettitle($symb); 2793: my ($table) = &showResourceInfo($url,$ENV{'form.probTitle'}); 2794: $result.=$table; 2795: $result.='<br /><table width=100% border=0><tr><td bgcolor="#777777">'."\n"; 2796: $result.='<table width=100% border=0><tr bgcolor="#e6ffff"><td>'."\n"; 2797: $result.=' <b>Specify a file containing the class scores for current resource'. 2798: '.</b></td></tr>'."\n"; 2799: $result.='<tr bgcolor=#ffffe6><td>'."\n"; 2800: my $upfile_select=&Apache::loncommon::upfile_select_html(); 2801: $result.=<<ENDUPFORM; 2802: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="gradesupload"> 2803: <input type="hidden" name="symb" value="$symb" /> 2804: <input type="hidden" name="url" value="$url" /> 2805: <input type="hidden" name="command" value="csvuploadmap" /> 2806: <input type="hidden" name="probTitle" value="$ENV{'form.probTitle'}" /> 2807: <input type="hidden" name="saveState" value="$ENV{'form.saveState'}" /> 2808: $upfile_select 2809: <br /><input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scores" /> 2810: 2811: </form> 2812: ENDUPFORM 2813: $result.='</td></tr></table>'."\n"; 2814: $result.='</td></tr></table><br /><br />'."\n"; 2815: $result.=&show_grading_menu_form($symb,$url); 2816: return $result; 2817: } 2818: 2819: 2820: sub csvuploadmap { 2821: my ($request)= @_; 2822: my ($symb,$url)=&get_symb_and_url($request); 2823: if (!$symb) {return '';} 2824: 2825: my $datatoken; 2826: if (!$ENV{'form.datatoken'}) { 2827: $datatoken=&Apache::loncommon::upfile_store($request); 2828: } else { 2829: $datatoken=$ENV{'form.datatoken'}; 2830: &Apache::loncommon::load_tmp_file($request); 2831: } 2832: my @records=&Apache::loncommon::upfile_record_sep(); 2833: &csvuploadmap_header($request,$symb,$url,$datatoken,$#records+1); 2834: my ($i,$keyfields); 2835: if (@records) { 2836: my @fields=&csvupload_fields($url,$symb); 2837: 2838: if ($ENV{'form.upfile_associate'} eq 'reverse') { 2839: &Apache::loncommon::csv_print_samples($request,\@records); 2840: $i=&Apache::loncommon::csv_print_select_table($request,\@records, 2841: \@fields); 2842: foreach (@fields) { $keyfields.=$_->[0].','; } 2843: chop($keyfields); 2844: } else { 2845: unshift(@fields,['none','']); 2846: $i=&Apache::loncommon::csv_samples_select_table($request,\@records, 2847: \@fields); 2848: my %sone=&Apache::loncommon::record_sep($records[0]); 2849: $keyfields=join(',',sort(keys(%sone))); 2850: } 2851: } 2852: &csvuploadmap_footer($request,$i,$keyfields); 2853: $request->print(&show_grading_menu_form($symb,$url)); 2854: 2855: return ''; 2856: } 2857: 2858: sub csvuploadassign { 2859: my ($request)= @_; 2860: my ($symb,$url)=&get_symb_and_url($request); 2861: if (!$symb) {return '';} 2862: &Apache::loncommon::load_tmp_file($request); 2863: my @gradedata = &Apache::loncommon::upfile_record_sep(); 2864: my @keyfields = split(/\,/,$ENV{'form.keyfields'}); 2865: my %fields=(); 2866: for (my $i=0; $i<=$ENV{'form.nfields'}; $i++) { 2867: if ($ENV{'form.upfile_associate'} eq 'reverse') { 2868: if ($ENV{'form.f'.$i} ne 'none') { 2869: $fields{$keyfields[$i]}=$ENV{'form.f'.$i}; 2870: } 2871: } else { 2872: if ($ENV{'form.f'.$i} ne 'none') { 2873: $fields{$ENV{'form.f'.$i}}=$keyfields[$i]; 2874: } 2875: } 2876: } 2877: $request->print('<h3>Assigning Grades</h3>'); 2878: my $courseid=$ENV{'request.course.id'}; 2879: my ($classlist) = &getclasslist('all',0); 2880: my @notallowed; 2881: my @skipped; 2882: my $countdone=0; 2883: foreach my $grade (@gradedata) { 2884: my %entries=&Apache::loncommon::record_sep($grade); 2885: my $username=$entries{$fields{'username'}}; 2886: $username=~s/\s//g; 2887: my $domain=$entries{$fields{'domain'}}; 2888: $domain=~s/\s//g; 2889: if (!exists($$classlist{"$username:$domain"})) { 2890: push(@skipped,"$username:$domain"); 2891: next; 2892: } 2893: my $usec=$classlist->{"$username:$domain"}[5]; 2894: if (!&canmodify($usec)) { 2895: push(@notallowed,"$username:$domain"); 2896: next; 2897: } 2898: my %grades; 2899: foreach my $dest (keys(%fields)) { 2900: if ($dest eq 'username' || $dest eq 'domain') { next; } 2901: if ($entries{$fields{$dest}} eq '') { next; } 2902: my $store_key=$dest; 2903: $store_key=~s/^stores/resource/; 2904: $store_key=~s/_/\./g; 2905: $grades{$store_key}=$entries{$fields{$dest}}; 2906: } 2907: $grades{"resource.regrader"}="$ENV{'user.name'}:$ENV{'user.domain'}"; 2908: &Apache::lonnet::cstore(\%grades,$symb,$ENV{'request.course.id'}, 2909: $domain,$username); 2910: $request->print('.'); 2911: $request->rflush(); 2912: $countdone++; 2913: } 2914: $request->print("<br />Stored $countdone students\n"); 2915: if (@skipped) { 2916: $request->print('<p<font size="+1"><b>Skipped Students</b></font></p>'); 2917: foreach my $student (@skipped) { $request->print("$student<br />\n"); } 2918: } 2919: if (@notallowed) { 2920: $request->print('<p><font size="+1" color="red"><b>Students Not Allowed to Modify</b></font></p>'); 2921: foreach my $student (@notallowed) { $request->print("$student<br />\n"); } 2922: } 2923: $request->print("<br />\n"); 2924: $request->print(&show_grading_menu_form($symb,$url)); 2925: return ''; 2926: } 2927: #------------- end of section for handling csv file upload --------- 2928: # 2929: #------------------------------------------------------------------- 2930: # 2931: #-------------- Next few routines handle grading by page/sequence 2932: # 2933: #--- Select a page/sequence and a student to grade 2934: sub pickStudentPage { 2935: my ($request) = shift; 2936: 2937: $request->print(<<LISTJAVASCRIPT); 2938: <script type="text/javascript" language="javascript"> 2939: 2940: function checkPickOne(formname) { 2941: if (radioSelection(formname.student) == null) { 2942: alert("Please select the student you wish to grade."); 2943: return; 2944: } 2945: ptr = pullDownSelection(formname.selectpage); 2946: formname.page.value = formname["page"+ptr].value; 2947: formname.title.value = formname["title"+ptr].value; 2948: formname.submit(); 2949: } 2950: 2951: </script> 2952: LISTJAVASCRIPT 2953: &commonJSfunctions($request); 2954: my ($symb,$url) = &get_symb_and_url($request); 2955: my $cdom = $ENV{"course.$ENV{'request.course.id'}.domain"}; 2956: my $cnum = $ENV{"course.$ENV{'request.course.id'}.num"}; 2957: my $getsec = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'}; 2958: 2959: my $result='<h3><font color="#339933"> '. 2960: 'Manual Grading by Page or Sequence</font></h3>'; 2961: 2962: $result.='<form action="/adm/grades" method="post" name="displayPage">'."\n"; 2963: $result.=' <b>Problems from:</b> <select name="selectpage">'."\n"; 2964: my ($titles,$symbx) = &getSymbMap($request); 2965: my ($curpage) =&Apache::lonnet::decode_symb($symb); 2966: # my ($curpage,$mapId) =&Apache::lonnet::decode_symb($symb); 2967: # my $type=($curpage =~ /\.(page|sequence)/); 2968: my $ctr=0; 2969: foreach (@$titles) { 2970: my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/); 2971: $result.='<option value="'.$ctr.'" '. 2972: ($$symbx{$_} =~ /$curpage$/ ? 'selected="on"' : ''). 2973: '>'.$showtitle.'</option>'."\n"; 2974: $ctr++; 2975: } 2976: $result.= '</select>'."<br>\n"; 2977: $ctr=0; 2978: foreach (@$titles) { 2979: my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/); 2980: $result.='<input type="hidden" name="page'.$ctr.'" value="'.$$symbx{$_}.'" />'."\n"; 2981: $result.='<input type="hidden" name="title'.$ctr.'" value="'.$showtitle.'" />'."\n"; 2982: $ctr++; 2983: } 2984: $result.='<input type="hidden" name="page" />'."\n". 2985: '<input type="hidden" name="title" />'."\n"; 2986: 2987: $result.=' <b>View Problems Text: </b><input type="radio" name="vProb" value="no" checked="on" /> no '."\n". 2988: '<input type="radio" name="vProb" value="yes" /> yes '."<br>\n"; 2989: 2990: $result.=' <b>Submission Details: </b>'. 2991: '<input type="radio" name="lastSub" value="none" /> none'."\n". 2992: '<input type="radio" name="lastSub" value="datesub" checked /> by dates and submissions'."\n". 2993: '<input type="radio" name="lastSub" value="all" /> all details'."\n"; 2994: 2995: $result.='<input type="hidden" name="section" value="'.$getsec.'" />'."\n". 2996: '<input type="hidden" name="Status" value="'.$ENV{'form.Status'}.'" />'."\n". 2997: '<input type="hidden" name="command" value="displayPage" />'."\n". 2998: '<input type="hidden" name="url" value="'.$url.'" />'."\n". 2999: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n". 3000: '<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."<br />\n"; 3001: 3002: $result.=' <input type="button" '. 3003: 'onClick="javascript:checkPickOne(this.form);"value="Next->" /><br />'."\n"; 3004: 3005: $request->print($result); 3006: 3007: my $studentTable.=' <b>Select a student you wish to grade and then click on the Next button.</b><br>'. 3008: '<table border="0"><tr><td bgcolor="#777777">'. 3009: '<table border="0"><tr bgcolor="#e6ffff">'. 3010: '<td align="right"> <b>No.</b></td>'. 3011: '<td>'.&nameUserString('header').'</td>'. 3012: '<td align="right"> <b>No.</b></td>'. 3013: '<td>'.&nameUserString('header').'</td></tr>'; 3014: 3015: my (undef,undef,$fullname) = &getclasslist($getsec,'1'); 3016: my $ptr = 1; 3017: foreach my $student (sort {lc($$fullname{$a}) cmp lc($$fullname{$b}) } keys %$fullname) { 3018: my ($uname,$udom) = split(/:/,$student); 3019: $studentTable.=($ptr%2 == 1 ? '<tr bgcolor="#ffffe6">' : '</td>'); 3020: $studentTable.='<td align="right">'.$ptr.' </td>'; 3021: $studentTable.='<td> <input type="radio" name="student" value="'.$student.'" /> ' 3022: .&nameUserString(undef,$$fullname{$student},$uname,$udom)."\n"; 3023: $studentTable.=($ptr%2 == 0 ? '</td></tr>' : ''); 3024: $ptr++; 3025: } 3026: $studentTable.='</td><td> </td><td> ' if ($ptr%2 == 0); 3027: $studentTable.='</td></tr></table></td></tr></table>'."\n"; 3028: $studentTable.='<input type="button" '. 3029: 'onClick="javascript:checkPickOne(this.form);"value="Next->" /></form>'."\n"; 3030: 3031: $studentTable.=&show_grading_menu_form($symb,$url); 3032: $request->print($studentTable); 3033: 3034: return ''; 3035: } 3036: 3037: sub getSymbMap { 3038: my ($request) = @_; 3039: my $navmap = Apache::lonnavmaps::navmap->new(); 3040: 3041: my %symbx = (); 3042: my @titles = (); 3043: my $minder = 0; 3044: 3045: # Gather every sequence that has problems. 3046: my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); }, 1); 3047: for my $sequence ($navmap->getById('0.0'), @sequences) { 3048: if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 0) ) { 3049: my $title = $minder.'.'.$sequence->compTitle(); 3050: push @titles, $title; # minder in case two titles are identical 3051: $symbx{$title} = $sequence->symb(); 3052: $minder++; 3053: } 3054: } 3055: return \@titles,\%symbx; 3056: } 3057: 3058: # 3059: #--- Displays a page/sequence w/wo problems, w/wo submissions 3060: sub displayPage { 3061: my ($request) = shift; 3062: 3063: my ($symb,$url) = &get_symb_and_url($request); 3064: my $cdom = $ENV{"course.$ENV{'request.course.id'}.domain"}; 3065: my $cnum = $ENV{"course.$ENV{'request.course.id'}.num"}; 3066: my $getsec = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'}; 3067: my $pageTitle = $ENV{'form.page'}; 3068: my ($classlist,undef,$fullname) = &getclasslist($getsec,'1'); 3069: my ($uname,$udom) = split(/:/,$ENV{'form.student'}); 3070: my $usec=$classlist->{$ENV{'form.student'}}[5]; 3071: 3072: #need to make sure we have the correct data for later EXT calls, 3073: #thus invalidate the cache 3074: &Apache::lonnet::devalidatecourseresdata( 3075: $ENV{'course.'.$ENV{'request.course.id'}.'.num'}, 3076: $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}); 3077: &Apache::lonnet::clear_EXT_cache_status(); 3078: 3079: if (!&canview($usec)) { 3080: $request->print('<font color="red">Unable to view requested student.('.$ENV{'form.student'}.')</font>'); 3081: $request->print(&show_grading_menu_form($symb,$url)); 3082: return; 3083: } 3084: my $result='<h3><font color="#339933"> '.$ENV{'form.title'}.'</font></h3>'; 3085: $result.='<h3> Student: '.&nameUserString(undef,$$fullname{$ENV{'form.student'}},$uname,$udom). 3086: '</h3>'."\n"; 3087: &sub_page_js($request); 3088: $request->print($result); 3089: 3090: my $navmap = Apache::lonnavmaps::navmap->new(); 3091: my ($mapUrl, $id, $resUrl)=&Apache::lonnet::decode_symb($ENV{'form.page'}); 3092: my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps 3093: 3094: my $iterator = $navmap->getIterator($map->map_start(), 3095: $map->map_finish()); 3096: 3097: my $studentTable='<form action="/adm/grades" method="post" name="gradePage">'."\n". 3098: '<input type="hidden" name="command" value="gradeByPage" />'."\n". 3099: '<input type="hidden" name="fullname" value="'.$$fullname{$ENV{'form.student'}}.'" />'."\n". 3100: '<input type="hidden" name="student" value="'.$ENV{'form.student'}.'" />'."\n". 3101: '<input type="hidden" name="page" value="'.$pageTitle.'" />'."\n". 3102: '<input type="hidden" name="title" value="'.$ENV{'form.title'}.'" />'."\n". 3103: '<input type="hidden" name="url" value="'.$url.'" />'."\n". 3104: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n". 3105: '<input type="hidden" name="overRideScore" value="no" />'."\n". 3106: '<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."\n"; 3107: 3108: my $checkIcon = '<img src="'.$request->dir_config('lonIconsURL'). 3109: '/check.gif" height="16" border="0" />'; 3110: 3111: $studentTable.=' <b>Note:</b> Problems graded correct by the computer are marked with a '.$checkIcon. 3112: ' symbol.'."\n". 3113: '<table border="0"><tr><td bgcolor="#777777">'. 3114: '<table border="0"><tr bgcolor="#e6ffff">'. 3115: '<td align="center"><b> Prob. </b></td>'. 3116: '<td><b> '.($ENV{'form.vProb'} eq 'no' ? 'Title' : 'Problem Text').'/Grade</b></td></tr>'; 3117: 3118: my ($depth,$question,$prob) = (1,1,1); 3119: $iterator->next(); # skip the first BEGIN_MAP 3120: my $curRes = $iterator->next(); # for "current resource" 3121: while ($depth > 0) { 3122: if($curRes == $iterator->BEGIN_MAP) { $depth++; } 3123: if($curRes == $iterator->END_MAP) { $depth--; } 3124: 3125: if (ref($curRes) && $curRes->is_problem()) { 3126: my $parts = $curRes->parts(); 3127: my $title = $curRes->compTitle(); 3128: my $symbx = $curRes->symb(); 3129: $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$prob. 3130: (scalar(@{$parts}) == 1 ? '' : '<br>('.scalar(@{$parts}).' parts)').'</td>'; 3131: $studentTable.='<td valign="top">'; 3132: if ($ENV{'form.vProb'} eq 'yes' ) { 3133: $studentTable.=&show_problem($request,$symbx,$uname,$udom,1, 3134: undef,'both'); 3135: } else { 3136: my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$ENV{'request.course.id'}); 3137: $companswer =~ s|<form(.*?)>||g; 3138: $companswer =~ s|</form>||g; 3139: # while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a> 3140: # $companswer =~ s/$1/ /ms; 3141: # $request->print('match='.$1."<br>\n"); 3142: # } 3143: # $companswer =~ s|<table border=\"1\">|<table border=\"0\">|g; 3144: $studentTable.=' <b>'.$title.'</b> <br> <b>Correct answer:</b><br>'.$companswer; 3145: } 3146: 3147: my %record = &Apache::lonnet::restore($symbx,$ENV{'request.course.id'},$udom,$uname); 3148: 3149: if ($ENV{'form.lastSub'} eq 'datesub') { 3150: if ($record{'version'} eq '') { 3151: $studentTable.='<br /> <font color="red">No recorded submission for this problem</font><br />'; 3152: } else { 3153: my %responseType = (); 3154: foreach my $partid (@{$parts}) { 3155: my @responseIds =$curRes->responseIds($partid); 3156: my @responseType =$curRes->responseType($partid); 3157: my %responseIds; 3158: for (my $i=0;$i<=$#responseIds;$i++) { 3159: $responseIds{$responseIds[$i]}=$responseType[$i]; 3160: } 3161: $responseType{$partid} = \%responseIds; 3162: } 3163: $studentTable.= &displaySubByDates($symbx,\%record,$parts,\%responseType,$checkIcon,$uname,$udom); 3164: 3165: } 3166: } elsif ($ENV{'form.lastSub'} eq 'all') { 3167: my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : ''); 3168: $studentTable.=&Apache::loncommon::get_previous_attempt($symbx,$uname,$udom, 3169: $ENV{'request.course.id'}, 3170: '','.submission'); 3171: 3172: } 3173: if (&canmodify($usec)) { 3174: foreach my $partid (@{$parts}) { 3175: $studentTable.=&gradeBox($request,$symbx,$uname,$udom,$question,$partid,\%record); 3176: $studentTable.='<input type="hidden" name="q_'.$question.'" value="'.$partid.'" />'."\n"; 3177: $question++; 3178: } 3179: $prob++; 3180: } 3181: $studentTable.='</td></tr>'; 3182: 3183: } 3184: $curRes = $iterator->next(); 3185: } 3186: 3187: $studentTable.='</td></tr></table></td></tr></table>'."\n". 3188: '<input type="button" value="Save" '. 3189: 'onClick="javascript:checkSubmitPage(this.form,'.$question.');" TARGET=_self />'. 3190: '</form>'."\n"; 3191: $studentTable.=&show_grading_menu_form($symb,$url); 3192: $request->print($studentTable); 3193: 3194: return ''; 3195: } 3196: 3197: sub displaySubByDates { 3198: my ($symb,$record,$parts,$responseType,$checkIcon,$uname,$udom) = @_; 3199: my $isCODE=0; 3200: if (exists($record->{'resource.CODE'})) { $isCODE=1; } 3201: my $studentTable='<table border="0" width="100%"><tr><td bgcolor="#777777">'. 3202: '<table border="0" width="100%"><tr bgcolor="#e6ffff">'. 3203: '<td><b>Date/Time</b></td>'. 3204: ($isCODE?'<td><b>CODE</b></td>':''). 3205: '<td><b>Submission</b></td>'. 3206: '<td><b>Status </b></td></tr>'; 3207: my ($version); 3208: my %mark; 3209: my %orders; 3210: $mark{'correct_by_student'} = $checkIcon; 3211: if (!exists($$record{'1:timestamp'})) { 3212: return '<br /> <font color="red">Nothing submitted - no attempts</font><br />'; 3213: } 3214: for ($version=1;$version<=$$record{'version'};$version++) { 3215: my $timestamp = scalar(localtime($$record{$version.':timestamp'})); 3216: $studentTable.='<tr bgcolor="#ffffff" valign="top"><td>'.$timestamp.'</td>'; 3217: if ($isCODE) { 3218: $studentTable.='<td>'.$record->{$version.':resource.CODE'}.'</td>'; 3219: } 3220: my @versionKeys = split(/\:/,$$record{$version.':keys'}); 3221: my @displaySub = (); 3222: foreach my $partid (@{$parts}) { 3223: my @matchKey = sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys); 3224: # next if ($$record{"$version:resource.$partid.solved"} eq ''); 3225: my $display_part=&get_display_part($partid,undef,$symb); 3226: foreach my $matchKey (@matchKey) { 3227: if (exists($$record{$version.':'.$matchKey}) && 3228: $$record{$version.':'.$matchKey} ne '') { 3229: my ($responseId)=($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/); 3230: $displaySub[0].='<b>Part:</b> '.$display_part.' '; 3231: $displaySub[0].='<font color="#999999">(ID '. 3232: $responseId.')</font> <b>'; 3233: if ($$record{"$version:resource.$partid.tries"} eq '') { 3234: $displaySub[0].='Trial not counted'; 3235: } else { 3236: $displaySub[0].='Trial '. 3237: $$record{"$version:resource.$partid.tries"}; 3238: } 3239: my $responseType=$responseType->{$partid}->{$responseId}; 3240: if (!exists($orders{$partid})) { $orders{$partid}={}; } 3241: if (!exists($orders{$partid}->{$responseId})) { 3242: $orders{$partid}->{$responseId}= 3243: &get_order($partid,$responseId,$symb,$uname,$udom); 3244: } 3245: $displaySub[0].='</b> '. 3246: &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:").'<br />'; 3247: } 3248: } 3249: if (exists $$record{"$version:resource.$partid.award"}) { 3250: $displaySub[1].='<b>Part:</b> '.$display_part.' '. 3251: lc($$record{"$version:resource.$partid.award"}).' '. 3252: $mark{$$record{"$version:resource.$partid.solved"}}. 3253: '<br />'; 3254: } 3255: if (exists $$record{"$version:resource.$partid.regrader"}) { 3256: $displaySub[2].=$$record{"$version:resource.$partid.regrader"}. 3257: ' (<b>'.&mt('Part').':</b> '.$display_part.')'; 3258: } 3259: } 3260: # needed because old essay regrader has not parts info 3261: if (exists $$record{"$version:resource.regrader"}) { 3262: $displaySub[2].=$$record{"$version:resource.regrader"}; 3263: } 3264: $studentTable.='<td>'.$displaySub[0].' </td><td>'.$displaySub[1]; 3265: if ($displaySub[2]) { 3266: $studentTable.='Manually graded by '.$displaySub[2]; 3267: } 3268: $studentTable.=' </td></tr>'; 3269: 3270: } 3271: $studentTable.='</table></td></tr></table>'; 3272: return $studentTable; 3273: } 3274: 3275: sub updateGradeByPage { 3276: my ($request) = shift; 3277: 3278: my $cdom = $ENV{"course.$ENV{'request.course.id'}.domain"}; 3279: my $cnum = $ENV{"course.$ENV{'request.course.id'}.num"}; 3280: my $getsec = $ENV{'form.section'} eq '' ? 'all' : $ENV{'form.section'}; 3281: my $pageTitle = $ENV{'form.page'}; 3282: my ($classlist,undef,$fullname) = &getclasslist($getsec,'1'); 3283: my ($uname,$udom) = split(/:/,$ENV{'form.student'}); 3284: my $usec=$classlist->{$ENV{'form.student'}}[5]; 3285: if (!&canmodify($usec)) { 3286: $request->print('<font color="red">Unable to modify requested student.('.$ENV{'form.student'}.'</font>'); 3287: $request->print(&show_grading_menu_form($ENV{'form.symb'},$ENV{'form.url'})); 3288: return; 3289: } 3290: my $result='<h3><font color="#339933"> '.$ENV{'form.title'}.'</font></h3>'; 3291: $result.='<h3> Student: '.&nameUserString(undef,$ENV{'form.fullname'},$uname,$udom). 3292: '</h3>'."\n"; 3293: 3294: $request->print($result); 3295: 3296: my $navmap = Apache::lonnavmaps::navmap->new(); 3297: my ($mapUrl, $id, $resUrl) = &Apache::lonnet::decode_symb( $ENV{'form.page'}); 3298: my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps 3299: 3300: my $iterator = $navmap->getIterator($map->map_start(), 3301: $map->map_finish()); 3302: 3303: my $studentTable='<table border="0"><tr><td bgcolor="#777777">'. 3304: '<table border="0"><tr bgcolor="#e6ffff">'. 3305: '<td align="center"><b> Prob. </b></td>'. 3306: '<td><b> Title </b></td>'. 3307: '<td><b> Previous Score </b></td>'. 3308: '<td><b> New Score </b></td></tr>'; 3309: 3310: $iterator->next(); # skip the first BEGIN_MAP 3311: my $curRes = $iterator->next(); # for "current resource" 3312: my ($depth,$question,$prob,$changeflag)= (1,1,1,0); 3313: while ($depth > 0) { 3314: if($curRes == $iterator->BEGIN_MAP) { $depth++; } 3315: if($curRes == $iterator->END_MAP) { $depth--; } 3316: 3317: if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout) { 3318: my $parts = $curRes->parts(); 3319: my $title = $curRes->compTitle(); 3320: my $symbx = $curRes->symb(); 3321: $studentTable.='<tr bgcolor="#ffffe6"><td align="center" valign="top" >'.$prob. 3322: (scalar(@{$parts}) == 1 ? '' : '<br>('.scalar(@{$parts}).' parts)').'</td>'; 3323: $studentTable.='<td valign="top"> <b>'.$title.'</b> </td>'; 3324: 3325: my %newrecord=(); 3326: my @displayPts=(); 3327: foreach my $partid (@{$parts}) { 3328: my $newpts = $ENV{'form.GD_BOX'.$question.'_'.$partid}; 3329: my $oldpts = $ENV{'form.oldpts'.$question.'_'.$partid}; 3330: 3331: my $wgt = $ENV{'form.WGT'.$question.'_'.$partid} != 0 ? 3332: $ENV{'form.WGT'.$question.'_'.$partid} : 1; 3333: my $partial = $newpts/$wgt; 3334: my $score; 3335: if ($partial > 0) { 3336: $score = 'correct_by_override'; 3337: } elsif ($newpts ne '') { #empty is taken as 0 3338: $score = 'incorrect_by_override'; 3339: } 3340: my $dropMenu = $ENV{'form.GD_SEL'.$question.'_'.$partid}; 3341: if ($dropMenu eq 'excused') { 3342: $partial = ''; 3343: $score = 'excused'; 3344: } elsif ($dropMenu eq 'reset status' 3345: && $ENV{'form.solved'.$question.'_'.$partid} ne '') { #update only if previous record exists 3346: $newrecord{'resource.'.$partid.'.tries'} = 0; 3347: $newrecord{'resource.'.$partid.'.solved'} = ''; 3348: $newrecord{'resource.'.$partid.'.award'} = ''; 3349: $newrecord{'resource.'.$partid.'.awarded'} = 0; 3350: $newrecord{'resource.'.$partid.'.regrader'} = "$ENV{'user.name'}:$ENV{'user.domain'}"; 3351: $changeflag++; 3352: $newpts = ''; 3353: } 3354: my $display_part=&get_display_part($partid,undef, 3355: $curRes->symb()); 3356: my $oldstatus = $ENV{'form.solved'.$question.'_'.$partid}; 3357: $displayPts[0].=' <b>Part:</b> '.$display_part.' = '. 3358: (($oldstatus eq 'excused') ? 'excused' : $oldpts). 3359: ' <br>'; 3360: $displayPts[1].=' <b>Part:</b> '.$display_part.' = '. 3361: (($score eq 'excused') ? 'excused' : $newpts). 3362: ' <br>'; 3363: 3364: $question++; 3365: next if ($dropMenu eq 'reset status' || ($newpts == $oldpts && $score ne 'excused')); 3366: 3367: $newrecord{'resource.'.$partid.'.awarded'} = $partial if $partial ne ''; 3368: $newrecord{'resource.'.$partid.'.solved'} = $score if $score ne ''; 3369: $newrecord{'resource.'.$partid.'.regrader'} = "$ENV{'user.name'}:$ENV{'user.domain'}" 3370: if (scalar(keys(%newrecord)) > 0); 3371: 3372: $changeflag++; 3373: } 3374: if (scalar(keys(%newrecord)) > 0) { 3375: &Apache::lonnet::cstore(\%newrecord,$symbx,$ENV{'request.course.id'}, 3376: $udom,$uname); 3377: } 3378: 3379: $studentTable.='<td valign="top">'.$displayPts[0].'</td>'. 3380: '<td valign="top">'.$displayPts[1].'</td>'. 3381: '</tr>'; 3382: 3383: $prob++; 3384: } 3385: $curRes = $iterator->next(); 3386: } 3387: 3388: $studentTable.='</td></tr></table></td></tr></table>'; 3389: $studentTable.=&show_grading_menu_form($ENV{'form.symb'},$ENV{'form.url'}); 3390: my $grademsg=($changeflag == 0 ? 'No score was changed or updated.' : 3391: 'The scores were changed for '. 3392: $changeflag.' problem'.($changeflag == 1 ? '.' : 's.')); 3393: $request->print($grademsg.$studentTable); 3394: 3395: return ''; 3396: } 3397: 3398: #-------- end of section for handling grading by page/sequence --------- 3399: # 3400: #------------------------------------------------------------------- 3401: 3402: #--------------------Scantron Grading----------------------------------- 3403: # 3404: #------ start of section for handling grading by page/sequence --------- 3405: 3406: sub defaultFormData { 3407: my ($symb,$url)=@_; 3408: return ' 3409: <input type="hidden" name="symb" value="'.$symb.'" />'."\n". 3410: '<input type="hidden" name="url" value="'.$url.'" />'."\n". 3411: '<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."\n". 3412: '<input type="hidden" name="probTitle" value="'.$ENV{'form.probTitle'}.'" />'."\n"; 3413: } 3414: 3415: sub getSequenceDropDown { 3416: my ($request,$symb)=@_; 3417: my $result='<select name="selectpage">'."\n"; 3418: my ($titles,$symbx) = &getSymbMap($request); 3419: my ($curpage)=&Apache::lonnet::decode_symb($symb); 3420: my $ctr=0; 3421: foreach (@$titles) { 3422: my ($minder,$showtitle) = ($_ =~ /(\d+)\.(.*)/); 3423: $result.='<option value="'.$$symbx{$_}.'" '. 3424: ($$symbx{$_} =~ /$curpage$/ ? 'selected="on"' : ''). 3425: '>'.$showtitle.'</option>'."\n"; 3426: $ctr++; 3427: } 3428: $result.= '</select>'; 3429: return $result; 3430: } 3431: 3432: sub scantron_filenames { 3433: my $cdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}; 3434: my $cname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'}; 3435: my @files=&Apache::lonnet::dirlist('userfiles',$cdom,$cname, 3436: &Apache::loncommon::propath($cdom,$cname)); 3437: my @possiblenames; 3438: foreach my $filename (sort(@files)) { 3439: ($filename)=split(/&/,$filename); 3440: if ($filename!~/^scantron_orig_/) { next ; } 3441: $filename=~s/^scantron_orig_//; 3442: push(@possiblenames,$filename); 3443: } 3444: return @possiblenames; 3445: } 3446: 3447: sub scantron_uploads { 3448: my ($file2grade) = @_; 3449: my $result= '<select name="scantron_selectfile">'; 3450: $result.="<option></option>"; 3451: foreach my $filename (sort(&scantron_filenames())) { 3452: $result.="<option".($filename eq $file2grade ? ' selected="on"':'').">$filename</option>\n"; 3453: } 3454: $result.="</select>"; 3455: return $result; 3456: } 3457: 3458: sub scantron_scantab { 3459: my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab'); 3460: my $result='<select name="scantron_format">'."\n"; 3461: $result.='<option></option>'."\n"; 3462: foreach my $line (<$fh>) { 3463: my ($name,$descrip)=split(/:/,$line); 3464: if ($name =~ /^\#/) { next; } 3465: $result.='<option value="'.$name.'">'.$descrip.'</option>'."\n"; 3466: } 3467: $result.='</select>'."\n"; 3468: 3469: return $result; 3470: } 3471: 3472: sub scantron_CODElist { 3473: my $cdom = $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}; 3474: my $cnum = $ENV{'course.'.$ENV{'request.course.id'}.'.num'}; 3475: my @names=&Apache::lonnet::getkeys('CODEs',$cdom,$cnum); 3476: my $namechoice='<option></option>'; 3477: foreach my $name (sort {uc($a) cmp uc($b)} @names) { 3478: if ($name =~ /^error: 2 /) { next; } 3479: $namechoice.='<option value="'.$name.'">'.$name.'</option>'; 3480: } 3481: $namechoice='<select name="scantron_CODElist">'.$namechoice.'</select>'; 3482: return $namechoice; 3483: } 3484: 3485: sub scantron_CODEunique { 3486: my $result='<nobr> 3487: <input type="radio" name="scantron_CODEunique" 3488: value="Yes" checked="on" /> Yes 3489: </nobr> 3490: <nobr> 3491: <input type="radio" name="scantron_CODEunique" 3492: value="No" /> No 3493: </nobr>'; 3494: return $result; 3495: } 3496: 3497: sub scantron_selectphase { 3498: my ($r,$file2grade) = @_; 3499: my ($symb,$url)=&get_symb_and_url($r); 3500: if (!$symb) {return '';} 3501: my $sequence_selector=&getSequenceDropDown($r,$symb); 3502: my $default_form_data=&defaultFormData($symb,$url); 3503: my $grading_menu_button=&show_grading_menu_form($symb,$url); 3504: my $file_selector=&scantron_uploads($file2grade); 3505: my $format_selector=&scantron_scantab(); 3506: my $CODE_selector=&scantron_CODElist(); 3507: my $CODE_unique=&scantron_CODEunique(); 3508: my $result; 3509: #FIXME allow instructor to be able to download the scantron file 3510: # and to upload it, 3511: $result.= <<SCANTRONFORM; 3512: <table width="100%" border="0"> 3513: <tr> 3514: <td bgcolor="#777777"> 3515: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantron_process"> 3516: <input type="hidden" name="command" value="scantron_warning" /> 3517: $default_form_data 3518: <table width="100%" border="0"> 3519: <tr bgcolor="#e6ffff"> 3520: <td colspan="2"> 3521: <b>Specify file and which Folder/Sequence to grade</b> 3522: </td> 3523: </tr> 3524: <tr bgcolor="#ffffe6"> 3525: <td> Sequence to grade: </td><td> $sequence_selector </td> 3526: </tr> 3527: <tr bgcolor="#ffffe6"> 3528: <td> Filename of scoring office file: </td><td> $file_selector </td> 3529: </tr> 3530: <tr bgcolor="#ffffe6"> 3531: <td> Format of data file: </td><td> $format_selector </td> 3532: </tr> 3533: <tr bgcolor="#ffffe6"> 3534: <td> Saved CODEs to validate against: </td><td> $CODE_selector</td> 3535: </tr> 3536: <tr bgcolor="#ffffe6"> 3537: <td> Each CODE is only to be used once:</td><td> $CODE_unique </td> 3538: </tr> 3539: <tr bgcolor="#ffffe6"> 3540: <td> Options: </td> 3541: <td> 3542: <input type="checkbox" name="scantron_options_redo" value="redo_skipped"/> Do only previously skipped records <br /> 3543: <input type="checkbox" name="scantron_options_ignore" value="ignore_corrections"/> Remove all exisiting corrections 3544: </td> 3545: </tr> 3546: <tr bgcolor="#ffffe6"> 3547: <td colspan="2"> 3548: <input type="submit" value="Validate Scantron Records" /> 3549: </td> 3550: </tr> 3551: </table> 3552: </form> 3553: </td> 3554: </tr> 3555: SCANTRONFORM 3556: 3557: $r->print($result); 3558: 3559: if (&Apache::lonnet::allowed('usc',$ENV{'request.role.domain'}) || 3560: &Apache::lonnet::allowed('usc',$ENV{'request.course.id'})) { 3561: 3562: $r->print(<<SCANTRONFORM); 3563: <tr> 3564: <td bgcolor="#777777"> 3565: <table width="100%" border="0"> 3566: <tr bgcolor="#e6ffff"> 3567: <td> 3568: <b>Specify a Scantron data file to upload.</b> 3569: </td> 3570: </tr> 3571: <tr bgcolor="#ffffe6"> 3572: <td> 3573: SCANTRONFORM 3574: my $default_form_data=&defaultFormData(&get_symb_and_url($r,1)); 3575: my $cdom= $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}; 3576: my $cnum= $ENV{'course.'.$ENV{'request.course.id'}.'.num'}; 3577: $r->print(<<UPLOAD); 3578: <script type="text/javascript" language="javascript"> 3579: function checkUpload(formname) { 3580: if (formname.upfile.value == "") { 3581: alert("Please use the browse button to select a file from your local directory."); 3582: return false; 3583: } 3584: formname.submit(); 3585: } 3586: </script> 3587: 3588: <form enctype='multipart/form-data' action='/adm/grades' name='rules' method='post'> 3589: $default_form_data 3590: <input name='courseid' type='hidden' value='$cnum' /> 3591: <input name='domainid' type='hidden' value='$cdom' /> 3592: <input name='command' value='scantronupload_save' type='hidden' /> 3593: File to upload:<input type="file" name="upfile" size="50" /> 3594: <br /> 3595: <input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scantron Data" /> 3596: </form> 3597: UPLOAD 3598: 3599: $r->print(<<SCANTRONFORM); 3600: </td> 3601: </tr> 3602: </table> 3603: </td> 3604: </tr> 3605: SCANTRONFORM 3606: } 3607: $r->print(<<SCANTRONFORM); 3608: <tr> 3609: <td bgcolor="#777777"> 3610: <form action='/adm/grades' name='scantron_download'> 3611: <input type="hidden" name="command" value="scantron_download" /> 3612: <table width="100%" border="0"> 3613: <tr bgcolor="#e6ffff"> 3614: <td colspan="2"> 3615: <b>Download a scoring office file</b> 3616: </td> 3617: </tr> 3618: <tr bgcolor="#ffffe6"> 3619: <td> Filename of scoring office file: </td><td> $file_selector </td> 3620: </tr> 3621: <tr bgcolor="#ffffe6"> 3622: <td colspan="2"> 3623: <input type="submit" value="Show List of Files" /> 3624: </td> 3625: </tr> 3626: </table> 3627: </form> 3628: </td> 3629: </tr> 3630: SCANTRONFORM 3631: 3632: $r->print(<<SCANTRONFORM); 3633: </table> 3634: </form> 3635: $grading_menu_button 3636: SCANTRONFORM 3637: 3638: return 3639: } 3640: 3641: sub get_scantron_config { 3642: my ($which) = @_; 3643: my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab'); 3644: my %config; 3645: #FIXME probably should move to XML it has already gotten a bit much now 3646: foreach my $line (<$fh>) { 3647: my ($name,$descrip)=split(/:/,$line); 3648: if ($name ne $which ) { next; } 3649: chomp($line); 3650: my @config=split(/:/,$line); 3651: $config{'name'}=$config[0]; 3652: $config{'description'}=$config[1]; 3653: $config{'CODElocation'}=$config[2]; 3654: $config{'CODEstart'}=$config[3]; 3655: $config{'CODElength'}=$config[4]; 3656: $config{'IDstart'}=$config[5]; 3657: $config{'IDlength'}=$config[6]; 3658: $config{'Qstart'}=$config[7]; 3659: $config{'Qlength'}=$config[8]; 3660: $config{'Qoff'}=$config[9]; 3661: $config{'Qon'}=$config[10]; 3662: $config{'PaperID'}=$config[11]; 3663: $config{'PaperIDlength'}=$config[12]; 3664: $config{'FirstName'}=$config[13]; 3665: $config{'FirstNamelength'}=$config[14]; 3666: $config{'LastName'}=$config[15]; 3667: $config{'LastNamelength'}=$config[16]; 3668: last; 3669: } 3670: return %config; 3671: } 3672: 3673: sub username_to_idmap { 3674: my ($classlist)= @_; 3675: my %idmap; 3676: foreach my $student (keys(%$classlist)) { 3677: $idmap{$classlist->{$student}->[&Apache::loncoursedata::CL_ID]}= 3678: $student; 3679: } 3680: return %idmap; 3681: } 3682: 3683: sub scantron_fixup_scanline { 3684: my ($scantron_config,$scan_data,$line,$whichline,$field,$args)=@_; 3685: if ($field eq 'ID') { 3686: if (length($args->{'newid'}) > $$scantron_config{'IDlength'}) { 3687: return ($line,1,'New value too large'); 3688: } 3689: if (length($args->{'newid'}) < $$scantron_config{'IDlength'}) { 3690: $args->{'newid'}=sprintf('%-'.$$scantron_config{'IDlength'}.'s', 3691: $args->{'newid'}); 3692: } 3693: substr($line,$$scantron_config{'IDstart'}-1, 3694: $$scantron_config{'IDlength'})=$args->{'newid'}; 3695: if ($args->{'newid'}=~/^\s*$/) { 3696: &scan_data($scan_data,"$whichline.user", 3697: $args->{'username'}.':'.$args->{'domain'}); 3698: } 3699: } elsif ($field eq 'CODE') { 3700: if ($args->{'CODE_ignore_dup'}) { 3701: &scan_data($scan_data,"$whichline.CODE_ignore_dup",'1'); 3702: } 3703: &scan_data($scan_data,"$whichline.useCODE",'1'); 3704: if ($args->{'CODE'} ne 'use_unfound') { 3705: if (length($args->{'CODE'}) > $$scantron_config{'CODElength'}) { 3706: return ($line,1,'New CODE value too large'); 3707: } 3708: if (length($args->{'CODE'}) < $$scantron_config{'CODElength'}) { 3709: $args->{'CODE'}=sprintf('%-'.$$scantron_config{'CODElength'}.'s',$args->{'CODE'}); 3710: } 3711: substr($line,$$scantron_config{'CODEstart'}-1, 3712: $$scantron_config{'CODElength'})=$args->{'CODE'}; 3713: } 3714: } elsif ($field eq 'answer') { 3715: my $length=$scantron_config->{'Qlength'}; 3716: my $off=$scantron_config->{'Qoff'}; 3717: my $on=$scantron_config->{'Qon'}; 3718: my $answer=${off}x$length; 3719: if ($args->{'response'} eq 'none') { 3720: &scan_data($scan_data, 3721: "$whichline.no_bubble.".$args->{'question'},'1'); 3722: } else { 3723: substr($answer,$args->{'response'},1)=$on; 3724: &scan_data($scan_data, 3725: "$whichline.no_bubble.".$args->{'question'},undef,'1'); 3726: } 3727: my $where=$length*($args->{'question'}-1)+$scantron_config->{'Qstart'}; 3728: substr($line,$where-1,$length)=$answer; 3729: } 3730: return $line; 3731: } 3732: 3733: sub scan_data { 3734: my ($scan_data,$key,$value,$delete)=@_; 3735: my $filename=$ENV{'form.scantron_selectfile'}; 3736: if (defined($value)) { 3737: $scan_data->{$filename.'_'.$key} = $value; 3738: } 3739: if ($delete) { delete($scan_data->{$filename.'_'.$key}); } 3740: return $scan_data->{$filename.'_'.$key}; 3741: } 3742: 3743: sub scantron_parse_scanline { 3744: my ($line,$whichline,$scantron_config,$scan_data,$justHeader)=@_; 3745: my %record; 3746: my $questions=substr($line,$$scantron_config{'Qstart'}-1); 3747: my $data=substr($line,0,$$scantron_config{'Qstart'}-1); 3748: if ($$scantron_config{'CODElocation'} ne 0) { 3749: if ($$scantron_config{'CODElocation'} < 0) { 3750: $record{'scantron.CODE'}=substr($data, 3751: $$scantron_config{'CODEstart'}-1, 3752: $$scantron_config{'CODElength'}); 3753: if (&scan_data($scan_data,"$whichline.useCODE")) { 3754: $record{'scantron.useCODE'}=1; 3755: } 3756: if (&scan_data($scan_data,"$whichline.CODE_ignore_dup")) { 3757: $record{'scantron.CODE_ignore_dup'}=1; 3758: } 3759: } else { 3760: #FIXME interpret first N questions 3761: } 3762: } 3763: $record{'scantron.ID'}=substr($data,$$scantron_config{'IDstart'}-1, 3764: $$scantron_config{'IDlength'}); 3765: $record{'scantron.PaperID'}= 3766: substr($data,$$scantron_config{'PaperID'}-1, 3767: $$scantron_config{'PaperIDlength'}); 3768: $record{'scantron.FirstName'}= 3769: substr($data,$$scantron_config{'FirstName'}-1, 3770: $$scantron_config{'FirstNamelength'}); 3771: $record{'scantron.LastName'}= 3772: substr($data,$$scantron_config{'LastName'}-1, 3773: $$scantron_config{'LastNamelength'}); 3774: if ($justHeader) { return \%record; } 3775: 3776: my @alphabet=('A'..'Z'); 3777: my $questnum=0; 3778: while ($questions) { 3779: $questnum++; 3780: my $currentquest=substr($questions,0,$$scantron_config{'Qlength'}); 3781: substr($questions,0,$$scantron_config{'Qlength'})=''; 3782: if (length($currentquest) < $$scantron_config{'Qlength'}) { next; } 3783: my @array=split($$scantron_config{'Qon'},$currentquest,-1); 3784: if (length($array[0]) eq $$scantron_config{'Qlength'}) { 3785: $record{"scantron.$questnum.answer"}=''; 3786: if (!&scan_data($scan_data,"$whichline.no_bubble.$questnum")) { 3787: push(@{$record{"scantron.missingerror"}},$questnum); 3788: } 3789: } else { 3790: $record{"scantron.$questnum.answer"}=$alphabet[length($array[0])]; 3791: } 3792: if (scalar(@array) gt 2) { 3793: push(@{$record{'scantron.doubleerror'}},$questnum); 3794: my @ans=@array; 3795: my $i=length($ans[0]);shift(@ans); 3796: while ($#ans) { 3797: $i+=length($ans[0])+1; 3798: $record{"scantron.$questnum.answer"}.=$alphabet[$i]; 3799: shift(@ans); 3800: } 3801: } 3802: } 3803: $record{'scantron.maxquest'}=$questnum; 3804: return \%record; 3805: } 3806: 3807: sub scantron_add_delay { 3808: my ($delayqueue,$scanline,$errormessage,$errorcode)=@_; 3809: push(@$delayqueue, 3810: {'line' => $scanline, 'emsg' => $errormessage, 3811: 'ecode' => $errorcode } 3812: ); 3813: } 3814: 3815: sub scantron_find_student { 3816: my ($scantron_record,$scan_data,$idmap,$line)=@_; 3817: my $scanID=$$scantron_record{'scantron.ID'}; 3818: if ($scanID =~ /^\s*$/) { 3819: return &scan_data($scan_data,"$line.user"); 3820: } 3821: foreach my $id (keys(%$idmap)) { 3822: if (lc($id) eq lc($scanID)) { 3823: return $$idmap{$id}; 3824: } 3825: } 3826: return undef; 3827: } 3828: 3829: sub scantron_filter { 3830: my ($curres)=@_; 3831: # randomout is dysfunctional at best for this purpose 3832: if (ref($curres) && $curres->is_problem()) { #&& !$curres->randomout) { 3833: return 1; 3834: } 3835: return 0; 3836: } 3837: 3838: sub scantron_process_corrections { 3839: my ($r) = @_; 3840: my %scantron_config=&get_scantron_config($ENV{'form.scantron_format'}); 3841: my ($scanlines,$scan_data)=&scantron_getfile(); 3842: my $classlist=&Apache::loncoursedata::get_classlist(); 3843: my $which=$ENV{'form.scantron_line'}; 3844: my $line=&scantron_get_line($scanlines,$scan_data,$which); 3845: my ($skip,$err,$errmsg); 3846: if ($ENV{'form.scantron_skip_record'}) { 3847: $skip=1; 3848: } elsif ($ENV{'form.scantron_corrections'} =~ /^(duplicate|incorrect)ID$/) { 3849: my $newstudent=$ENV{'form.scantron_username'}.':'. 3850: $ENV{'form.scantron_domain'}; 3851: my $newid=$classlist->{$newstudent}->[&Apache::loncoursedata::CL_ID]; 3852: ($line,$err,$errmsg)= 3853: &scantron_fixup_scanline(\%scantron_config,$scan_data,$line,$which, 3854: 'ID',{'newid'=>$newid, 3855: 'username'=>$ENV{'form.scantron_username'}, 3856: 'domain'=>$ENV{'form.scantron_domain'}}); 3857: } elsif ($ENV{'form.scantron_corrections'} =~ /^(duplicate|incorrect)CODE$/) { 3858: my $resolution=$ENV{'form.scantron_CODE_resolution'}; 3859: my $newCODE; 3860: my %args; 3861: if ($resolution eq 'use_unfound') { 3862: $newCODE='use_unfound'; 3863: } elsif ($resolution eq 'use_found') { 3864: $newCODE=$ENV{'form.scantron_CODE_selectedvalue'}; 3865: } elsif ($resolution eq 'use_typed') { 3866: $newCODE=$ENV{'form.scantron_CODE_newvalue'}; 3867: } elsif ($resolution =~ /^use_closest_(\d+)/) { 3868: $newCODE=$ENV{"form.scantron_CODE_closest_$1"}; 3869: } 3870: if ($ENV{'form.scantron_corrections'} eq 'duplicateCODE') { 3871: $args{'CODE_ignore_dup'}=1; 3872: } 3873: $args{'CODE'}=$newCODE; 3874: ($line,$err,$errmsg)= 3875: &scantron_fixup_scanline(\%scantron_config,$scan_data,$line,$which, 3876: 'CODE',\%args); 3877: } elsif ($ENV{'form.scantron_corrections'} =~ /^(missing|double)bubble$/) { 3878: foreach my $question (split(',',$ENV{'form.scantron_questions'})) { 3879: ($line,$err,$errmsg)= 3880: &scantron_fixup_scanline(\%scantron_config,$scan_data,$line, 3881: $which,'answer', 3882: { 'question'=>$question, 3883: 'response'=>$ENV{"form.scantron_correct_Q_$question"}}); 3884: if ($err) { last; } 3885: } 3886: } 3887: if ($err) { 3888: $r->print("Unable to accept last correction, an error occurred :$errmsg:"); 3889: } else { 3890: &scantron_put_line($scanlines,$scan_data,$which,$line,$skip); 3891: &scantron_putfile($scanlines,$scan_data); 3892: } 3893: } 3894: 3895: sub reset_skipping_status { 3896: my ($scanlines,$scan_data)=&scantron_getfile(); 3897: &scan_data($scan_data,'remember_skipping',undef,1); 3898: &scantron_putfile(undef,$scan_data); 3899: } 3900: 3901: sub allow_skipping { 3902: my ($scan_data,$i)=@_; 3903: my %remembered=split(':',&scan_data($scan_data,'remember_skipping')); 3904: delete($remembered{$i}); 3905: &scan_data($scan_data,'remember_skipping',join(':',%remembered)); 3906: } 3907: 3908: sub should_be_skipped { 3909: my ($scan_data,$i)=@_; 3910: if ($ENV{'form.scantron_options_redo'} !~ /^redo_/) { 3911: # not redoing old skips 3912: return 0; 3913: } 3914: my %remembered=split(':',&scan_data($scan_data,'remember_skipping')); 3915: if (exists($remembered{$i})) { return 0; } 3916: return 1; 3917: } 3918: 3919: sub remember_current_skipped { 3920: my ($scanlines,$scan_data)=&scantron_getfile(); 3921: my %to_remember; 3922: for (my $i=0;$i<=$scanlines->{'count'};$i++) { 3923: if ($scanlines->{'skipped'}[$i]) { 3924: $to_remember{$i}=1; 3925: } 3926: } 3927: &Apache::lonnet::logthis('remembering '.join(':',%to_remember)); 3928: &scan_data($scan_data,'remember_skipping',join(':',%to_remember)); 3929: &scantron_putfile(undef,$scan_data); 3930: } 3931: 3932: sub check_for_error { 3933: my ($r,$result)=@_; 3934: if ($result ne 'ok' && $result ne 'not_found' ) { 3935: $r->print("An error occured ($result) when trying to Remove the existing corrections."); 3936: } 3937: } 3938: 3939: sub scantron_warning_screen { 3940: my ($button_text)=@_; 3941: my $title=&Apache::lonnet::gettitle($ENV{'form.selectpage'}); 3942: return (<<STUFF); 3943: <p> 3944: <font color="red">Please double check the information 3945: below before clicking on '$button_text'</font> 3946: </p> 3947: <table> 3948: <tr><td><b>Sequence To be Graded:</b></td><td>$title</td></tr> 3949: <tr><td><b>Data File that will be used:</b></td><td><tt>$ENV{'form.scantron_selectfile'}</tt></td></tr> 3950: </table> 3951: </font> 3952: <br /> 3953: <p> If this information is correct, please click on '$button_text'.</p> 3954: <p> If something is incorrect, please click the 'Grading Menu' button to start over.</p> 3955: 3956: <br /> 3957: STUFF 3958: } 3959: 3960: sub scantron_do_warning { 3961: my ($r)=@_; 3962: my ($symb,$url)=&get_symb_and_url($r); 3963: if (!$symb) {return '';} 3964: my $default_form_data=&defaultFormData($symb,$url); 3965: $r->print(&scantron_form_start().$default_form_data); 3966: my $warning=&scantron_warning_screen('Validate Records'); 3967: $r->print(<<STUFF); 3968: $warning 3969: <input type="submit" name="submit" value="Validate Records" /> 3970: <input type="hidden" name="command" value="scantron_validate" /> 3971: </form> 3972: STUFF 3973: $r->print("<br />".&show_grading_menu_form($symb,$url)."</body></html>"); 3974: return ''; 3975: } 3976: 3977: sub scantron_form_start { 3978: my ($max_bubble)=@_; 3979: my $result= <<SCANTRONFORM; 3980: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload"> 3981: <input type="hidden" name="selectpage" value="$ENV{'form.selectpage'}" /> 3982: <input type="hidden" name="scantron_format" value="$ENV{'form.scantron_format'}" /> 3983: <input type="hidden" name="scantron_selectfile" value="$ENV{'form.scantron_selectfile'}" /> 3984: <input type="hidden" name="scantron_maxbubble" value="$max_bubble" /> 3985: <input type="hidden" name="scantron_CODElist" value="$ENV{'form.scantron_CODElist'}" /> 3986: <input type="hidden" name="scantron_CODEunique" value="$ENV{'form.scantron_CODEunique'}" /> 3987: <input type="hidden" name="scantron_options_redo" value="$ENV{'form.scantron_options_redo'}" /> 3988: <input type="hidden" name="scantron_options_ignore" value="$ENV{'form.scantron_options_ignore'}" /> 3989: SCANTRONFORM 3990: return $result; 3991: } 3992: 3993: sub scantron_validate_file { 3994: my ($r) = @_; 3995: my ($symb,$url)=&get_symb_and_url($r); 3996: if (!$symb) {return '';} 3997: my $default_form_data=&defaultFormData($symb,$url); 3998: 3999: # do the detection of only doing skipped records first befroe we delete 4000: # them when doing the corrections reset 4001: if ($ENV{'form.scantron_options_redo'} ne 'redo_skipped_ready') { 4002: &reset_skipping_status(); 4003: } 4004: if ($ENV{'form.scantron_options_redo'} eq 'redo_skipped') { 4005: &remember_current_skipped(); 4006: &scantron_remove_file('skipped'); 4007: $ENV{'form.scantron_options_redo'}='redo_skipped_ready'; 4008: } 4009: 4010: if ($ENV{'form.scantron_options_ignore'} eq 'ignore_corrections') { 4011: &check_for_error($r,&scantron_remove_file('corrected')); 4012: &check_for_error($r,&scantron_remove_file('skipped')); 4013: &check_for_error($r,&scantron_remove_scan_data()); 4014: $ENV{'form.scantron_options_ignore'}='done'; 4015: } 4016: 4017: if ($ENV{'form.scantron_corrections'}) { 4018: &scantron_process_corrections($r); 4019: } 4020: $r->print("<p>Gathering neccessary info.</p>");$r->rflush(); 4021: #get the student pick code ready 4022: $r->print(&Apache::loncommon::studentbrowser_javascript()); 4023: my $max_bubble=&scantron_get_maxbubble($r); 4024: my $result=&scantron_form_start($max_bubble).$default_form_data; 4025: $r->print($result); 4026: 4027: my @validate_phases=( 'ID', 4028: 'CODE', 4029: 'doublebubble', 4030: 'missingbubbles'); 4031: if (!$ENV{'form.validatepass'}) { 4032: $ENV{'form.validatepass'} = 0; 4033: } 4034: my $currentphase=$ENV{'form.validatepass'}; 4035: 4036: my $stop=0; 4037: while (!$stop && $currentphase < scalar(@validate_phases)) { 4038: $r->print("<p> Validating ".$validate_phases[$currentphase]."</p>"); 4039: $r->rflush(); 4040: my $which="scantron_validate_".$validate_phases[$currentphase]; 4041: { 4042: no strict 'refs'; 4043: ($stop,$currentphase)=&$which($r,$currentphase); 4044: } 4045: } 4046: if (!$stop) { 4047: my $warning=&scantron_warning_screen('Start Grading'); 4048: $r->print(<<STUFF); 4049: Validation process complete.<br /> 4050: $warning 4051: <input type="submit" name="submit" value="Start Grading" /> 4052: <input type="hidden" name="command" value="scantron_process" /> 4053: STUFF 4054: 4055: } else { 4056: $r->print('<input type="hidden" name="command" value="scantron_validate" />'); 4057: $r->print("<input type='hidden' name='validatepass' value='".$currentphase."' />"); 4058: } 4059: if ($stop) { 4060: $r->print('<input type="submit" name="submit" value="Continue ->" />'); 4061: $r->print(' using corrected info <br />'); 4062: $r->print("<input type='submit' value='Skip' name='scantron_skip_record' />"); 4063: $r->print(" this scanline saving it for later."); 4064: } 4065: $r->print(" </form><br />".&show_grading_menu_form($symb,$url). 4066: "</body></html>"); 4067: return ''; 4068: } 4069: 4070: sub scantron_remove_file { 4071: my ($which)=@_; 4072: my $cname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'}; 4073: my $cdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}; 4074: my $file='scantron_'; 4075: if ($which eq 'corrected' || $which eq 'skipped') { 4076: $file.=$which.'_'; 4077: } else { 4078: return 'refused'; 4079: } 4080: $file.=$ENV{'form.scantron_selectfile'}; 4081: return &Apache::lonnet::removeuserfile($cname,$cdom,$file); 4082: } 4083: 4084: sub scantron_remove_scan_data { 4085: my $cname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'}; 4086: my $cdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}; 4087: my @keys=&Apache::lonnet::getkeys('nohist_scantrondata',$cdom,$cname); 4088: my @todelete; 4089: my $filename=$ENV{'form.scantron_selectfile'}; 4090: foreach my $key (@keys) { 4091: if ($key=~/^\Q$filename\E_/) { 4092: if ($ENV{'form.scantron_options_redo'} eq 'redo_skipped_ready' && 4093: $key=~/remember_skipping/) { 4094: next; 4095: } 4096: push(@todelete,$key); 4097: } 4098: } 4099: my $result; 4100: if (@todelete) { 4101: $result=&Apache::lonnet::del('nohist_scantrondata',\@todelete,$cdom,$cname); 4102: } 4103: return $result; 4104: } 4105: 4106: sub scantron_getfile { 4107: #FIXME really would prefer a scantron directory 4108: my $cname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'}; 4109: my $cdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}; 4110: my $lines; 4111: $lines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'. 4112: 'scantron_orig_'.$ENV{'form.scantron_selectfile'}); 4113: my %scanlines; 4114: $scanlines{'orig'}=[(split("\n",$lines,-1))]; 4115: my $temp=$scanlines{'orig'}; 4116: $scanlines{'count'}=$#$temp; 4117: 4118: $lines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'. 4119: 'scantron_corrected_'.$ENV{'form.scantron_selectfile'}); 4120: if ($lines eq '-1') { 4121: $scanlines{'corrected'}=[]; 4122: } else { 4123: $scanlines{'corrected'}=[(split("\n",$lines,-1))]; 4124: } 4125: $lines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'. 4126: 'scantron_skipped_'.$ENV{'form.scantron_selectfile'}); 4127: if ($lines eq '-1') { 4128: $scanlines{'skipped'}=[]; 4129: } else { 4130: $scanlines{'skipped'}=[(split("\n",$lines,-1))]; 4131: } 4132: my @tmp=&Apache::lonnet::dump('nohist_scantrondata',$cdom,$cname); 4133: if ($tmp[0] =~ /^(error:|no_such_host)/) { @tmp=(); } 4134: my %scan_data = @tmp; 4135: return (\%scanlines,\%scan_data); 4136: } 4137: 4138: sub lonnet_putfile { 4139: my ($contents,$filename)=@_; 4140: my $docuname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'}; 4141: my $docudom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}; 4142: my $docuhome=$ENV{'course.'.$ENV{'request.course.id'}.'.home'}; 4143: $ENV{'form.sillywaytopassafilearound'}=$contents; 4144: &Apache::lonnet::finishuserfileupload($docuname,$docudom,$docuhome,'sillywaytopassafilearound',$filename); 4145: 4146: } 4147: 4148: sub scantron_putfile { 4149: my ($scanlines,$scan_data) = @_; 4150: #FIXME really would prefer a scantron directory 4151: my $cname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'}; 4152: my $cdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}; 4153: if ($scanlines) { 4154: my $prefix='scantron_'; 4155: # no need to update orig, shouldn't change 4156: # &lonnet_putfile(join("\n",@{$scanlines->{'orig'}}),$prefix.'orig_'. 4157: # $ENV{'form.scantron_selectfile'}); 4158: &lonnet_putfile(join("\n",@{$scanlines->{'corrected'}}), 4159: $prefix.'corrected_'. 4160: $ENV{'form.scantron_selectfile'}); 4161: &lonnet_putfile(join("\n",@{$scanlines->{'skipped'}}), 4162: $prefix.'skipped_'. 4163: $ENV{'form.scantron_selectfile'}); 4164: } 4165: &Apache::lonnet::put('nohist_scantrondata',$scan_data,$cdom,$cname); 4166: } 4167: 4168: sub scantron_get_line { 4169: my ($scanlines,$scan_data,$i)=@_; 4170: if (&should_be_skipped($scan_data,$i)) { return undef; } 4171: if ($scanlines->{'skipped'}[$i]) { return undef; } 4172: if ($scanlines->{'corrected'}[$i]) {return $scanlines->{'corrected'}[$i];} 4173: return $scanlines->{'orig'}[$i]; 4174: } 4175: 4176: sub get_todo_count { 4177: my ($scanlines,$scan_data)=@_; 4178: my $count=0; 4179: for (my $i=0;$i<=$scanlines->{'count'};$i++) { 4180: my $line=&scantron_get_line($scanlines,$scan_data,$i); 4181: if ($line=~/^[\s\cz]*$/) { next; } 4182: $count++; 4183: } 4184: return $count; 4185: } 4186: 4187: sub scantron_put_line { 4188: my ($scanlines,$scan_data,$i,$newline,$skip)=@_; 4189: if ($skip) { 4190: $scanlines->{'skipped'}[$i]=$newline; 4191: &allow_skipping($scan_data,$i); 4192: return; 4193: } 4194: $scanlines->{'corrected'}[$i]=$newline; 4195: } 4196: 4197: sub scantron_validate_ID { 4198: my ($r,$currentphase) = @_; 4199: 4200: #get student info 4201: my $classlist=&Apache::loncoursedata::get_classlist(); 4202: my %idmap=&username_to_idmap($classlist); 4203: 4204: #get scantron line setup 4205: my %scantron_config=&get_scantron_config($ENV{'form.scantron_format'}); 4206: my ($scanlines,$scan_data)=&scantron_getfile(); 4207: 4208: my %found=('ids'=>{},'usernames'=>{}); 4209: for (my $i=0;$i<=$scanlines->{'count'};$i++) { 4210: my $line=&scantron_get_line($scanlines,$scan_data,$i); 4211: if ($line=~/^[\s\cz]*$/) { next; } 4212: my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config, 4213: $scan_data); 4214: my $id=$$scan_record{'scantron.ID'}; 4215: my $found; 4216: foreach my $checkid (keys(%idmap)) { 4217: if (lc($checkid) eq lc($id)) { $found=$checkid;last; } 4218: } 4219: if ($found) { 4220: my $username=$idmap{$found}; 4221: if ($found{'ids'}{$found}) { 4222: &scantron_get_correction($r,$i,$scan_record,\%scantron_config, 4223: $line,'duplicateID',$found); 4224: return(1,$currentphase); 4225: } elsif ($found{'usernames'}{$username}) { 4226: &scantron_get_correction($r,$i,$scan_record,\%scantron_config, 4227: $line,'duplicateID',$username); 4228: return(1,$currentphase); 4229: } 4230: #FIXME store away line we previously saw the ID on to use above 4231: $found{'ids'}{$found}++; 4232: $found{'usernames'}{$username}++; 4233: } else { 4234: if ($id =~ /^\s*$/) { 4235: my $username=&scan_data($scan_data,"$i.user"); 4236: if (defined($username) && $found{'usernames'}{$username}) { 4237: &scantron_get_correction($r,$i,$scan_record, 4238: \%scantron_config, 4239: $line,'duplicateID',$username); 4240: return(1,$currentphase); 4241: } elsif (!defined($username)) { 4242: &scantron_get_correction($r,$i,$scan_record, 4243: \%scantron_config, 4244: $line,'incorrectID'); 4245: return(1,$currentphase); 4246: } 4247: $found{'usernames'}{$username}++; 4248: } else { 4249: &scantron_get_correction($r,$i,$scan_record,\%scantron_config, 4250: $line,'incorrectID'); 4251: return(1,$currentphase); 4252: } 4253: } 4254: } 4255: 4256: return (0,$currentphase+1); 4257: } 4258: 4259: sub scantron_get_correction { 4260: my ($r,$i,$scan_record,$scan_config,$line,$error,$arg)=@_; 4261: 4262: #FIXME in the case of a duplicated ID the previous line, probaly need 4263: #to show both the current line and the previous one and allow skipping 4264: #the previous one or the current one 4265: 4266: $r->print("<p><b>An error was detected ($error)</b>"); 4267: if ( defined($$scan_record{'scantron.PaperID'}) ) { 4268: $r->print(" for PaperID <tt>". 4269: $$scan_record{'scantron.PaperID'}."</tt> \n"); 4270: } else { 4271: $r->print(" in scanline $i <pre>". 4272: $line."</pre> \n"); 4273: } 4274: $r->print('<input type="hidden" name="scantron_corrections" value="'.$error.'" />'."\n"); 4275: $r->print('<input type="hidden" name="scantron_line" value="'.$i.'" />'."\n"); 4276: if ($error =~ /ID$/) { 4277: if ($error eq 'incorrectID') { 4278: $r->print("The encoded ID is not in the classlist</p>\n"); 4279: } elsif ($error eq 'duplicateID') { 4280: $r->print("The encoded ID has also been used by a previous paper $arg</p>\n"); 4281: } 4282: $r->print("<p>The ID on the form is <tt>". 4283: $$scan_record{'scantron.ID'}."</tt><br />\n"); 4284: $r->print("The name on the paper is ". 4285: $$scan_record{'scantron.LastName'}.",". 4286: $$scan_record{'scantron.FirstName'}."</p>"); 4287: $r->print("<p>How should I handle this? <br /> \n"); 4288: $r->print("\n<ul><li> "); 4289: #FIXME it would be nice if this sent back the user ID and 4290: #could do partial userID matches 4291: $r->print(&Apache::loncommon::selectstudent_link('scantronupload', 4292: 'scantron_username','scantron_domain')); 4293: $r->print(": <input type='text' name='scantron_username' value='' />"); 4294: $r->print("\n@". 4295: &Apache::loncommon::select_dom_form($ENV{'request.role.domain'},'scantron_domain')); 4296: 4297: $r->print('</li>'); 4298: } elsif ($error =~ /CODE$/) { 4299: if ($error eq 'incorrectCODE') { 4300: $r->print("</p><p>The encoded CODE is not in the list of possible CODEs</p>\n"); 4301: } elsif ($error eq 'duplicateCODE') { 4302: $r->print("</p><p>The encoded CODE has also been used by a previous paper ".join(', ',@{$arg}).", and CODEs are supposed to be unique</p>\n"); 4303: } 4304: $r->print("<p>The CODE on the form is <tt>'". 4305: $$scan_record{'scantron.CODE'}."'</tt><br />\n"); 4306: $r->print("<p>The ID on the form is <tt>". 4307: $$scan_record{'scantron.ID'}."</tt><br />\n"); 4308: $r->print("The name on the paper is ". 4309: $$scan_record{'scantron.LastName'}.",". 4310: $$scan_record{'scantron.FirstName'}."</p>"); 4311: $r->print("<p>How should I handle this? <br /> \n"); 4312: $r->print("\n<br /> "); 4313: my $i=0; 4314: if ($error eq 'incorrectCODE') { 4315: my ($max,$closest)=&scantron_get_closely_matching_CODEs($arg,$$scan_record{'scantron.CODE'}); 4316: foreach my $testcode (@{$closest}) { 4317: my $checked=''; 4318: if (!$i) { $checked=' checked="on" '; } 4319: $r->print("<input type='radio' name='scantron_CODE_resolution' value='use_closest_$i' $checked /> Use the similar CODE <b><tt>".$testcode."</tt></b> instead.<input type='hidden' name='scantron_CODE_closest_$i' value='$testcode' />"); 4320: $r->print("\n<br />"); 4321: $i++; 4322: } 4323: } 4324: my $checked; if (!$i) { $checked=' checked="on" '; } 4325: $r->print("<input type='radio' name='scantron_CODE_resolution' value='use_unfound' $checked /> Use the CODE <b><tt>".$$scan_record{'scantron.CODE'}."</tt></b> that is was on the paper, ignoring the error."); 4326: $r->print("\n<br />"); 4327: 4328: $r->print(<<ENDSCRIPT); 4329: <script type="text/javascript"> 4330: function change_radio(field) { 4331: var slct=document.scantronupload.scantron_CODE_resolution; 4332: var i; 4333: for (i=0;i<slct.length;i++) { 4334: if (slct[i].value==field) { slct[i].checked=true; } 4335: } 4336: } 4337: </script> 4338: ENDSCRIPT 4339: my $href="/adm/pickcode?". 4340: "form=".&Apache::lonnet::escape("scantronupload"). 4341: "&scantron_format=".&Apache::lonnet::escape($ENV{'form.scantron_format'}). 4342: "&scantron_CODElist=".&Apache::lonnet::escape($ENV{'form.scantron_CODElist'}). 4343: "&curCODE=".&Apache::lonnet::escape($$scan_record{'scantron.CODE'}). 4344: "&scantron_selectfile=".&Apache::lonnet::escape($ENV{'form.scantron_selectfile'}); 4345: $r->print("<input type='radio' name='scantron_CODE_resolution' value='use_found' /> <a target='_blank' href='$href'>Select</a> a CODE from the list of all CODEs and use it. Selected CODE is <input readonly='true' type='text' size='8' name='scantron_CODE_selectedvalue' onfocus=\"javascript:change_radio('use_found')\" onchange=\"javascript:change_radio('use_found')\" />"); 4346: $r->print("\n<br />"); 4347: $r->print("<input type='radio' name='scantron_CODE_resolution' value='use_typed' /> Use <input type='text' size='8' name='scantron_CODE_newvalue' onfocus=\"javascript:change_radio('use_typed')\" onkeypress=\"javascript:change_radio('use_typed')\" /> as the CODE."); 4348: $r->print("\n<br /><br />"); 4349: } elsif ($error eq 'doublebubble') { 4350: $r->print("<p>There have been multiple bubbles scanned for a some question(s)</p>\n"); 4351: $r->print('<input type="hidden" name="scantron_questions" value="'. 4352: join(',',@{$arg}).'" />'); 4353: $r->print("<p>Please indicate which bubble should be used for grading</p>"); 4354: foreach my $question (@{$arg}) { 4355: my $selected=$$scan_record{"scantron.$question.answer"}; 4356: &scantron_bubble_selector($r,$scan_config,$question,split('',$selected)); 4357: } 4358: } elsif ($error eq 'missingbubble') { 4359: $r->print("<p>There have been <b>no</b> bubbles scanned for some question(s)</p>\n"); 4360: $r->print("<p>Please indicate which bubble should be used for grading</p>"); 4361: $r->print("Some questions have no scanned bubbles\n"); 4362: $r->print('<input type="hidden" name="scantron_questions" value="'. 4363: join(',',@{$arg}).'" />'); 4364: foreach my $question (@{$arg}) { 4365: my $selected=$$scan_record{"scantron.$question.answer"}; 4366: &scantron_bubble_selector($r,$scan_config,$question); 4367: } 4368: } else { 4369: $r->print("\n<ul>"); 4370: } 4371: $r->print("\n</li></ul>"); 4372: 4373: } 4374: 4375: sub scantron_bubble_selector { 4376: my ($r,$scan_config,$quest,@selected)=@_; 4377: my $max=$$scan_config{'Qlength'}; 4378: my @alphabet=('A'..'Z'); 4379: $r->print("<table border='1'><tr><td rowspan='2'>$quest</td>"); 4380: for (my $i=0;$i<$max+1;$i++) { 4381: $r->print('<td align="center">'); 4382: if ($selected[0] eq $alphabet[$i]) { $r->print('X'); shift(@selected) } 4383: else { $r->print(' '); } 4384: $r->print('</td>'); 4385: } 4386: $r->print('<td></td></tr><tr>'); 4387: for (my $i=0;$i<$max;$i++) { 4388: $r->print('<td><input type="radio" name="scantron_correct_Q_'.$quest. 4389: '" value="'.$i.'" />'.$alphabet[$i]."</td>"); 4390: } 4391: $r->print('<td><input type="radio" name="scantron_correct_Q_'.$quest. 4392: '" value="none" /> No bubble </td>'); 4393: $r->print('</tr></table>'); 4394: } 4395: 4396: sub num_matches { 4397: my ($orig,$code) = @_; 4398: my @code=split(//,$code); 4399: my @orig=split(//,$orig); 4400: my $same=0; 4401: for (my $i=0;$i<scalar(@code);$i++) { 4402: if ($code[$i] eq $orig[$i]) { $same++; } 4403: } 4404: return $same; 4405: } 4406: 4407: sub scantron_get_closely_matching_CODEs { 4408: my ($allcodes,$CODE)=@_; 4409: my @CODEs; 4410: foreach my $testcode (sort(keys(%{$allcodes}))) { 4411: push(@{$CODEs[&num_matches($CODE,$testcode)]},$testcode); 4412: } 4413: 4414: return ($#CODEs,$CODEs[-1]); 4415: } 4416: 4417: sub get_codes { 4418: my $old_name=$ENV{'form.scantron_CODElist'}; 4419: my $cdom =$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}; 4420: my $cnum =$ENV{'course.'.$ENV{'request.course.id'}.'.num'}; 4421: my %result=&Apache::lonnet::get('CODEs',[$old_name],$cdom,$cnum); 4422: my %allcodes=map {(&Apache::lonprintout::num_to_letters($_),1)} split(',',$result{$old_name}); 4423: return %allcodes; 4424: } 4425: 4426: sub scantron_validate_CODE { 4427: my ($r,$currentphase) = @_; 4428: my %scantron_config=&get_scantron_config($ENV{'form.scantron_format'}); 4429: if ($scantron_config{'CODElocation'} && 4430: $scantron_config{'CODEstart'} && 4431: $scantron_config{'CODElength'}) { 4432: if (!defined($ENV{'form.scantron_CODElist'})) { 4433: &FIXME_blow_up() 4434: } 4435: } else { 4436: return (0,$currentphase+1); 4437: } 4438: 4439: my %usedCODEs; 4440: 4441: my %allcodes=&get_codes(); 4442: 4443: my ($scanlines,$scan_data)=&scantron_getfile(); 4444: for (my $i=0;$i<=$scanlines->{'count'};$i++) { 4445: my $line=&scantron_get_line($scanlines,$scan_data,$i); 4446: if ($line=~/^[\s\cz]*$/) { next; } 4447: my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config, 4448: $scan_data); 4449: my $CODE=$$scan_record{'scantron.CODE'}; 4450: my $error=0; 4451: if (!&Apache::lonnet::validCODE($CODE)) { 4452: &scantron_get_correction($r,$i,$scan_record, 4453: \%scantron_config, 4454: $line,'incorrectCODE',\%allcodes); 4455: return(1,$currentphase); 4456: } 4457: if (%allcodes && !exists($allcodes{$CODE}) 4458: && !$$scan_record{'scantron.useCODE'}) { 4459: &scantron_get_correction($r,$i,$scan_record, 4460: \%scantron_config, 4461: $line,'incorrectCODE',\%allcodes); 4462: return(1,$currentphase); 4463: } 4464: if (exists($usedCODEs{$CODE}) 4465: && $ENV{'form.scantron_CODEunique'} eq 'yes' 4466: && !$$scan_record{'scantron.CODE_ignore_dup'}) { 4467: &scantron_get_correction($r,$i,$scan_record, 4468: \%scantron_config, 4469: $line,'duplicateCODE',$usedCODEs{$CODE}); 4470: return(1,$currentphase); 4471: } 4472: push (@{$usedCODEs{$CODE}},$$scan_record{'scantron.PaperID'}); 4473: } 4474: return (0,$currentphase+1); 4475: } 4476: 4477: sub scantron_validate_doublebubble { 4478: my ($r,$currentphase) = @_; 4479: #get student info 4480: my $classlist=&Apache::loncoursedata::get_classlist(); 4481: my %idmap=&username_to_idmap($classlist); 4482: 4483: #get scantron line setup 4484: my %scantron_config=&get_scantron_config($ENV{'form.scantron_format'}); 4485: my ($scanlines,$scan_data)=&scantron_getfile(); 4486: for (my $i=0;$i<=$scanlines->{'count'};$i++) { 4487: my $line=&scantron_get_line($scanlines,$scan_data,$i); 4488: if ($line=~/^[\s\cz]*$/) { next; } 4489: my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config, 4490: $scan_data); 4491: if (!defined($$scan_record{'scantron.doubleerror'})) { next; } 4492: &scantron_get_correction($r,$i,$scan_record,\%scantron_config,$line, 4493: 'doublebubble', 4494: $$scan_record{'scantron.doubleerror'}); 4495: return (1,$currentphase); 4496: } 4497: return (0,$currentphase+1); 4498: } 4499: 4500: sub scantron_get_maxbubble { 4501: my ($r)=@_; 4502: if (defined($ENV{'form.scantron_maxbubble'}) && 4503: $ENV{'form.scantron_maxbubble'}) { 4504: return $ENV{'form.scantron_maxbubble'}; 4505: } 4506: my $navmap=Apache::lonnavmaps::navmap->new(); 4507: my (undef,undef,$sequence)= 4508: &Apache::lonnet::decode_symb($ENV{'form.selectpage'}); 4509: my $map=$navmap->getResourceByUrl($sequence); 4510: my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0); 4511: &Apache::lonnet::delenv('form.counter'); 4512: foreach my $resource (@resources) { 4513: my $result=&Apache::lonnet::ssi($resource->src()); 4514: } 4515: &Apache::lonnet::delenv('scantron\.'); 4516: my $envfile=$ENV{'user.environment'}; 4517: $envfile=~/\/([^\/]+)\.id$/; 4518: $envfile=$1; 4519: &Apache::lonnet::transfer_profile_to_env($r->dir_config('lonIDsDir'), 4520: $envfile); 4521: $ENV{'form.scantron_maxbubble'}=$ENV{'form.counter'}-1; 4522: return $ENV{'form.scantron_maxbubble'}; 4523: } 4524: 4525: sub scantron_validate_missingbubbles { 4526: my ($r,$currentphase) = @_; 4527: #get student info 4528: my $classlist=&Apache::loncoursedata::get_classlist(); 4529: my %idmap=&username_to_idmap($classlist); 4530: 4531: #get scantron line setup 4532: my %scantron_config=&get_scantron_config($ENV{'form.scantron_format'}); 4533: my ($scanlines,$scan_data)=&scantron_getfile(); 4534: my $max_bubble=&scantron_get_maxbubble(); 4535: if (!$max_bubble) { $max_bubble=2**31; } 4536: for (my $i=0;$i<=$scanlines->{'count'};$i++) { 4537: my $line=&scantron_get_line($scanlines,$scan_data,$i); 4538: if ($line=~/^[\s\cz]*$/) { next; } 4539: my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config, 4540: $scan_data); 4541: if (!defined($$scan_record{'scantron.missingerror'})) { next; } 4542: my @to_correct; 4543: foreach my $missing (@{$$scan_record{'scantron.missingerror'}}) { 4544: if ($missing > $max_bubble) { next; } 4545: push(@to_correct,$missing); 4546: } 4547: if (@to_correct) { 4548: &scantron_get_correction($r,$i,$scan_record,\%scantron_config, 4549: $line,'missingbubble',\@to_correct); 4550: return (1,$currentphase); 4551: } 4552: 4553: } 4554: return (0,$currentphase+1); 4555: } 4556: 4557: sub scantron_process_students { 4558: my ($r) = @_; 4559: my (undef,undef,$sequence)=&Apache::lonnet::decode_symb($ENV{'form.selectpage'}); 4560: my ($symb,$url)=&get_symb_and_url($r); 4561: if (!$symb) {return '';} 4562: my $default_form_data=&defaultFormData($symb,$url); 4563: 4564: my %scantron_config=&get_scantron_config($ENV{'form.scantron_format'}); 4565: my ($scanlines,$scan_data)=&scantron_getfile(); 4566: my $classlist=&Apache::loncoursedata::get_classlist(); 4567: my %idmap=&username_to_idmap($classlist); 4568: my $navmap=Apache::lonnavmaps::navmap->new(); 4569: my $map=$navmap->getResourceByUrl($sequence); 4570: my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0); 4571: # $r->print("geto ".scalar(@resources)."<br />"); 4572: my $result= <<SCANTRONFORM; 4573: <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload"> 4574: <input type="hidden" name="command" value="scantron_configphase" /> 4575: $default_form_data 4576: SCANTRONFORM 4577: $r->print($result); 4578: 4579: my @delayqueue; 4580: my %completedstudents; 4581: 4582: my $count=&get_todo_count($scanlines,$scan_data); 4583: my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron Status', 4584: 'Scantron Progress',$count, 4585: 'inline',undef,'scantronupload'); 4586: &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state, 4587: 'Processing first student'); 4588: my $start=&Time::HiRes::time(); 4589: my $i=-1; 4590: my ($uname,$udom,$started); 4591: while ($i<$scanlines->{'count'}) { 4592: ($uname,$udom)=('',''); 4593: $i++; 4594: my $line=&scantron_get_line($scanlines,$scan_data,$i); 4595: if ($line=~/^[\s\cz]*$/) { next; } 4596: if ($started) { 4597: &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state, 4598: 'last student'); 4599: } 4600: $started=1; 4601: my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config, 4602: $scan_data); 4603: unless ($uname=&scantron_find_student($scan_record,$scan_data, 4604: \%idmap,$i)) { 4605: &scantron_add_delay(\@delayqueue,$line, 4606: 'Unable to find a student that matches',1); 4607: next; 4608: } 4609: if (exists $completedstudents{$uname}) { 4610: &scantron_add_delay(\@delayqueue,$line, 4611: 'Student '.$uname.' has multiple sheets',2); 4612: next; 4613: } 4614: ($uname,$udom)=split(/:/,$uname); 4615: &Apache::lonnet::delenv('form.counter'); 4616: &Apache::lonnet::appenv(%$scan_record); 4617: 4618: my $i=0; 4619: foreach my $resource (@resources) { 4620: $i++; 4621: my %form=('submitted' =>'scantron', 4622: 'grade_target' =>'grade', 4623: 'grade_username'=>$uname, 4624: 'grade_domain' =>$udom, 4625: 'grade_courseid'=>$ENV{'request.course.id'}, 4626: 'grade_symb' =>$resource->symb()); 4627: if (exists($scan_record->{'scantron.CODE'}) && 4628: $scan_record->{'scantron.CODE'}) { 4629: $form{'CODE'}=$scan_record->{'scantron.CODE'}; 4630: } else { 4631: $form{'CODE'}=''; 4632: } 4633: my $result=&Apache::lonnet::ssi($resource->src(),%form); 4634: if (&Apache::loncommon::connection_aborted($r)) { last; } 4635: } 4636: $completedstudents{$uname}={'line'=>$line}; 4637: if (&Apache::loncommon::connection_aborted($r)) { last; } 4638: } continue { 4639: &Apache::lonnet::delenv('form.counter'); 4640: &Apache::lonnet::delenv('scantron\.'); 4641: } 4642: &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); 4643: # my $lasttime = &Time::HiRes::time()-$start; 4644: # $r->print("<p>took $lasttime</p>"); 4645: 4646: $r->print("</form>"); 4647: $r->print(&show_grading_menu_form($symb,$url)); 4648: return ''; 4649: } 4650: 4651: sub scantron_upload_scantron_data { 4652: my ($r)=@_; 4653: $r->print(&Apache::loncommon::coursebrowser_javascript($ENV{'request.role.domain'})); 4654: my $select_link=&Apache::loncommon::selectcourse_link('rules','courseid', 4655: 'domainid', 4656: 'coursename'); 4657: my $domsel=&Apache::loncommon::select_dom_form($ENV{'request.role.domain'}, 4658: 'domainid'); 4659: my $default_form_data=&defaultFormData(&get_symb_and_url($r,1)); 4660: $r->print(<<UPLOAD); 4661: <script type="text/javascript" language="javascript"> 4662: function checkUpload(formname) { 4663: if (formname.upfile.value == "") { 4664: alert("Please use the browse button to select a file from your local directory."); 4665: return false; 4666: } 4667: formname.submit(); 4668: } 4669: </script> 4670: 4671: <form enctype='multipart/form-data' action='/adm/grades' name='rules' method='post'> 4672: $default_form_data 4673: <table> 4674: <tr><td>$select_link </td></tr> 4675: <tr><td>Course ID: </td><td><input name='courseid' type='text' /> </td></tr> 4676: <tr><td>Course Name: </td><td><input name='coursename' type='text' /></td></tr> 4677: <tr><td>Domain: </td><td>$domsel </td></tr> 4678: <tr><td>File to upload:</td><td><input type="file" name="upfile" size="50" /></td></tr> 4679: </table> 4680: <input name='command' value='scantronupload_save' type='hidden' /> 4681: <input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scantron Data" /> 4682: </form> 4683: UPLOAD 4684: return ''; 4685: } 4686: 4687: sub scantron_upload_scantron_data_save { 4688: my($r)=@_; 4689: my ($symb,$url)=&get_symb_and_url($r,1); 4690: my $doanotherupload= 4691: '<br /><form action="/adm/grades" method="post">'."\n". 4692: '<input type="hidden" name="command" value="scantronupload" />'."\n". 4693: '<input type="submit" name="submit" value="Do Another Upload" />'."\n". 4694: '</form>'."\n"; 4695: if (!&Apache::lonnet::allowed('usc',$ENV{'form.domainid'}) && 4696: !&Apache::lonnet::allowed('usc', 4697: $ENV{'form.domainid'}.'_'.$ENV{'form.courseid'})) { 4698: $r->print("You are not allowed to upload Scantron data to the requested course.<br />"); 4699: if ($symb) { 4700: $r->print(&show_grading_menu_form($symb,$url)); 4701: } else { 4702: $r->print($doanotherupload); 4703: } 4704: return ''; 4705: } 4706: my %coursedata=&Apache::lonnet::coursedescription($ENV{'form.domainid'}.'_'.$ENV{'form.courseid'}); 4707: $r->print("Doing upload to ".$coursedata{'description'}." <br />"); 4708: my $home=&Apache::lonnet::homeserver($ENV{'form.courseid'}, 4709: $ENV{'form.domainid'}); 4710: my $fname=$ENV{'form.upfile.filename'}; 4711: #FIXME 4712: #copied from lonnet::userfileupload() 4713: #make that function able to target a specified course 4714: # Replace Windows backslashes by forward slashes 4715: $fname=~s/\\/\//g; 4716: # Get rid of everything but the actual filename 4717: $fname=~s/^.*\/([^\/]+)$/$1/; 4718: # Replace spaces by underscores 4719: $fname=~s/\s+/\_/g; 4720: # Replace all other weird characters by nothing 4721: $fname=~s/[^\w\.\-]//g; 4722: # See if there is anything left 4723: unless ($fname) { return 'error: no uploaded file'; } 4724: my $uploadedfile=$fname; 4725: $fname='scantron_orig_'.$fname; 4726: if (length($ENV{'form.upfile'}) < 2) { 4727: $r->print("<font color='red'>Error:</font> The file you attempted to upload, <tt>".&HTML::Entities::encode($ENV{'form.upfile.filename'},'<>&"')."</tt>, contained no information. Please check that you entered the correct filename."); 4728: } else { 4729: my $result=&Apache::lonnet::finishuserfileupload($ENV{'form.courseid'},$ENV{'form.domainid'},$home,'upfile',$fname); 4730: if ($result =~ m|^/uploaded/|) { 4731: $r->print("<font color='green'>Success:</font> Successfully uploaded ".(length($ENV{'form.upfile'})-1)." bytes of data into location <tt>".$result."</tt>"); 4732: } else { 4733: $r->print("<font color='red'>Error:</font> An error (".$result.") occurred when attempting to upload the file, <tt>".&HTML::Entities::encode($ENV{'form.upfile.filename'},'<>&"')."</tt>"); 4734: } 4735: } 4736: if ($symb) { 4737: $r->print(&scantron_selectphase($r,$uploadedfile)); 4738: } else { 4739: $r->print($doanotherupload); 4740: } 4741: return ''; 4742: } 4743: 4744: sub valid_file { 4745: my ($requested_file)=@_; 4746: foreach my $filename (sort(&scantron_filenames())) { 4747: &Apache::lonnet::logthis("$requested_file $filename"); 4748: if ($requested_file eq $filename) { return 1; } 4749: } 4750: return 0; 4751: } 4752: 4753: sub scantron_download_scantron_data { 4754: my ($r)=@_; 4755: my $default_form_data=&defaultFormData(&get_symb_and_url($r,1)); 4756: my $cname=$ENV{'course.'.$ENV{'request.course.id'}.'.num'}; 4757: my $cdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}; 4758: my $file=$ENV{'form.scantron_selectfile'}; 4759: if (! &valid_file($file)) { 4760: $r->print(<<ERROR); 4761: <p> 4762: The requested file name was invalid. 4763: </p> 4764: ERROR 4765: $r->print(&show_grading_menu_form(&get_symb_and_url($r,1))); 4766: return; 4767: } 4768: my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file; 4769: my $corrected='/uploaded/'.$cdom.'/'.$cname.'/scantron_corrected_'.$file; 4770: my $skipped='/uploaded/'.$cdom.'/'.$cname.'/scantron_skipped_'.$file; 4771: &Apache::lonnet::allowuploaded('/adm/grades',$orig); 4772: &Apache::lonnet::allowuploaded('/adm/grades',$corrected); 4773: &Apache::lonnet::allowuploaded('/adm/grades',$skipped); 4774: $r->print(<<DOWNLOAD); 4775: <p> 4776: <a href="$orig">Original</a> file as uploaded by the scantron office. 4777: </p> 4778: <p> 4779: <a href="$corrected">Corrections</a>, a file of corrected records that were used in grading. 4780: </p> 4781: <p> 4782: <a href="$skipped">Skipped</a>, a file of records that were skipped. 4783: </p> 4784: DOWNLOAD 4785: $r->print(&show_grading_menu_form(&get_symb_and_url($r,1))); 4786: return ''; 4787: } 4788: 4789: #-------- end of section for handling grading scantron forms ------- 4790: # 4791: #------------------------------------------------------------------- 4792: 4793: 4794: #-------------------------- Menu interface ------------------------- 4795: # 4796: #--- Show a Grading Menu button - Calls the next routine --- 4797: sub show_grading_menu_form { 4798: my ($symb,$url)=@_; 4799: my $result.='<br /><form action="/adm/grades" method="post">'."\n". 4800: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n". 4801: '<input type="hidden" name="url" value="'.$url.'" />'."\n". 4802: '<input type="hidden" name="saveState" value="'.$ENV{'form.saveState'}.'" />'."\n". 4803: '<input type="hidden" name="command" value="gradingmenu" />'."\n". 4804: '<input type="submit" name="submit" value="Grading Menu" />'."\n". 4805: '</form>'."\n"; 4806: return $result; 4807: } 4808: 4809: # -- Retrieve choices for grading form 4810: sub savedState { 4811: my %savedState = (); 4812: if ($ENV{'form.saveState'}) { 4813: foreach (split(/:/,$ENV{'form.saveState'})) { 4814: my ($key,$value) = split(/=/,$_,2); 4815: $savedState{$key} = $value; 4816: } 4817: } 4818: return \%savedState; 4819: } 4820: 4821: #--- Displays the main menu page ------- 4822: sub gradingmenu { 4823: my ($request) = @_; 4824: my ($symb,$url)=&get_symb_and_url($request); 4825: if (!$symb) {return '';} 4826: my $probTitle = &Apache::lonnet::gettitle($symb); 4827: 4828: $request->print(<<GRADINGMENUJS); 4829: <script type="text/javascript" language="javascript"> 4830: function checkChoice(formname,val,cmdx) { 4831: if (val <= 2) { 4832: var cmd = radioSelection(formname.radioChoice); 4833: var cmdsave = cmd; 4834: } else { 4835: cmd = cmdx; 4836: cmdsave = 'submission'; 4837: } 4838: formname.command.value = cmd; 4839: formname.saveState.value = "saveCmd="+cmdsave+":saveSec="+pullDownSelection(formname.section)+ 4840: ":saveSub="+pullDownSelection(formname.submitonly)+":saveStatus="+pullDownSelection(formname.Status); 4841: if (val < 5) formname.submit(); 4842: if (val == 5) { 4843: if (!checkReceiptNo(formname,'notOK')) { return false;} 4844: formname.submit(); 4845: } 4846: } 4847: 4848: function checkReceiptNo(formname,nospace) { 4849: var receiptNo = formname.receipt.value; 4850: var checkOpt = false; 4851: if (nospace == "OK" && isNaN(receiptNo)) {checkOpt = true;} 4852: if (nospace == "notOK" && (isNaN(receiptNo) || receiptNo == "")) {checkOpt = true;} 4853: if (checkOpt) { 4854: alert("Please enter a receipt number given by a student in the receipt box."); 4855: formname.receipt.value = ""; 4856: formname.receipt.focus(); 4857: return false; 4858: } 4859: return true; 4860: } 4861: </script> 4862: GRADINGMENUJS 4863: &commonJSfunctions($request); 4864: my $result='<h3> <font color="#339933">Manual Grading/View Submission</font></h3>'; 4865: my ($table,undef,$hdgrade) = &showResourceInfo($url,$probTitle); 4866: $result.=$table; 4867: my (undef,$sections) = &getclasslist('all','0'); 4868: my $savedState = &savedState(); 4869: my $saveCmd = ($$savedState{'saveCmd'} eq '' ? 'submission' : $$savedState{'saveCmd'}); 4870: my $saveSec = ($$savedState{'saveSec'} eq '' ? 'all' : $$savedState{'saveSec'}); 4871: my $saveSub = ($$savedState{'saveSub'} eq '' ? 'all' : $$savedState{'saveSub'}); 4872: my $saveStatus = ($$savedState{'saveStatus'} eq '' ? 'Active' : $$savedState{'saveStatus'}); 4873: 4874: $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n". 4875: '<input type="hidden" name="symb" value="'.$symb.'" />'."\n". 4876: '<input type="hidden" name="url" value="'.$url.'" />'."\n". 4877: '<input type="hidden" name="handgrade" value="'.$hdgrade.'" />'."\n". 4878: '<input type="hidden" name="probTitle" value="'.$probTitle.'" />'."\n". 4879: '<input type="hidden" name="command" value="" />'."\n". 4880: '<input type="hidden" name="saveState" value="" />'."\n". 4881: '<input type="hidden" name="gradingMenu" value="1" />'."\n". 4882: '<input type="hidden" name="showgrading" value="yes" />'."\n"; 4883: 4884: $result.='<table width="100%" border=0><tr><td bgcolor=#777777>'."\n". 4885: '<table width=100% border=0><tr bgcolor="#e6ffff"><td colspan="2">'."\n". 4886: ' <b>Select a Grading/Viewing Option</b></td></tr>'."\n". 4887: '<tr bgcolor="#ffffe6" valign="top"><td>'."\n"; 4888: 4889: $result.='<table width="100%" border=0>'; 4890: $result.='<tr bgcolor="#ffffe6" valign="top"><td>'."\n". 4891: ' '.&mt('Select Section').': <select name="section">'."\n"; 4892: if (ref($sections)) { 4893: foreach (sort (@$sections)) { 4894: $result.='<option value="'.$_.'" '. 4895: ($saveSec eq $_ ? 'selected="on"':'').'>'.$_.'</option>'."\n"; 4896: } 4897: } 4898: $result.= '<option value="all" '.($saveSec eq 'all' ? 'selected="on"' : ''). '>all</select> '; 4899: 4900: $result.=&mt('Student Status').':</b>'.&Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,undef); 4901: 4902: $result.='</td></tr>'; 4903: 4904: $result.='<tr bgcolor="#ffffe6"valign="top"><td>'. 4905: '<input type="radio" name="radioChoice" value="submission" '. 4906: ($saveCmd eq 'submission' ? 'checked' : '').'> '.'<b>'.&mt('Current Resource').':</b> '.&mt('For one or more students'). 4907: ' <select name="submitonly">'. 4908: '<option value="yes" '. 4909: ($saveSub eq 'yes' ? 'selected="on"' : '').'>with submissions</option>'. 4910: '<option value="graded" '. 4911: ($saveSub eq 'graded' ? 'selected="on"' : '').'>with ungraded submissions</option>'. 4912: '<option value="incorrect" '. 4913: ($saveSub eq 'incorrect' ? 'selected="on"' : '').'>with incorrect submissions</option>'. 4914: '<option value="all" '. 4915: ($saveSub eq 'all' ? 'selected="on"' : '').'>with any status</option></select></td></tr>'."\n"; 4916: 4917: $result.='<tr bgcolor="#ffffe6"valign="top"><td>'. 4918: '<input type="radio" name="radioChoice" value="viewgrades" '. 4919: ($saveCmd eq 'viewgrades' ? 'checked' : '').'> '. 4920: '<b>Current Resource:</b> For all students in selected section or course</td></tr>'."\n"; 4921: 4922: $result.='<tr bgcolor="#ffffe6" valign="top"><td>'. 4923: '<input type="radio" name="radioChoice" value="pickStudentPage" '. 4924: ($saveCmd eq 'pickStudentPage' ? 'checked' : '').'> '. 4925: 'The <b>complete</b> set/page/sequence: For one student</td></tr>'."\n"; 4926: 4927: $result.='<tr bgcolor="#ffffe6"><td><br />'. 4928: '<input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="Next->" />'. 4929: '</td></tr></table>'."\n"; 4930: 4931: $result.='</td><td valign="top">'; 4932: 4933: $result.='<table width="100%" border=0>'; 4934: $result.='<tr bgcolor="#ffffe6"><td>'. 4935: '<input type="button" onClick="javascript:checkChoice(this.form,\'3\',\'csvform\');" value="'.&mt('Upload').'" />'. 4936: ' '.&mt('scores from file').' </td></tr>'."\n"; 4937: 4938: $result.='<tr bgcolor="#ffffe6"valign="top"><td colspan="2">'. 4939: '<input type="button" onClick="javascript:checkChoice(this.form,\'4\',\'scantron_selectphase\');'. 4940: '" value="'.&mt('Grade').'" /> scantron forms</td></tr>'."\n"; 4941: 4942: if ((&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'})) && ($symb)) { 4943: $result.='<tr bgcolor="#ffffe6"valign="top"><td>'. 4944: '<input type="button" onClick="javascript:checkChoice(this.form,\'5\',\'verify\');" value="'.&mt('Verify').'" />'. 4945: ' '.&mt('receipt').': '. 4946: &Apache::lonnet::recprefix($ENV{'request.course.id'}). 4947: '-<input type="text" name="receipt" size="4" onChange="javascript:checkReceiptNo(this.form,\'OK\')">'. 4948: '</td></tr>'."\n"; 4949: } 4950: 4951: $result.='</form></td></tr></table>'."\n". 4952: '</td></tr></table>'."\n". 4953: '</td></tr></table>'."\n"; 4954: return $result; 4955: } 4956: 4957: sub handler { 4958: my $request=$_[0]; 4959: 4960: undef(%perm); 4961: if ($ENV{'browser.mathml'}) { 4962: &Apache::loncommon::content_type($request,'text/xml'); 4963: } else { 4964: &Apache::loncommon::content_type($request,'text/html'); 4965: } 4966: $request->send_http_header; 4967: return '' if $request->header_only; 4968: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}); 4969: my $url=$ENV{'form.url'}; 4970: my $symb=$ENV{'form.symb'}; 4971: my @commands=&Apache::loncommon::get_env_multiple('form.command'); 4972: my $command=$commands[0]; 4973: if ($#commands > 0) { 4974: &Apache::lonnet::logthis("grades got multiple commands ".join(':',@commands)); 4975: } 4976: if (!$url) { 4977: my ($temp1,$temp2); 4978: ($temp1,$temp2,$ENV{'form.url'})=&Apache::lonnet::decode_symb($symb); 4979: $url = $ENV{'form.url'}; 4980: } 4981: &send_header($request); 4982: if ($url eq '' && $symb eq '' && $command eq '') { 4983: if ($ENV{'user.adv'}) { 4984: if (($ENV{'form.codeone'}) && ($ENV{'form.codetwo'}) && 4985: ($ENV{'form.codethree'})) { 4986: my $token=$ENV{'form.codeone'}.'*'.$ENV{'form.codetwo'}.'*'. 4987: $ENV{'form.codethree'}; 4988: my ($tsymb,$tuname,$tudom,$tcrsid)= 4989: &Apache::lonnet::checkin($token); 4990: if ($tsymb) { 4991: my ($map,$id,$url)=&Apache::lonnet::decode_symb($tsymb); 4992: if (&Apache::lonnet::allowed('mgr',$tcrsid)) { 4993: $request->print(&Apache::lonnet::ssi_body('/res/'.$url, 4994: ('grade_username' => $tuname, 4995: 'grade_domain' => $tudom, 4996: 'grade_courseid' => $tcrsid, 4997: 'grade_symb' => $tsymb))); 4998: } else { 4999: $request->print('<h3>Not authorized: '.$token.'</h3>'); 5000: } 5001: } else { 5002: $request->print('<h3>Not a valid DocID: '.$token.'</h3>'); 5003: } 5004: } else { 5005: $request->print(&Apache::lonxml::tokeninputfield()); 5006: } 5007: } 5008: } else { 5009: if (!($perm{'vgr'}=&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'}))) { 5010: if ($perm{'vgr'}=&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'}.'/'.$ENV{'request.course.sec'})) { 5011: $perm{'vgr_section'}=$ENV{'request.course.sec'}; 5012: } else { 5013: delete($perm{'vgr'}); 5014: } 5015: } 5016: if (!($perm{'mgr'}=&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'}))) { 5017: if ($perm{'mgr'}=&Apache::lonnet::allowed('mgr',$ENV{'request.course.id'}.'/'.$ENV{'request.course.sec'})) { 5018: $perm{'mgr_section'}=$ENV{'request.course.sec'}; 5019: } else { 5020: delete($perm{'mgr'}); 5021: } 5022: } 5023: if ($command eq 'submission' && $perm{'vgr'}) { 5024: ($ENV{'form.student'} eq '' ? &listStudents($request) : &submission($request,0,0)); 5025: } elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) { 5026: &pickStudentPage($request); 5027: } elsif ($command eq 'displayPage' && $perm{'vgr'}) { 5028: &displayPage($request); 5029: } elsif ($command eq 'gradeByPage' && $perm{'mgr'}) { 5030: &updateGradeByPage($request); 5031: } elsif ($command eq 'processGroup' && $perm{'vgr'}) { 5032: &processGroup($request); 5033: } elsif ($command eq 'gradingmenu' && $perm{'vgr'}) { 5034: $request->print(&gradingmenu($request)); 5035: } elsif ($command eq 'viewgrades' && $perm{'vgr'}) { 5036: $request->print(&viewgrades($request)); 5037: } elsif ($command eq 'handgrade' && $perm{'mgr'}) { 5038: $request->print(&processHandGrade($request)); 5039: } elsif ($command eq 'editgrades' && $perm{'mgr'}) { 5040: $request->print(&editgrades($request)); 5041: } elsif ($command eq 'verify' && $perm{'vgr'}) { 5042: $request->print(&verifyreceipt($request)); 5043: } elsif ($command eq 'csvform' && $perm{'mgr'}) { 5044: $request->print(&upcsvScores_form($request)); 5045: } elsif ($command eq 'csvupload' && $perm{'mgr'}) { 5046: $request->print(&csvupload($request)); 5047: } elsif ($command eq 'csvuploadmap' && $perm{'mgr'} ) { 5048: $request->print(&csvuploadmap($request)); 5049: } elsif ($command eq 'csvuploadassign' && $perm{'mgr'}) { 5050: if ($ENV{'form.associate'} ne 'Reverse Association') { 5051: $request->print(&csvuploadassign($request)); 5052: } else { 5053: if ( $ENV{'form.upfile_associate'} ne 'reverse' ) { 5054: $ENV{'form.upfile_associate'} = 'reverse'; 5055: } else { 5056: $ENV{'form.upfile_associate'} = 'forward'; 5057: } 5058: $request->print(&csvuploadmap($request)); 5059: } 5060: } elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) { 5061: $request->print(&scantron_selectphase($request)); 5062: } elsif ($command eq 'scantron_warning' && $perm{'mgr'}) { 5063: $request->print(&scantron_do_warning($request)); 5064: } elsif ($command eq 'scantron_validate' && $perm{'mgr'}) { 5065: $request->print(&scantron_validate_file($request)); 5066: } elsif ($command eq 'scantron_process' && $perm{'mgr'}) { 5067: $request->print(&scantron_process_students($request)); 5068: } elsif ($command eq 'scantronupload' && 5069: (&Apache::lonnet::allowed('usc',$ENV{'request.role.domain'})|| 5070: &Apache::lonnet::allowed('usc',$ENV{'request.course.id'}))) { 5071: $request->print(&scantron_upload_scantron_data($request)); 5072: } elsif ($command eq 'scantronupload_save' && 5073: (&Apache::lonnet::allowed('usc',$ENV{'request.role.domain'})|| 5074: &Apache::lonnet::allowed('usc',$ENV{'request.course.id'}))) { 5075: $request->print(&scantron_upload_scantron_data_save($request)); 5076: } elsif ($command eq 'scantron_download' && 5077: &Apache::lonnet::allowed('usc',$ENV{'request.course.id'})) { 5078: $request->print(&scantron_download_scantron_data($request)); 5079: } elsif ($command) { 5080: $request->print("Access Denied ($command)"); 5081: } 5082: } 5083: &send_footer($request); 5084: return ''; 5085: } 5086: 5087: sub send_header { 5088: my ($request)= @_; 5089: $request->print(&Apache::lontexconvert::header()); 5090: # $request->print(" 5091: #<script> 5092: #remotewindow=open('','homeworkremote'); 5093: #remotewindow.close(); 5094: #</script>"); 5095: $request->print(&Apache::loncommon::bodytag('Grading')); 5096: $request->rflush(); 5097: } 5098: 5099: sub send_footer { 5100: my ($request)= @_; 5101: $request->print('</body></html>'); 5102: } 5103: 5104: 1; 5105: 5106: __END__;