--- loncom/homework/grades.pm 2003/07/19 15:11:27 1.120 +++ loncom/homework/grades.pm 2003/09/24 23:51:14 1.130.2.1.2.1 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.120 2003/07/19 15:11:27 ng Exp $ +# $Id: grades.pm,v 1.130.2.1.2.1 2003/09/24 23:51:14 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -33,6 +33,7 @@ # June-August H.K. Ng # Year 2003 # February, March H.K. Ng +# July, H. K. Ng # package Apache::grades; @@ -95,10 +96,23 @@ sub get_fullname { return $fullname; } +#--- Format fullname, username:domain if different for display +#--- Use anywhere where the student names are listed +sub nameUserString { + my ($type,$fullname,$uname,$udom) = @_; + if ($type eq 'header') { + return ' Fullname (Username) '; + } else { + return ' '.$fullname.' ('.$uname. + ($ENV{'user.domain'} eq $udom ? '' : ' ('.$udom.')').')'; + } +} + #--- Get the partlist and the response type for a given problem. --- #--- Indicate if a response type is coded handgraded or not. --- sub response_type { - my ($url) = shift; + my ($url,$symb) = shift; + $symb=($ENV{'form.symb'} ne '' ? $ENV{'form.symb'} : (&Apache::lonnet::symbread($url))) if ($symb eq ''); my $allkeys = &Apache::lonnet::metadata($url,'keys'); my %seen = (); my (@partlist,%handgrade); @@ -107,7 +121,8 @@ sub response_type { my ($responsetype,$part) = split(/_/,$_,2); my ($partid,$respid) = split(/_/,$part); $responsetype =~ s/response$//; # make it compatible w/ navmaps - should move to that!! - $handgrade{$part} = $responsetype.':'.($allkeys =~ /parameter_$part\_handgrade/ ? 'yes' : 'no'); + my ($value) = &Apache::lonnet::EXT('resource.'.$part.'.handgrade',$symb); + $handgrade{$part} = $responsetype.':'.($value eq 'yes' ? 'yes' : 'no'); next if ($seen{$partid} > 0); $seen{$partid}++; push @partlist,$partid; @@ -123,23 +138,25 @@ sub showResourceInfo { my $result =''. ''."\n"; my ($partlist,$handgrade) = &response_type($url); - my ($resptype,$hdgrade)=('','no'); + my %resptype = (); + my $hdgrade='no'; for (sort keys(%$handgrade)) { my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_}); - $resptype = $responsetype; + my $partID = (split(/_/))[0]; + $resptype{$partID} = $responsetype; $hdgrade = $handgrade if ($handgrade eq 'yes'); - $result.=''. + $result.=''. ''; # ''; } $result.='
Current Resource: '.$probTitle.'
Part '.(split(/_/))[0].'
Part '.$partID.'Type: '.$responsetype.'
Handgrade: '.$handgrade.'
'."\n"; - return $result,$resptype,$hdgrade,$partlist,$handgrade; + return $result,\%resptype,$hdgrade,$partlist,$handgrade; } #--- Clean response type for display #--- Currently filters option response type only. sub cleanRecord { - my ($answer,$response) = @_; + my ($answer,$response,$symb) = @_; if ($response eq 'option') { my (@IDs,@ans); foreach (split(/\&/,&Apache::lonnet::unescape($answer))) { @@ -148,12 +165,27 @@ sub cleanRecord { push @ans,$ans; } my $grayFont = ''; - return ''. + return '
'. ''. ''. - '
Answer'. (join '',@ans).'
'.$grayFont.'Option ID'.$grayFont. (join ''.$grayFont,@IDs).'
'; + ''; + } + if ($response eq 'essay') { + if (! exists ($ENV{'form.'.$symb})) { + my (%keyhash) = &Apache::lonnet::dump('nohist_handgrade', + $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}, + $ENV{'course.'.$ENV{'request.course.id'}.'.num'}); + + my $loginuser = $ENV{'user.name'}.':'.$ENV{'user.domain'}; + $ENV{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : ''; + $ENV{'form.kwclr'} = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red'; + $ENV{'form.kwsize'} = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0'; + $ENV{'form.kwstyle'} = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : ''; + $ENV{'form.'.$symb} = 1; # so that we don't have to read it from disk for multiple sub of the same prob. + } + return '

