--- loncom/homework/grades.pm 2003/08/18 18:24:32 1.134 +++ loncom/homework/grades.pm 2003/11/07 08:56:52 1.148 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.134 2003/08/18 18:24:32 www Exp $ +# $Id: grades.pm,v 1.148 2003/11/07 08:56:52 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -55,17 +55,35 @@ my %perm=(); # ----- These first few routines are general use routines.---- # -# --- Retrieve the parts that matches stores_\d+ from the metadata file.--- +# --- Retrieve the parts from the metadata file.--- sub getpartlist { - my ($url) = @_; - my @parts =(); - my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys')); - foreach my $key (@metakeys) { - if ( $key =~ m/stores_(\w+)_.*/) { - push(@parts,$key); + my ($url,$symb) = @_; + my $partorder = &Apache::lonnet::metadata($url, 'partorder'); + my @parts; + if ($partorder) { + for my $part (split (/,/,$partorder)) { + if (!&Apache::loncommon::check_if_partid_hidden($part,$symb)) { + push(@parts, $part); + } + } + } else { + my $metadata = &Apache::lonnet::metadata($url, 'packages'); + foreach (split(/\,/,$metadata)) { + if ($_ =~ /^part_(.*)$/) { + if (!&Apache::loncommon::check_if_partid_hidden($1,$symb)) { + push(@parts, $1); + } + } } } - return @parts; + my @stores; + foreach my $part (@parts) { + my (@metakeys) = split(/,/,&Apache::lonnet::metadata($url,'keys')); + foreach my $key (@metakeys) { + if ($key =~ m/^stores_\Q$part\E_/) { push(@stores,$key); } + } + } + return @stores; } # --- Get the symbolic name of a problem and the url @@ -115,20 +133,25 @@ sub response_type { $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); + my (@partlist,%handgrade,%responseType); foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) { - if (/^\w+response_\w+.*/) { + if (/^\w+response_.*/) { my ($responsetype,$part) = split(/_/,$_,2); my ($partid,$respid) = split(/_/,$part); + if (&Apache::loncommon::check_if_partid_hidden($partid,$symb)) { + next; + } $responsetype =~ s/response$//; # make it compatible w/ navmaps - should move to that!! my ($value) = &Apache::lonnet::EXT('resource.'.$part.'.handgrade',$symb); - $handgrade{$part} = $responsetype.':'.($value eq 'yes' ? 'yes' : 'no'); + $handgrade{$part} = ($value eq 'yes' ? 'yes' : 'no'); + if (!exists($responseType{$partid})) { $responseType{$partid}={}; } + $responseType{$partid}->{$respid}=$responsetype; next if ($seen{$partid} > 0); $seen{$partid}++; push @partlist,$partid; } } - return \@partlist,\%handgrade; + return \@partlist,\%handgrade,\%responseType; } #--- Show resource title @@ -137,42 +160,105 @@ sub showResourceInfo { my ($url,$probTitle) = @_; my $result =''. ''."\n"; - my ($partlist,$handgrade) = &response_type($url); + my ($partlist,$handgrade,$responseType) = &response_type($url); my %resptype = (); my $hdgrade='no'; - for (sort keys(%$handgrade)) { - my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_}); - my $partID = (split(/_/))[0]; - $resptype{$partID} = $responsetype; + for my $part_resID (sort keys(%$handgrade)) { + my $handgrade=$$handgrade{$part_resID}; + my ($partID,$resID) = split(/_/,$part_resID); + my $responsetype = $responseType->{$partID}->{$resID}; $hdgrade = $handgrade if ($handgrade eq 'yes'); - $result.=''. + $result.=''. ''; # ''; } $result.='
Current Resource: '.$probTitle.'
Part '.$partID.'
Part '.$partID.' '. + $resID.'Type: '.$responsetype.'
Handgrade: '.$handgrade.'
'."\n"; - return $result,\%resptype,$hdgrade,$partlist,$handgrade; + return $result,$responseType,$hdgrade,$partlist,$handgrade; } + +sub get_order { + my ($partid,$respid,$symb,$uname,$udom)=@_; + my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb); + $url=&Apache::lonnet::clutter($url); + my $subresult=&Apache::lonnet::ssi($url, + ('grade_target' => 'analyze'), + ('grade_domain' => $udom), + ('grade_symb' => $symb), + ('grade_courseid' => + $ENV{'request.course.id'}), + ('grade_username' => $uname)); + (my $debug,$subresult)=split(/_HASH_REF__/,$subresult,2); + Apache->request->print($debug); + my %analyze=&Apache::lonnet::str2hash($subresult); + return ($analyze{"$partid.$respid.shown"}); +} #--- Clean response type for display -#--- Currently filters option response type only. +#--- Currently filters option/rank/radiobutton/match/essay response types only. sub cleanRecord { - my ($answer,$response,$symb) = @_; - if ($response eq 'option') { - my (@IDs,@ans); - foreach (split(/\&/,&Apache::lonnet::unescape($answer))) { - my ($optionID,$ans) = split(/=/); - push @IDs,$optionID.''; - push @ans,$ans; + my ($answer,$response,$symb,$partid,$respid,$record,$order,$version) = @_; + my $grayFont = ''; + if ($response =~ /^(option|rank)$/) { + my %answer=&Apache::lonnet::str2hash($answer); + my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"}); + my ($toprow,$bottomrow); + foreach my $foil (@$order) { + if ($grading{$foil} == 1) { + $toprow.=''.$answer{$foil}.' '; + } else { + $toprow.=''.$answer{$foil}.' '; + } + $bottomrow.=''.$grayFont.$foil.' '; } - my $grayFont = ''; return '
'. - ''. - ''. - '
Answer'. - (join '',@ans).'
'.$grayFont.'Option ID'.$grayFont. - (join ''.$grayFont,@IDs).'
'; - } - if ($response eq 'essay') { + 'Answer'.$toprow.''. + ''.$grayFont.'Option ID
'. + $grayFont.$bottomrow.''.''; + } elsif ($response eq 'match') { + my %answer=&Apache::lonnet::str2hash($answer); + my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"}); + my @items=&Apache::lonnet::str2array($record->{$version."resource.$partid.$respid.submissionitems"}); + my ($toprow,$middlerow,$bottomrow); + foreach my $foil (@$order) { + my $item=shift(@items); + if ($grading{$foil} == 1) { + $toprow.=''.$item.' '; + $middlerow.=''.$grayFont.$answer{$foil}.' '; + } else { + $toprow.=''.$item.' '; + $middlerow.=''.$grayFont.$answer{$foil}.' '; + } + $bottomrow.=''.$grayFont.$foil.' '; + } + return '
'. + ''.$toprow.''. + ''. + $middlerow.''. + ''. + $bottomrow.''.'
Answer
'.$grayFont.'Item ID
'.$grayFont.'Option ID
'; + } elsif ($response eq 'radiobutton') { + my %answer=&Apache::lonnet::str2hash($answer); + my ($toprow,$bottomrow); + my $correct=($order->[0])+1; + for (my $i=1;$i<=$#$order;$i++) { + my $foil=$order->[$i]; + if (exists($answer{$foil})) { + if ($i == $correct) { + $toprow.='true'; + } else { + $toprow.='true'; + } + } else { + $toprow.='false'; + } + $bottomrow.=''.$grayFont.$foil.' '; + } + return '
'. + ''.$toprow.''. + ''. + $grayFont.$bottomrow.''.'
Answer
'.$grayFont.'Option ID
'; + } elsif ($response eq 'essay') { if (! exists ($ENV{'form.'.$symb})) { my (%keyhash) = &Apache::lonnet::dump('nohist_handgrade', $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}, @@ -185,7 +271,7 @@ sub cleanRecord { $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 '