'.&keywords_highlight($answer).'
'; } return $answer; } @@ -197,6 +229,7 @@ COMMONJSFUNCTIONS #--- section, ids and fullnames for each user. sub getclasslist { my ($getsec,$filterlist) = @_; + $getsec = $getsec eq '' ? 'all' : $getsec; my $classlist=&Apache::loncoursedata::get_classlist(); # Bail out if we were unable to get the classlist return if (! defined($classlist)); @@ -277,7 +310,7 @@ sub student_gradeStatus { my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname); my %partstatus = (); foreach (@$partlist) { - my ($status,$foo) = split(/_/,$record{"resource.$_.solved"},2); + my ($status,undef) = split(/_/,$record{"resource.$_.solved"},2); $status = 'nothing' if ($status eq ''); $partstatus{$_} = $status; my $subkey = "resource.$_.submitted_by"; @@ -303,6 +336,7 @@ sub jscriptNform { ''."\n". ''."\n". ''."\n". + ''."\n". ''."\n". ''."\n". ''."\n". @@ -430,7 +464,7 @@ sub listStudents { my $result='

 '.$viewgrade. ' Submissions for a Student or a Group of Students

'; - my ($table,$resptype,$hdgrade,$partlist,$handgrade) = &showResourceInfo($url,$ENV{'form.probTitle'}); + my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($url,$ENV{'form.probTitle'}); $result.=$table; $request->print(< last sub only'."\n". ' last sub & parts info'."\n". + ' by dates and submissions'."\n". ' all details'."\n". ''."\n". ''."\n". - ''."\n". '
'."\n". '
'."\n". ''."\n". @@ -497,30 +531,34 @@ LISTJAVASCRIPT ''."\n". ''."\n"; - $gradeTable.='Student Status: '. - &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);').'
'; + if (exists($ENV{'form.gradingMenu'}) && exists($ENV{'form.Status'})) { + $gradeTable.=''."\n"; + } else { + $gradeTable.='Student Status: '. + &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);').'
'; + } - $gradeTable.='To '.lc($viewgrade).' a submission, click on the check box next to the student\'s name. Then '."\n". - 'click on the '.$viewgrade.' button. To view the submissions for a group of students, click'."\n". - ' on the check boxes for the group of students.
'."\n". + $gradeTable.='To '.lc($viewgrade).' a submission or a group of submissions, click on the check box(es) '. + 'next to the student\'s name(s). Then click on the Next button.
'."\n". ''."\n"; $gradeTable.=''."\n"; + 'value="Next->" />'."\n"; my (undef, undef, $fullname) = &getclasslist($getsec,'1'); $gradeTable.='
'. ''; my $loop = 0; while ($loop < 2) { - $gradeTable.=''; + $gradeTable.=''. + ''; if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { foreach (sort(@$partlist)) { $gradeTable.=''; } } $loop++; +# $gradeTable.='' if ($loop%2 ==1); } $gradeTable.=''."\n"; @@ -547,10 +585,10 @@ LISTJAVASCRIPT $ctr++; if ( $perm{'vgr'} eq 'F' ) { $gradeTable.='' if ($ctr%2 ==1); - $gradeTable.=''. + ''."\n". - ''."\n"; + ''."\n"; if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { foreach (sort keys(%status)) { @@ -558,11 +596,12 @@ LISTJAVASCRIPT $gradeTable.=''."\n"; } } +# $gradeTable.='' if ($ctr%2 ==1); $gradeTable.=''."\n" if ($ctr%2 ==0); } } if ($ctr%2 ==1) { - $gradeTable.=''; + $gradeTable.=''; if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { foreach (@$partlist) { $gradeTable.=''; @@ -574,7 +613,7 @@ LISTJAVASCRIPT $gradeTable.='
 Select  Fullname '. - '(Username)  No.  Select '.&nameUserString('header').' Part '.(split(/_/))[0].' Status 
'.$ctr.'  '.$$fullname{$student}.' '."\n". - '('.$uname.')'.&nameUserString(undef,$$fullname{$student},$uname,$udom).' '.$status{$_}.' 
      
'. ''."\n"; + 'value="Next->" />'."\n"; if ($ctr == 0) { my $num_students=(scalar(keys(%$fullname))); if ($num_students eq 0) { @@ -623,9 +662,9 @@ sub sub_page_js { $request->print(< function updateRadio(formname,id,weight) { - var gradeBox = eval("formname.GD_BOX"+id); - var radioButton = eval("formname.RADVAL"+id); - var oldpts = eval("formname.oldpts"+id+".value"); + var gradeBox = formname["GD_BOX"+id]; + var radioButton = formname["RADVAL"+id]; + var oldpts = formname["oldpts"+id].value; var pts = checkSolved(formname,id) == 'update' ? gradeBox.value : oldpts; gradeBox.value = pts; var resetbox = false; @@ -647,7 +686,7 @@ sub sub_page_js { var resp = confirm("You entered a value ("+pts+ ") greater than the weight for the part. Accept?"); if (resp == false) { - gradeBox.value = ""; + gradeBox.value = oldpts; return; } } @@ -659,18 +698,17 @@ sub sub_page_js { } } updateSelect(formname,id); - var stores = eval("formname.stores"+id); - stores.value = "0"; + formname["stores"+id].value = "0"; } function writeBox(formname,id,pts) { - var gradeBox = eval("formname.GD_BOX"+id); + var gradeBox = formname["GD_BOX"+id]; if (checkSolved(formname,id) == 'update') { gradeBox.value = pts; } else { - var oldpts = eval("formname.oldpts"+id+".value"); + var oldpts = formname["oldpts"+id].value; gradeBox.value = oldpts; - var radioButton = eval("formname.RADVAL"+id); + var radioButton = formname["RADVAL"+id]; for (var i=0; i //===================== Show list of keywords ==================== - function keywords(keyform) { - var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",keyform.value); + function keywords(formname) { + var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",formname.keywords.value); if (nret==null) return; - keyform.value = nret; + formname.keywords.value = nret; - document.SCORE.refresh.value = "on"; - if (document.SCORE.keywords.value != "") { - document.SCORE.submit(); + if (formname.keywords.value != "") { + formname.refresh.value = "on"; + formname.submit(); } return; } @@ -839,10 +874,9 @@ sub sub_page_kw_js { } var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt); if (nret==null) return; - var curlist = document.SCORE.keywords.value; - document.SCORE.keywords.value = curlist+" "+nret; - document.SCORE.refresh.value = "on"; + document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret; if (document.SCORE.keywords.value != "") { + document.SCORE.refresh.value = "on"; document.SCORE.submit(); } return; @@ -859,21 +893,23 @@ sub sub_page_kw_js { var Nmsg = msgform.savemsgN.value; savedMsgHeader(Nmsg,usrctr,fullname); var subject = msgform.msgsub.value; - var rtrchk = eval("document.SCORE.includemsg"+usrctr); - var msgchk = rtrchk.value; + var msgchk = document.SCORE["includemsg"+usrctr].value; re = /msgsub/; var shwsel = ""; if (re.test(msgchk)) { shwsel = "checked" } - displaySubject(subject,shwsel); + subject = (document.SCORE.shownSub.value == 0 ? checkEntities(subject) : subject); + displaySubject(checkEntities(subject),shwsel); for (var i=1; i<=Nmsg; i++) { - var testpt = "savemsg"+i+","; - re = /testpt/; + var testmsg = "savemsg"+i+","; + re = new RegExp(testmsg,"g"); shwsel = ""; if (re.test(msgchk)) { shwsel = "checked" } - var message = eval("document.SCORE.savemsg"+i+".value"); - displaySavedMsg(i,message,shwsel); + var message = document.SCORE["savemsg"+i].value; + message = (document.SCORE["shownOnce"+i].value == 0 ? checkEntities(message) : message); + displaySavedMsg(i,message,shwsel); //I do not get it. w/o checkEntities on saved messages, + //any < is already converted to <, etc. However, only once!! } - newmsg = eval("document.SCORE.newmsg"+usrctr+".value"); + newmsg = document.SCORE["newmsg"+usrctr].value; shwsel = ""; re = /newmsg/; if (re.test(msgchk)) { shwsel = "checked" } @@ -882,6 +918,22 @@ sub sub_page_kw_js { return; } + function checkEntities(strx) { + if (strx.length == 0) return strx; + var orgStr = ["&", "<", ">", '"']; + var newStr = ["&", "<", ">", """]; + var counter = 0; + while (counter < 4) { + strx = strReplace(strx,orgStr[counter],newStr[counter]); + counter++; + } + return strx; + } + + function strReplace(strx, orgStr, newStr) { + return strx.split(orgStr).join(newStr); + } + function savedMsgHeader(Nmsg,usrctr,fullname) { var height = 70*Nmsg+250; var scrollbar = "no"; @@ -889,7 +941,6 @@ sub sub_page_kw_js { height = 600; scrollbar = "yes"; } -// if (window.pWin) {window.pWin.close(); window.pWin=null} var xpos = (screen.width-600)/2; xpos = (xpos < 0) ? '0' : xpos; var ypos = (screen.height-height)/2-30; @@ -898,16 +949,17 @@ sub sub_page_kw_js { pWin = window.open('', 'MessageCenter', 'toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height); pWin.focus(); pDoc = pWin.document; + pDoc.open('text/html','replace'); pDoc.write(""); pDoc.write("Message Central"); pDoc.write(" @@ -1122,11 +1179,12 @@ sub gradeBox { 'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n"; if ($$record{'resource.'.$partid.'.solved'} eq 'excused') { $result.=''. - ''."\n"; + ''; } else { $result.=''. - ''."\n"; + ''; } + $result.=''."\n"; $result.="  \n"; $result.=''."\n". ''."\n". @@ -1183,8 +1241,10 @@ sub submission { return; } - $ENV{'form.lastSub'} = ($ENV{'form.lastSub'} eq '' ? 'all' : $ENV{'form.lastSub'}); + $ENV{'form.lastSub'} = ($ENV{'form.lastSub'} eq '' ? 'datesub' : $ENV{'form.lastSub'}); my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : ''); + my $checkIcon = ''; # header info if ($counter == 0) { @@ -1197,8 +1257,6 @@ sub submission { ' Resource: '.$ENV{'form.probTitle'}.''."\n"); if ($ENV{'form.handgrade'} eq 'no') { - my $checkIcon = ''; my $checkMark='

 Note: Part(s) graded correct by the computer is marked with a '. $checkIcon.' symbol.'."\n"; $request->print($checkMark); @@ -1226,7 +1284,6 @@ sub submission { $ENV{'form.msgsub'} = $keyhash{$symb.'_subject'} ne '' ? $keyhash{$symb.'_subject'} : $ENV{'form.probTitle'}; $ENV{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0'; - } my $overRideScore = $ENV{'form.overRideScore'} eq '' ? 'no' : $ENV{'form.overRideScore'}; @@ -1246,24 +1303,27 @@ sub submission { ''."\n". ''."\n". ''."\n". - ''."\n". ''."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". - ''."\n". ''."\n"); + if ($ENV{'form.handgrade'} eq 'yes') { + $request->print(''."\n". + ''."\n". + ''."\n". + ''."\n". + ''."\n". + ''."\n". + ''."\n"); + } my ($cts,$prnmsg) = (1,''); while ($cts <= $ENV{'form.savemsgN'}) { $prnmsg.=''."\n"; + '" />'."\n". + ''."\n"; $cts++; } $request->print($prnmsg); @@ -1274,7 +1334,7 @@ sub submission { # $request->print(<Keyword Options:  -List    +List    Paste Selection to List    Highlight Attribute

@@ -1295,16 +1355,15 @@ KEYWORDS $request->print(&show_problem($request,$symb,$uname,$udom,1,1)); } my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname); - my ($partlist,$handgrade) = &response_type($url); + + my ($partlist,$handgrade) = &response_type($url,$symb); # Display student info $request->print(($counter == 0 ? '' : '
')); my $result='
'."\n". '
'."\n"; - $result.='Fullname: '.$ENV{'form.fullname'}. - '   Username: '.$uname. - ($ENV{'user.domain'} eq $udom ? '' : ' ('.$udom.')').'
'."\n"; + $result.='Fullname: '.&nameUserString(undef,$ENV{'form.fullname'},$uname,$udom).'
'."\n"; $result.=''."\n"; @@ -1393,7 +1452,8 @@ KEYWORDS if (!exists($record{'resource.'.$partid.'.'.$respid.'.submission'})) { $lastsubonly.='
Part '. $partid.' ( ID '.$respid. - ' )   Nothing submitted