'.&keywords_highlight($answer).'
'; } return $answer; } @@ -218,7 +304,8 @@ sub commonJSfunctions { } } } else { - if (selectOne.selected) return selectOne.value; + // only one value it must be the selected one + return selectOne.value; } } @@ -506,9 +593,12 @@ LISTJAVASCRIPT my $checkhdgrade = ($ENV{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1 ) ? 'checked' : ''; my $checklastsub = $checkhdgrade eq '' ? 'checked' : ''; my $gradeTable='
'."\n". - ' View Problem Text: no '."\n". + ' View Problem Text: no '."\n". ' one student '."\n". ' all students
'."\n". + ' View Answer: no '."\n". + ' one student '."\n". + ' all students
'."\n". ' Submissions: '."\n"; if ($ENV{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) { $gradeTable.=' essay part only'."\n"; @@ -517,8 +607,8 @@ LISTJAVASCRIPT my $saveStatus = $ENV{'form.Status'} eq '' ? 'Active' : $ENV{'form.Status'}; $ENV{'form.Status'} = $saveStatus; - $gradeTable.=' last sub only'."\n". - ' last sub & parts info'."\n". + $gradeTable.=' last submission only'."\n". + ' last submission & parts info'."\n". ' by dates and submissions'."\n". ' all details'."\n". ''."\n". @@ -568,18 +658,21 @@ LISTJAVASCRIPT my %status = (); if ($ENV{'form.showgrading'} eq 'yes' && $submitonly ne 'all') { (%status) =&student_gradeStatus($url,$symb,$udom,$uname,$partlist); - my $statusflg = ''; + my $submitted = 0; + my $graded = 1; foreach (keys(%status)) { - $statusflg = 1 if ($status{$_} ne 'nothing'); + $submitted = 1 if ($status{$_} ne 'nothing'); + $graded = 0 if ($status{$_} =~ /^correct/); my ($foo,$partid,$foo1) = split(/\./,$_); if ($status{'resource.'.$partid.'.submitted_by'} ne '') { - $statusflg = ''; + $submitted = 0; $gradeTable.=''; } } - next if ($statusflg eq '' && $submitonly eq 'yes'); + next if (!$submitted && ($submitonly eq 'yes' || $submitonly eq 'graded')); + next if (!$graded && $submitonly eq 'graded'); } $ctr++; @@ -621,7 +714,7 @@ LISTJAVASCRIPT } else { $gradeTable='
 '. 'No submissions found for this resource for any students. ('.$num_students. - ' checked for submissions
'; + ' checked for submissions)
'; } } elsif ($ctr == 1) { $gradeTable =~ s/type=checkbox/type=checkbox checked/; @@ -1195,27 +1288,46 @@ sub gradeBox { } sub show_problem { - my ($request,$symb,$uname,$udom,$removeform,$viewon) = @_; - my $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom, - $ENV{'request.course.id'}); + my ($request,$symb,$uname,$udom,$removeform,$viewon,$mode) = @_; + my $rendered; + if ($mode eq 'both' or $mode eq 'text') { + $rendered=&Apache::loncommon::get_student_view($symb,$uname,$udom, + $ENV{'request.course.id'}); + } if ($removeform) { $rendered=~s|||g; $rendered=~s|||g; $rendered=~s|name="submit"|name="would_have_been_submit"|g; } - my $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom, - $ENV{'request.course.id'}); + my $companswer; + if ($mode eq 'both' or $mode eq 'answer') { + $companswer=&Apache::loncommon::get_student_answers($symb,$uname,$udom, + $ENV{'request.course.id'}); + } if ($removeform) { $companswer=~s|||g; $companswer=~s|||g; - $rendered=~s|name="submit"|name="would_have_been_submit"|g; + $companswer=~s|name="submit"|name="would_have_been_submit"|g; } my $result.='
'; $result.=''; - $result.='' if ($viewon); - $result.=''; + } + if ($mode eq 'both') { + $result.='
View of the problem - '.$ENV{'form.fullname'}. - '
'.$rendered.'
'; - $result.='Correct answer:
'.$companswer; + if ($viewon) { + $result.='
'; + if ($mode eq 'both' or $mode eq 'text') { + $result.='View of the problem - '; + } else { + $result.='Correct answer: '; + } + $result.=$ENV{'form.fullname'}.'
'.$rendered.'
'; + $result.='Correct answer:
'.$companswer; + } elsif ($mode eq 'text') { + $result.='
'.$rendered; + } elsif ($mode eq 'answer') { + $result.='
'.$companswer; + } $result.='
'; $result.='