'; + ' )   '. + 'Nothing submitted - no attempts

'; } else { foreach (@$string) { my ($partid,$respid) = /^resource\.(\w+)\.(\w+)\.submission/; @@ -1418,11 +1478,9 @@ KEYWORDS '"> File uploaded by student '. 'Like all files provided by users, '. 'this file may contain virusses
':''). - 'Submitted Answer: '.($responsetype =~ /^(essay|option)$/ ? - '
' : ''). - &cleanRecord(&keywords_highlight($subval),$responsetype). - ($responsetype =~ /^(essay|option)$/ ? '

' : - '

').$similar."\n" + 'Submitted Answer: '. + &cleanRecord($subval,$responsetype,$symb). + '

'.$similar."\n" if ($ENV{'form.lastSub'} eq 'lastonly' || ($ENV{'form.lastSub'} eq 'hdgrade' && $$handgrade{$part} =~ /:yes$/)); @@ -1434,23 +1492,24 @@ KEYWORDS $lastsubonly.='
'."\n"; $request->print($lastsubonly); } - } else { + } elsif ($ENV{'form.lastSub'} eq 'datesub') { + my (undef,$responseType,undef,$parts) = &showResourceInfo($url); + $request->print(&displaySubByDates(\$symb,\%record,$parts,$responseType,$checkIcon)); + } elsif ($ENV{'form.lastSub'} =~ /^(last|all)$/) { $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom, $ENV{'request.course.id'}, $last,'.submission', 'Apache::grades::keywords_highlight')); } - $result=''."\n". - ''."\n". - ''."\n"; - $request->print($result); + $request->print(''."\n"); # return if view submission with no grading option if ($ENV{'form.showgrading'} eq '' || (!&canmodify($usec))) { my $toGrade.='  '."\n"; + 'onClick="javascript:checksubmit(this.form,\'Grade Student\',\'' + .$counter.'\');" TARGET=_self>  '."\n" if (&canmodify($usec)); $toGrade.='
'."\n"; $toGrade.=&show_grading_menu_form($symb,$url) if (($ENV{'form.command'} eq 'submission') || @@ -1459,7 +1518,7 @@ KEYWORDS return; } - # essay grading options + # essay grading message center if ($ENV{'form.handgrade'} eq 'yes') { my ($lastname,$givenn) = split(/,/,$ENV{'form.fullname'}); my $msgfor = $givenn.' '.$lastname; @@ -1468,30 +1527,35 @@ KEYWORDS $msgfor .= ', '.(join ', ',@col_fullnames).' and '.$lastone.'.'; } $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript -# $result.=''."\n". - $result=' '."\n". + ''."\n"; + $result.=' '. 'Compose Message to student'.(scalar(@col_fullnames) >= 1 ? 's' : '').'  '. ''."\n". '
 (Message will be sent when you click on Save & Next below.)'."\n" if ($ENV{'form.handgrade'} eq 'yes'); + $request->print($result); } - $request->print($result); my %seen = (); my @partlist; + my @gradePartRespid; for (sort keys(%$handgrade)) { my ($partid,$respid) = split(/_/); next if ($seen{$partid} > 0); $seen{$partid}++; next if ($$handgrade{$_} =~ /:no$/ && $ENV{'form.lastSub'} =~ /^(hdgrade)$/); push @partlist,$partid; + push @gradePartRespid,$partid.'.'.$respid; $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record)); } $result=''."\n"; + $result.=''."\n" if ($counter == 0); my $ctr = 0; while ($ctr < scalar(@partlist)) { $result.=''."\n"; -# ''."\n"; $endform.='  '."\n"; @@ -1514,11 +1577,11 @@ KEYWORDS my $nsel = ($ENV{'form.NTSTU'} ne '' ? $ENV{'form.NTSTU'} : '1'); $ntstu =~ s/