'; return $result; @@ -1264,8 +1376,16 @@ sub submission { # option to display problem, only once else it cause problems # with the form later since the problem has a form. - if ($ENV{'form.vProb'} eq 'yes' or !$ENV{'form.vProb'}) { - $request->print(&show_problem($request,$symb,$uname,$udom,0,1)); + if ($ENV{'form.vProb'} eq 'yes' or $ENV{'form.vAns'} eq 'yes') { + my $mode; + if ($ENV{'form.vProb'} eq 'yes' && $ENV{'form.vAns'} eq 'yes') { + $mode='both'; + } elsif ($ENV{'form.vProb'} eq 'yes') { + $mode='text'; + } elsif ($ENV{'form.vAns'} eq 'yes') { + $mode='answer'; + } + $request->print(&show_problem($request,$symb,$uname,$udom,0,1,$mode)); } # kwclr is the only variable that is guaranteed to be non blank @@ -1300,6 +1420,7 @@ sub submission { ''."\n". ''."\n". ''."\n". + ''."\n". ''."\n". ''."\n". ''."\n". @@ -1350,13 +1471,22 @@ KEYWORDS } } - if ($ENV{'form.vProb'} eq 'all') { + if ($ENV{'form.vProb'} eq 'all' or $ENV{'form.vAns'} eq 'all') { $request->print('