'."\n". ''."\n". + ''."\n". ''."\n". ''."\n". ''."\n". ''."\n". ''."\n". + ''."\n". ''."\n"; my $checkIcon = 'No recorded submission for this problem
'; @@ -2837,7 +2912,7 @@ sub displayPage { foreach my $partid (@{$parts}) { $responseType{$partid} = $curRes->responseType($partid); } - $studentTable.= &displaySubByDates(\%record,$parts,\%responseType,$checkIcon); + $studentTable.= &displaySubByDates(\$symbx,\%record,$parts,\%responseType,$checkIcon); } } elsif ($ENV{'form.lastSub'} eq 'all') { my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : ''); @@ -2862,7 +2937,7 @@ sub displayPage { $navmap->untieHashes(); $studentTable.=''."\n". - '  '. '
'."\n"; $studentTable.=&show_grading_menu_form($symb,$url); @@ -2872,7 +2947,7 @@ sub displayPage { } sub displaySubByDates { - my ($record,$parts,$responseType,$checkIcon) = @_; + my ($symbx,$record,$parts,$responseType,$checkIcon) = @_; my $studentTable='
'. ''. ''. @@ -2881,6 +2956,8 @@ sub displaySubByDates { my ($version); my %mark; $mark{'correct_by_student'} = $checkIcon; + return '
 Nothing submitted - no attempts
' + if (!exists($$record{'1:timestamp'})); for ($version=1;$version<=$$record{'version'};$version++) { my $timestamp = scalar(localtime($$record{$version.':timestamp'})); $studentTable.=''; @@ -2888,22 +2965,21 @@ sub displaySubByDates { my @displaySub = (); foreach my $partid (@{$parts}) { my @matchKey = grep /^resource\.$partid\..*?\.submission$/,@versionKeys; - next if ($$record{"$version:resource.$partid.solved"} eq ''); +# next if ($$record{"$version:resource.$partid.solved"} eq ''); $displaySub[0].=(exists $$record{$version.':'.$matchKey[0]}) ? 'Part '.$partid.' '. ($$record{"$version:resource.$partid.tries"} eq '' ? 'Trial not counted' : 'Trial '.$$record{"$version:resource.$partid.tries"}).'  '. - &cleanRecord($$record{$version.':'.$matchKey[0]},$$responseType{$partid}).'
' : ''; + &cleanRecord($$record{$version.':'.$matchKey[0]},$$responseType{$partid},$$symbx).'
' : ''; $displaySub[1].=(exists $$record{"$version:resource.$partid.award"}) ? 'Part '.$partid.'  '. lc($$record{"$version:resource.$partid.award"}).' '. $mark{$$record{"$version:resource.$partid.solved"}}.'
' : ''; -# $$record{"$version:resource.$partid.solved"}.'
' : ''; $displaySub[2].=(exists $$record{"$version:resource.$partid.regrader"}) ? $$record{"$version:resource.$partid.regrader"}.' (Part: '.$partid.')' : ''; } $displaySub[2].=(exists $$record{"$version:resource.regrader"}) ? - $$record{"$version:resource.regrader"} : ''; + $$record{"$version:resource.regrader"} : ''; # needed because old essay regrader has not parts info $studentTable.=''; } @@ -2927,8 +3003,8 @@ sub updateGradeByPage { return; } my $result='

 '.$ENV{'form.title'}.'

'; - $result.='

 Student: '.$$fullname{$ENV{'form.student'}}. - ' ('.$uname.($udom eq $cdom ? '':':'.$udom).')

'."\n"; + $result.='

 Student: '.&nameUserString(undef,$ENV{'form.fullname'},$uname,$udom). + '

'."\n"; $request->print($result); @@ -2942,7 +3018,7 @@ sub updateGradeByPage { my $studentTable='
Date/Time
'.$timestamp.''.$displaySub[0].' '.$displaySub[1]. ($displaySub[2] eq '' ? '' : 'Manually graded by '.$displaySub[2]).' 
'. ''. - ''. + ''. ''. ''. ''; @@ -2974,31 +3050,39 @@ sub updateGradeByPage { my $score; if ($partial > 0) { $score = 'correct_by_override'; - } elsif ($partial == 0) { + } elsif ($newpts ne '') { #empty is taken as 0 $score = 'incorrect_by_override'; } - if ($ENV{'form.GD_SEL'.$question.'_'.$partid} eq 'excused') { + my $dropMenu = $ENV{'form.GD_SEL'.$question.'_'.$partid}; + if ($dropMenu eq 'excused') { $partial = ''; $score = 'excused'; + } elsif ($dropMenu eq 'reset status' + && $ENV{'form.solved'.$question.'_'.$partid} ne '') { #update only if previous record exists + $newrecord{'resource.'.$partid.'.tries'} = 0; + $newrecord{'resource.'.$partid.'.solved'} = ''; + $newrecord{'resource.'.$partid.'.award'} = ''; + $newrecord{'resource.'.$partid.'.awarded'} = 0; + $newrecord{'resource.'.$partid.'.regrader'} = "$ENV{'user.name'}:$ENV{'user.domain'}"; + $changeflag++; + $newpts = ''; } + my $oldstatus = $ENV{'form.solved'.$question.'_'.$partid}; $displayPts[0].=' Part '.$partid.' = '. (($oldstatus eq 'excused') ? 'excused' : $oldpts). ' 
'; $displayPts[1].=' Part '.$partid.' = '. - ($oldstatus eq 'correct_by_student' ? $oldpts : - (($score eq 'excused') ? 'excused' : $newpts)). + (($score eq 'excused') ? 'excused' : $newpts). ' 
'; $question++; - if (($oldstatus eq 'correct_by_student') || - ($newpts eq $oldpts && $score eq $oldstatus)) - { - next; - } + next if ($dropMenu eq 'reset status' || ($newpts == $oldpts && $score ne 'excused')); + $newrecord{'resource.'.$partid.'.awarded'} = $partial if $partial ne ''; - $newrecord{'resource.'.$partid.'.solved'} = $score; - $newrecord{'resource.'.$partid.'.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}"; + $newrecord{'resource.'.$partid.'.solved'} = $score if $score ne ''; + $newrecord{'resource.'.$partid.'.regrader'} = "$ENV{'user.name'}:$ENV{'user.domain'}" + if (scalar(keys(%newrecord)) > 0); $changeflag++; } @@ -3006,6 +3090,7 @@ sub updateGradeByPage { &Apache::lonnet::cstore(\%newrecord,$symbx,$ENV{'request.course.id'}, $udom,$uname); } + $studentTable.=''. ''. ''; @@ -3099,7 +3184,7 @@ sub scantron_selectphase { my $result; $result.= < - + $default_form_data
 No  Prob.  Title  Previous Score  New Score 