') if ($counter > 0); - $request->print(&show_problem($request,$symb,$uname,$udom,1,1)); + my $mode; + if ($ENV{'form.vProb'} eq 'all' && $ENV{'form.vAns'} eq 'all') { + $mode='both'; + } elsif ($ENV{'form.vProb'} eq 'all' ) { + $mode='text'; + } elsif ($ENV{'form.vAns'} eq 'all') { + $mode='answer'; + } + $request->print(&show_problem($request,$symb,$uname,$udom,1,1,$mode)); } + my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname); - my ($partlist,$handgrade) = &response_type($url,$symb); + my ($partlist,$handgrade,$responseType) = &response_type($url,$symb); # Display student info $request->print(($counter == 0 ? '' : '
')); @@ -1439,7 +1569,7 @@ KEYWORDS $$fullname{$ENV{'form.'.$uname.':'.$udom.':submitted_by'}}.''; $request->print($submitby); } else { - my ($string,$timestamp)= &get_last_submission (\%record); + my ($string,$timestamp)= &get_last_submission(\%record); my $lastsubonly=''. ($$timestamp eq '' ? '' : 'Date Submitted: '. $$timestamp)."\n"; @@ -1447,8 +1577,8 @@ KEYWORDS $lastsubonly.=''.$$string[0]; } else { for my $part (sort keys(%$handgrade)) { - my ($responsetype,$foo) = split(/:/,$$handgrade{$part}); my ($partid,$respid) = split(/_/,$part); + my $responsetype = $responseType->{$partid}->{$respid}; if (!exists($record{'resource.'.$partid.'.'.$respid.'.submission'})) { $lastsubonly.='Part '. $partid.' ( ID '.$respid. @@ -1456,7 +1586,7 @@ KEYWORDS 'Nothing submitted - no attempts

'; } else { foreach (@$string) { - my ($partid,$respid) = /^resource\.(\w+)\.(\w+)\.submission/; + my ($partid,$respid) = /^resource\.([^\.]*)\.([^\.]*)\.submission/; if ($part eq ($partid.'_'.$respid)) { my ($ressub,$subval) = split(/:/,$_,2); # Similarity check @@ -1476,6 +1606,7 @@ KEYWORDS &keywords_highlight($oessay).'
'; } } + my $order=&get_order($partid,$respid,$symb,$uname,$udom); $lastsubonly.='Part '. $partid.' ( ID '.$respid. ' )   '. @@ -1486,7 +1617,7 @@ KEYWORDS 'Like all files provided by users, '. 'this file may contain virusses
':''). 'Submitted Answer: '. - &cleanRecord($subval,$responsetype,$symb). + &cleanRecord($subval,$responsetype,$symb,$partid,$respid,\%record,$order). '

'.$similar."\n" if ($ENV{'form.lastSub'} eq 'lastonly' || ($ENV{'form.lastSub'} eq 'hdgrade' && @@ -1501,7 +1632,7 @@ KEYWORDS } } elsif ($ENV{'form.lastSub'} eq 'datesub') { my (undef,$responseType,undef,$parts) = &showResourceInfo($url); - $request->print(&displaySubByDates(\$symb,\%record,$parts,$responseType,$checkIcon)); + $request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom)); } elsif ($ENV{'form.lastSub'} =~ /^(last|all)$/) { $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom, $ENV{'request.course.id'}, @@ -1789,15 +1920,25 @@ sub processHandGrade { } $ctr = 0; @parsedlist = reverse @parsedlist if ($button eq 'Previous'); + my ($partlist) = &response_type($url); foreach my $student (@parsedlist) { + my $submitonly=$ENV{'form.submitonly'}; my ($uname,$udom) = split(/:/,$student); - if ($ENV{'form.submitonly'} eq 'yes') { - my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname); - my $statusflg = ''; - foreach (split(/:/,$ENV{'form.gradePartRespid'})){ - $statusflg = 1 if (exists ($record{'resource.'.$_.'.submission'})); + if ($submitonly =~ /^(yes|graded)$/) { +# my %record = &Apache::lonnet::restore($symb,$ENV{'request.course.id'},$udom,$uname); + my %status=&student_gradeStatus($url,$symb,$udom,$uname,$partlist); + my $submitted = 0; + my $graded = 1; + foreach (keys(%status)) { + $submitted = 1 if ($status{$_} ne 'nothing'); + $graded = 0 if ($status{$_} =~ /^correct/); + my ($foo,$partid,$foo1) = split(/\./,$_); + if ($status{'resource.'.$partid.'.submitted_by'} ne '') { + $submitted = 0; + } } - next if ($statusflg eq ''); + next if (!$submitted && ($submitonly eq 'yes' || $submitonly eq 'graded')); + next if (!$graded && $submitonly eq 'graded'); } push @nextlist,$student if ($ctr < $ntstu); last if ($ctr == $ntstu); @@ -2097,7 +2238,7 @@ sub viewgrades { my ($partid,$respid) = split (/_/,$_,2); next if $seen{$partid}; $seen{$partid}++; - my ($responsetype,$handgrade)=split(/:/,$$handgrade{$_}); + my $handgrade=$$handgrade{$_}; my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb); $weight{$partid} = $wgt eq '' ? '1' : $wgt; @@ -2139,7 +2280,7 @@ sub viewgrades { $result.= '
'."\n". ''. '\n"; - my (@parts) = sort(&getpartlist($url)); + my (@parts) = sort(&getpartlist($url,$symb)); foreach my $part (@parts) { my $display=&Apache::lonnet::metadata($url,$part.'.display'); $display =~ s|^Number of Attempts|Tries
|; # makes the column narrower @@ -2256,7 +2397,7 @@ sub editgrades { my %columns = (); my ($i,$ctr,$count,$rec_update) = (0,0,0,0); - my (@parts) = sort(&getpartlist($url)); + my (@parts) = sort(&getpartlist($url,$symb)); my $header; while ($ctr < $ENV{'form.totalparts'}) { my $partid = $ENV{'form.partid_'.$ctr}; @@ -2334,18 +2475,17 @@ sub editgrades { $newrecord{'resource.'.$_.'.awarded'} = 0; $newrecord{'resource.'.$_.'.regrader'}="$ENV{'user.name'}:$ENV{'user.domain'}"; $updateflag = 1; + } elsif (!($old_part eq $partial && $old_score eq $score)) { + $updateflag = 1; + $newrecord{'resource.'.$_.'.awarded'} = $partial if $partial ne ''; + $newrecord{'resource.'.$_.'.solved'} = $score; + $rec_update++; } $line .= ''. ''; - if (!($old_part eq $partial && $old_score eq $score)) { - $updateflag = 1; - $newrecord{'resource.'.$_.'.awarded'} = $partial if $partial ne ''; - $newrecord{'resource.'.$_.'.solved'} = $score; - $rec_update++; - } my $partid=$_; foreach my $stores (@parts) { @@ -2522,8 +2662,8 @@ ENDPICK } sub csvupload_fields { - my ($url) = @_; - my (@parts) = &getpartlist($url); + my ($url,$symb) = @_; + my (@parts) = &getpartlist($url,$symb); my @fields=(['username','Student Username'],['domain','Student Domain']); foreach my $part (sort(@parts)) { my @datum; @@ -2606,7 +2746,7 @@ sub csvuploadmap { &csvuploadmap_header($request,$symb,$url,$datatoken,$#records+1); my ($i,$keyfields); if (@records) { - my @fields=&csvupload_fields($url); + my @fields=&csvupload_fields($url,$symb); if ($ENV{'form.upfile_associate'} eq 'reverse') { &Apache::loncommon::csv_print_samples($request,\@records); @@ -2733,7 +2873,9 @@ LISTJAVASCRIPT $result.=''."\n"; $result.=' Problems from:'."\n". ''."\n"; - $result.=' View Problems Text: no '."\n". + $result.=' View Problems Text: no '."\n". ' yes '."
\n"; $result.=' Submission Details: '. @@ -2851,7 +2993,7 @@ sub displayPage { $request->print($result); my $navmap = Apache::lonnavmaps::navmap->new(); - my ($mapUrl, $id, $resUrl) = split(/___/, $ENV{'form.page'}); + my ($mapUrl, $id, $resUrl)=&Apache::lonnet::decode_symb($ENV{'form.page'}); my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps my $iterator = $navmap->getIterator($map->map_start(), @@ -2892,8 +3034,9 @@ sub displayPage { $studentTable.=''; $studentTable.=''."\n"; + ($saveCmd eq 'submission' ? 'checked' : '').'> '.'Current Resource: For one or more students '. + ''."\n"; $result.='
 No. '.&nameUserString('header')."'.$old_aw.' '.$awarded. ($score eq 'excused' ? $score : '').' 
'.$question. (scalar(@{$parts}) == 1 ? '' : '
('.scalar(@{$parts}).' parts)').'
'; - if ($ENV{'form.vProb'} eq 'yes') { - $studentTable.=&show_problem($request,$symbx,$uname,$udom,1); + if ($ENV{'form.vProb'} eq 'yes' ) { + $studentTable.=&show_problem($request,$symbx,$uname,$udom,1, + undef,'both'); } else { my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$ENV{'request.course.id'}); $companswer =~ s|||g; @@ -2914,9 +3057,16 @@ sub displayPage { } else { my %responseType = (); foreach my $partid (@{$parts}) { - $responseType{$partid} = $curRes->responseType($partid); + my @responseIds =$curRes->responseIds($partid); + my @responseType =$curRes->responseType($partid); + my %responseIds; + for (my $i=0;$i<=$#responseIds;$i++) { + $responseIds{$responseIds[$i]}=$responseType[$i]; + } + $responseType{$partid} = \%responseIds; } - $studentTable.= &displaySubByDates(\$symbx,\%record,$parts,\%responseType,$checkIcon); + $studentTable.= &displaySubByDates($symbx,\%record,$parts,\%responseType,$checkIcon,$uname,$udom); + } } elsif ($ENV{'form.lastSub'} eq 'all') { my $last = ($ENV{'form.lastSub'} eq 'last' ? 'last' : ''); @@ -2951,7 +3101,7 @@ sub displayPage { } sub displaySubByDates { - my ($symbx,$record,$parts,$responseType,$checkIcon) = @_; + my ($symb,$record,$parts,$responseType,$checkIcon,$uname,$udom) = @_; my $studentTable='
'. ''. ''. @@ -2959,33 +3109,62 @@ sub displaySubByDates { ''; my ($version); my %mark; + my %orders; $mark{'correct_by_student'} = $checkIcon; - return '
 Nothing submitted - no attempts
' - if (!exists($$record{'1:timestamp'})); + if (!exists($$record{'1:timestamp'})) { + return '
 Nothing submitted - no attempts
'; + } for ($version=1;$version<=$$record{'version'};$version++) { my $timestamp = scalar(localtime($$record{$version.':timestamp'})); $studentTable.=''; my @versionKeys = split(/\:/,$$record{$version.':keys'}); my @displaySub = (); foreach my $partid (@{$parts}) { - my @matchKey = grep /^resource\.$partid\..*?\.submission$/,@versionKeys; + my @matchKey = sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys); # 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},$$symbx).'
' : ''; - $displaySub[1].=(exists $$record{"$version:resource.$partid.award"}) ? - 'Part '.$partid.'  '. - lc($$record{"$version:resource.$partid.award"}).' '. - $mark{$$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"} : ''; # needed because old essay regrader has not parts info - $studentTable.=''; + foreach my $matchKey (@matchKey) { + if (exists $$record{$version.':'.$matchKey}) { + my ($responseId)=($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/); + $displaySub[0].='Part '.$partid.' '; + $displaySub[0].='(ID '. + $responseId.') '; + if ($$record{"$version:resource.$partid.tries"} eq '') { + $displaySub[0].='Trial not counted'; + } else { + $displaySub[0].='Trial '. + $$record{"$version:resource.$partid.tries"}; + } + my $responseType=$responseType->{$partid}->{$responseId}; + if (!exists($orders{$partid})) { $orders{$partid}={}; } + if (!exists($orders{$partid}->{$responseId})) { + $orders{$partid}->{$responseId}= + &get_order($partid,$responseId,$symb,$uname,$udom); + } + $displaySub[0].='  '. + &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:").'
'; + } + } + if (exists $$record{"$version:resource.$partid.award"}) { + $displaySub[1].='Part '.$partid.'  '. + lc($$record{"$version:resource.$partid.award"}).' '. + $mark{$$record{"$version:resource.$partid.solved"}}. + '
'; + } + if (exists $$record{"$version:resource.$partid.regrader"}) { + $displaySub[2].=$$record{"$version:resource.$partid.regrader"}. + ' (Part: '.$partid.')'; + } + } + # needed because old essay regrader has not parts info + if (exists $$record{"$version:resource.regrader"}) { + $displaySub[2].=$$record{"$version:resource.regrader"}; + } + $studentTable.=''; + } $studentTable.='
Date/TimeStatus 
'.$timestamp.''.$displaySub[0].' '.$displaySub[1]. - ($displaySub[2] eq '' ? '' : 'Manually graded by '.$displaySub[2]).' 
'.$displaySub[0].' '.$displaySub[1]; + if ($displaySub[2]) { + $studentTable.='Manually graded by '.$displaySub[2]; + } + $studentTable.=' 
'; return $studentTable; @@ -3013,7 +3192,7 @@ sub updateGradeByPage { $request->print($result); my $navmap = Apache::lonnavmaps::navmap->new(); - my ($mapUrl, $id, $resUrl) = split(/___/, $ENV{'form.page'}); + my ($mapUrl, $id, $resUrl) = &Apache::lonnet::decode_symb( $ENV{'form.page'}); my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps my $iterator = $navmap->getIterator($map->map_start(), @@ -3135,7 +3314,7 @@ sub getSequenceDropDown { my ($request,$symb)=@_; my $result=' + $default_form_data @@ -3217,7 +3396,7 @@ sub scantron_selectphase {
- + $grading_menu_button SCANTRONFORM @@ -3299,14 +3478,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; } @@ -3319,9 +3507,21 @@ 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'}); + my (undef,undef,$sequence)=&Apache::lonnet::decode_symb($ENV{'form.selectpage'}); my ($symb,$url)=&get_symb_and_url($r); if (!$symb) {return '';} my $default_form_data=&defaultFormData($symb,$url); @@ -3334,7 +3534,7 @@ sub scantron_process_students { my $navmap=Apache::lonnavmaps::navmap->new(); 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= < @@ -3343,29 +3543,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) { @@ -3377,31 +3584,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 @@ -3409,7 +3616,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 @@ -3476,7 +3687,7 @@ sub gradingmenu { } formname.command.value = cmd; formname.saveState.value = "saveCmd="+cmdsave+":saveSec="+pullDownSelection(formname.section)+ - ":saveSub="+radioSelection(formname.submitonly)+":saveStatus="+pullDownSelection(formname.Status); + ":saveSub="+pullDownSelection(formname.submitonly)+":saveStatus="+pullDownSelection(formname.Status); if (val < 5) formname.submit(); if (val == 5) { if (!checkReceiptNo(formname,'notOK')) { return false;} @@ -3544,12 +3755,14 @@ GRADINGMENUJS $result.='
'. ' '.'Current Resource: For one or more students'. - '
            -->For students with '. - ' submissions or '. - ' for all
'. 'content_type('text/xml'); + &Apache::loncommon::content_type($request,'text/xml'); } else { - $request->content_type('text/html'); + &Apache::loncommon::content_type($request,'text/html'); } $request->send_http_header; return '' if $request->header_only; @@ -3607,7 +3820,7 @@ sub handler { my $command=$ENV{'form.command'}; if (!$url) { my ($temp1,$temp2); - ($temp1,$temp2,$ENV{'form.url'})=split(/___/,$symb); + ($temp1,$temp2,$ENV{'form.url'})=&Apache::lonnet::decode_symb($symb); $url = $ENV{'form.url'}; } &send_header($request); @@ -3620,7 +3833,7 @@ sub handler { my ($tsymb,$tuname,$tudom,$tcrsid)= &Apache::lonnet::checkin($token); if ($tsymb) { - my ($map,$id,$url)=split(/\_\_\_/,$tsymb); + my ($map,$id,$url)=&Apache::lonnet::decode_symb($tsymb); if (&Apache::lonnet::allowed('mgr',$tcrsid)) { $request->print(&Apache::lonnet::ssi_body('/res/'.$url, ('grade_username' => $tuname, @@ -3692,6 +3905,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) {