'.$displayPts[0].''.$displayPts[1].'
@@ -3129,7 +3214,7 @@ sub scantron_selectphase {
- + $grading_menu_button SCANTRONFORM @@ -3211,14 +3296,23 @@ sub scantron_parse_scanline { } sub scantron_add_delay { + my ($delayqueue,$scanline,$errormessage,$errorcode)=@_; + Apache->request->print('add_delay_error '.$_[2] ); + push(@$delayqueue, + {'line' => $scanline, 'emsg' => $errormessage, + 'ecode' => $errorcode } + ); } sub scantron_find_student { my ($scantron_record,$idmap)=@_; my $scanID=$$scantron_record{'scantron.ID'}; foreach my $id (keys(%$idmap)) { - Apache->request->print('
checking studnet -'.$id.'- againt -'.$scanID.'- 
'); - if (lc($id) eq lc($scanID)) { Apache->request->print('success');return $$idmap{$id}; } + #Apache->request->print('
checking studnet -'.$id.'- againt -'.$scanID.'- 
'); + if (lc($id) eq lc($scanID)) { + #Apache->request->print('success'); + return $$idmap{$id}; + } } return undef; } @@ -3231,6 +3325,18 @@ sub scantron_filter { return 0; } +#FIXME I think I am doing this in the wrong order, I think it would be +#better to make a several passes analyzing all of the lines in the +#file for common errors wrong/invalid PID/username duplicated +#PID/username, missing bubbles, double bubbles, missing/invalid CODE +#and then get the instructor to fix all of these errors, then grade +#the corrected one, I'll still need to catch error conditions, but +#maybe most will taken care even before we start + +sub scantron_validate_file { + my ($r) = @_; +} + sub scantron_process_students { my ($r) = @_; my (undef,undef,$sequence)=split(/___/,$ENV{'form.selectpage'}); @@ -3246,7 +3352,7 @@ sub scantron_process_students { my $navmap=Apache::lonnavmaps::navmap->new($ENV{'request.course.fn'}.'.db',$ENV{'request.course.fn'}.'_parms.db',1, 1); my $map=$navmap->getResourceByUrl($sequence); my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0); - $r->print("geto ".scalar(@resources)."
"); +# $r->print("geto ".scalar(@resources)."
"); my $result= < @@ -3255,29 +3361,36 @@ SCANTRONFORM $r->print($result); my @delayqueue; - my $totalcorrect; - my $totalincorrect; - + my %completedstudents; + my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r, 'Scantron Status','Scantron Progress',scalar(@scanlines)); + &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state, + 'Processing first student'); + my $start=&Time::HiRes::time(); foreach my $line (@scanlines) { - my $studentcorrect; - my $studentincorrect; + $r->print('
line is'.$line.'
'); chomp($line); my $scan_record=&scantron_parse_scanline($line,\%scantron_config); my ($uname,$udom); - if ($uname=&scantron_find_student($scan_record,\%idmap)) { + unless ($uname=&scantron_find_student($scan_record,\%idmap)) { &scantron_add_delay(\@delayqueue,$line, - 'Unable to find a student that matches'); + 'Unable to find a student that matches',1); + next; + } + if (exists $completedstudents{$uname}) { + &scantron_add_delay(\@delayqueue,$line, + 'Student '.$uname.' has multiple sheets',2); + next; } $r->print('
doing studnet'.$uname.'
'); ($uname,$udom)=split(/:/,$uname); &Apache::lonnet::delenv('form.counter'); &Apache::lonnet::appenv(%$scan_record); # &Apache::lonhomework::showhash(%ENV); - $Apache::lonxml::debug=1; - &Apache::lonxml::debug("line is $line"); +# $Apache::lonxml::debug=1; +# &Apache::lonxml::debug("line is $line"); my $i=0; foreach my $resource (@resources) { @@ -3289,31 +3402,31 @@ SCANTRONFORM 'grade_domain' =>$udom, 'grade_courseid'=>$ENV{'request.course.id'}, 'grade_symb' =>$resource->symb())); - my %score=&Apache::lonnet::restore($resource->symb(), - $ENV{'request.course.id'}, - $udom,$uname); - foreach my $part ($resource->{PARTS}) { - if ($score{'resource.'.$part.'.solved'} =~ /^correct/) { - $studentcorrect++; - $totalcorrect++; - } else { - $studentincorrect++; - $totalincorrect++; - } - } - $r->print('
'.
-		      $resource->symb().'-'.
-		      $resource->src().'-'.'
result is'.$result); - &Apache::lonhomework::showhash(%score); +# my %score=&Apache::lonnet::restore($resource->symb(), +# $ENV{'request.course.id'}, +# $udom,$uname); +# foreach my $part ($resource->{PARTS}) { +# if ($score{'resource.'.$part.'.solved'} =~ /^correct/) { +# $studentcorrect++; +# $totalcorrect++; +# } else { +# $studentincorrect++; +# $totalincorrect++; +# } +# } +# $r->print('
'.
+#		      $resource->symb().'-'.
+#		      $resource->src().'-'.'
result is'.$result); +# &Apache::lonhomework::showhash(%score); # if ($i eq 3) {last;} } + $completedstudents{$uname}={'line'=>$line}; + } continue { &Apache::lonnet::delenv('form.counter'); &Apache::lonnet::delenv('scantron\.'); &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state, - 'last student Who got a '.$studentcorrect.' correct and '. - $studentincorrect.' incorrect. The class has gotten '. - $totalcorrect.' correct and '.$totalincorrect.' incorrect'); - last; + 'last student'); + #last; #FIXME #get iterator for $sequence #foreach question 'submit' the students answer to the server @@ -3321,7 +3434,11 @@ SCANTRONFORM # generate data to pass back that includes grade recevied #} } - $Apache::lonxml::debug=0; + &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); + my $lasttime = &Time::HiRes::time()-$start; + $r->print("

took $lasttime

"); + + #$Apache::lonxml::debug=0; foreach my $delay (@delayqueue) { #FIXME #print out each delayed student with interface to select how @@ -3347,7 +3464,7 @@ SCANTRONFORM #--- Show a Grading Menu button - Calls the next routine --- sub show_grading_menu_form { my ($symb,$url)=@_; - my $result.='
'."\n". + my $result.='
'."\n". ''."\n". ''."\n". ''."\n". @@ -3413,7 +3530,7 @@ sub gradingmenu { GRADINGMENUJS &commonJSfunctions($request); my $result='

 Manual Grading/View Submission

'; - my ($table,$resptype,$hdgrade) = &showResourceInfo($url,$probTitle); + my ($table,undef,$hdgrade) = &showResourceInfo($url,$probTitle); $result.=$table; my (undef,$sections) = &getclasslist('all','0'); my $savedState = &savedState(); @@ -3425,11 +3542,11 @@ GRADINGMENUJS $result.=''."\n". ''."\n". ''."\n". - ''."\n". ''."\n". ''."\n". ''."\n". ''."\n". + ''."\n". ''."\n"; $result.=''."\n"; $result.='
'."\n". @@ -3474,7 +3591,7 @@ GRADINGMENUJS 'The complete set/page/sequence: For one student

'. - ''. + ''. '
'."\n"; $result.='
'; @@ -3604,6 +3721,8 @@ sub handler { } } elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) { $request->print(&scantron_selectphase($request)); + } elsif ($command eq 'scantron_validate' && $perm{'mgr'}) { + $request->print(&scantron_validate_file($request)); } elsif ($command eq 'scantron_process' && $perm{'mgr'}) { $request->print(&scantron_process_students($request)); } elsif ($command) {