--- loncom/interface/lonquickgrades.pm 2011/05/21 01:02:27 1.86 +++ loncom/interface/lonquickgrades.pm 2011/06/01 21:32:50 1.97 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Quick Student Grades Display # -# $Id: lonquickgrades.pm,v 1.86 2011/05/21 01:02:27 www Exp $ +# $Id: lonquickgrades.pm,v 1.97 2011/06/01 21:32:50 www Exp $ # # Copyright Michigan State University Board of Trustees # @@ -244,7 +244,9 @@ sub getData { for my $part (@{$parts}) { my $completionStatus = $curRes->getCompletionStatus($part); my $dateStatus = $curRes->getDateStatus($part); - + my $weight = $curRes->weight($part); + my $problemstatus = $curRes->problemstatus($part); + if ($completionStatus == $curRes->EXCUSED()) { next; } @@ -252,29 +254,29 @@ sub getData { my $score = 0; # If we're not telling status and the answer date isn't passed yet, # it's an "attempted" point - if ((($curRes->problemstatus($part) eq 'no') || - ($curRes->problemstatus($part) eq 'no_feedback_ever')) && + if ((($problemstatus eq 'no') || + ($problemstatus eq 'no_feedback_ever')) && ($dateStatus != $curRes->ANSWER_OPEN)) { my $status = $curRes->simpleStatus($part); if ($status == $curRes->ATTEMPTED) { - $partsAttempted += $curRes->weight($part); + $partsAttempted += $weight; $totalAttempted += $partsAttempted; } } else { - $score = &Apache::grades::compute_points($curRes->weight($part), $curRes->awarded($part)); + $score = &Apache::grades::compute_points($weight, $curRes->awarded($part)); } $partsRight += $score; $totalRight += $score; - $partsCount += $curRes->weight($part); + $partsCount += $weight; $curRes->{DATA}->{PROB_SCORE} += $score; - $curRes->{DATA}->{PROB_WEIGHT} += $curRes->weight($part); + $curRes->{DATA}->{PROB_WEIGHT} += $weight; if ($curRes->opendate($part) < $now) { - $totalPossible += $curRes->weight($part); - $curRes->{DATA}->{PROB_POSSIBLE} += $curRes->weight($part); + $totalPossible += $weight; + $curRes->{DATA}->{PROB_POSSIBLE} += $weight; } - $totalParts += $curRes->weight($part); + $totalParts += $weight; } else { my $status = $curRes->simpleStatus($part); my $thisright = 0; @@ -291,7 +293,6 @@ sub getData { $totalAttempted++; } - my $dateStatus = $curRes->getDateStatus($part); $totalParts++; if ($curRes->opendate($part) < $now) { $totalPossible++; @@ -478,6 +479,33 @@ sub outputCategories { # foreach (keys(%data)) { # $r->print("\n<br />".$_.'='.$data{$_}); # } +# my @debugarray=('5:1','4:3','1:1','5:5','6:7'); +# $r->print("Array: ".join(',',@debugarray).'<br />'); +# $r->print("0,0,0: ".join(',',&drop(0,0,0,@debugarray)).'<br />'); +# $r->print("1,0,0: ".join(',',&drop(1,0,0,@debugarray)).'<br />'); +# $r->print("0,1,0: ".join(',',&drop(0,1,0,@debugarray)).'<br />'); +# $r->print("1,1,0: ".join(',',&drop(1,1,0,@debugarray)).'<br />'); +# +# $r->print("0,0,2: ".join(',',&drop(0,0,2,@debugarray)).'<br />'); +# $r->print("1,0,2: ".join(',',&drop(1,0,2,@debugarray)).'<br />'); +# $r->print("0,1,2: ".join(',',&drop(0,1,2,@debugarray)).'<br />'); +# $r->print("1,1,2: ".join(',',&drop(1,1,2,@debugarray)).'<br />'); +# +# $r->print("0,0,4: ".join(',',&drop(0,0,4,@debugarray)).'<br />'); +# $r->print("1,0,4: ".join(',',&drop(1,0,4,@debugarray)).'<br />'); +# $r->print("0,1,4: ".join(',',&drop(0,1,4,@debugarray)).'<br />'); +# $r->print("1,1,4: ".join(',',&drop(1,1,4,@debugarray)).'<br />'); +# +# $r->print("0,0,5: ".join(',',&drop(0,0,5,@debugarray)).'<br />'); +# $r->print("1,0,5: ".join(',',&drop(1,0,5,@debugarray)).'<br />'); +# $r->print("0,1,5: ".join(',',&drop(0,1,5,@debugarray)).'<br />'); +# $r->print("1,1,5: ".join(',',&drop(1,1,5,@debugarray)).'<br />'); +# +# $r->print("0,0,7: ".join(',',&drop(0,0,7,@debugarray)).'<br />'); +# $r->print("1,0,7: ".join(',',&drop(1,0,7,@debugarray)).'<br />'); +# $r->print("0,1,7: ".join(',',&drop(0,1,7,@debugarray)).'<br />'); +# $r->print("1,1,7: ".join(',',&drop(1,1,7,@debugarray)).'<br />'); + } # @@ -539,7 +567,7 @@ sub process_category_edits { } elsif ($cmd=~/^delcat\_(.+)$/) { %categories=&del_category($1,$cangrade,%categories); } elsif ($cmd=~/^addcont\_(.+)$/) { - %categories=&add_category_content($1,$cangrade,$env{'form.addcont_'.$1.'_symb'},%categories); + %categories=&add_category_content($1,$cangrade,$env{'form.resourcesymb'},%categories); } elsif ($cmd=~/^delcont\_(.+)\_\_\_\_\_\_(.+)$/) { %categories=&del_category_content($1,$cangrade,$2,%categories); } elsif ($cmd=~/^newrule\_(.+)$/) { @@ -561,25 +589,32 @@ sub process_category_edits { sub output_category_table { my ($r,$cangrade,$navmaps,%categories)=@_; - my $sum=0; - my $total=0; + + my $totalweight=0; + my $totalpoints=0; + $r->print(&Apache::loncommon::start_data_table()); # &output_category_table_header($r,$cangrade); # my @order=split(/\,/,$categories{'order'}); # + my %performance=&dumpdata($navmaps); my $maxpos=$#order; for (my $i=0;$i<=$maxpos;$i++) { - my ($value,$weight)=&output_and_calc_category($r,$cangrade,$navmaps,$order[$i],$i,$maxpos,%categories); - $sum+=$value*$weight; - $total+=$weight; + my ($correct,$possible,$type,$weight)=&output_and_calc_category($r,$cangrade,$navmaps,$order[$i],$i,$maxpos,\%performance,1,%categories); + unless ($possible) { next; } + $totalpoints+=$weight*$correct/$possible; + $totalweight+=$weight; } # - &bottom_line_category($r,$cangrade,$sum,$total); + my $perc=0; + if ($totalweight) { $perc=100.*$totalpoints/$totalweight; } + + &bottom_line_category($r,$cangrade,$perc); # $r->print(&Apache::loncommon::end_data_table()); - return $sum; + return $perc; } sub output_category_table_header { @@ -603,16 +638,15 @@ sub output_category_table_header { # sub output_and_calc_category { - my ($r,$cangrade,$navmaps,$id,$currentpos,$maxpos,%categories)=@_; - my $value=0; - my $weight=0; + my ($r,$cangrade,$navmaps,$id,$currentpos,$maxpos,$performance,$output,%categories)=@_; + my $iconpath = &Apache::loncommon::lonhttpdurl($r->dir_config('lonIconsURL') . "/"); my %lt=&Apache::lonlocal::texthash( 'up' => 'Move Up', 'dw' => 'Move Down'); - $r->print("\n".&Apache::loncommon::start_data_table_row()); + if ($output) { $r->print("\n".&Apache::loncommon::start_data_table_row()); } - if ($cangrade) { + if ($output && $cangrade) { $r->print(<<ENDMOVE); <td> <div class="LC_docs_entry_move"> @@ -639,96 +673,212 @@ ENDMOVE $r->print('<td><a href="javascript:storecmd(\'delcat_'.$id.'\');">'.&mt('Delete').'</a></td>'); $r->print('<td><input type="text" name="name_'.$id. '" value="'.&Apache::lonhtmlcommon::entity_encode($categories{$id.'_name'}).'" /></td>'); - } else { + } elsif ($output) { $r->print('<td>'.$categories{$id.'_name'}.'</td>'); } -# Content - $r->print('<td><ul>'); +# Content display and summing up of points + my $totalpossible=0; + my $totalcorrect=0; + my @individual=(); + if ($output) { $r->print('<td><ul>'); } foreach my $contentid (split(/\,/,$categories{$id.'_content'})) { - $r->print('<li>'); - $r->print(&Apache::lonnet::gettitle($contentid)); - if ($cangrade) { - $r->print(' <a href="javascript:storecmd(\'delcont_'.$id.'______'.$contentid.'\');">'.&mt('Delete').'</a>'); - } - $r->print('</li>'); - } - $r->print('</ul>'); - if ($cangrade) { - $r->print('<br />'.&Apache::loncommon::selectresource_link('quickform','addcont_'.$id,&mt('Add Problem or Folder')).'<br />'); + my ($type,$possible,$attempted,$correct)=split(/\:/,$$performance{$contentid}); + $totalpossible+=$possible; + $totalcorrect+=$correct; + if ($possible>0) { push(@individual,"$possible:$correct"); } + if ($output) { + $r->print('<li>'); + $r->print(&Apache::lonnet::gettitle($contentid).' ('.&numberout($correct).'/'.&numberout($possible).')'); + if ($cangrade) { + $r->print(' <a href="javascript:storecmd(\'delcont_'.$id.'______'.$contentid.'\');">'.&mt('Delete').'</a>'); + } + $r->print('</li>'); + } + } + if ($output) { + $r->print('</ul>'); + if ($cangrade) { + $r->print('<br />'.&Apache::loncommon::selectresource_link('quickform','addcont_'.$id,&mt('Add Problem or Folder')).'<br />'); + } + $r->print('<p><b>'.&mt('Total raw points: [_1]/[_2]',&numberout($totalcorrect),&numberout($totalpossible)).'</b></p>'); + $r->print('</td>'); } - $r->print('</td>'); - # Total + if ($output) { $r->print('<td>'); } if ($cangrade) { - $r->print('<td>'. + if ($output) { + $r->print( '<select name="totaltype_'.$id.'">'. '<option value="default"'.($categories{$id.'_totaltype'} eq 'default'?' selected="selected"':'').'>'.&mt('default').'</option>'. '<option value="typein"'.($categories{$id.'_totaltype'} eq 'typein'?' selected="selected"':'').'>'.&mt('Type-in value').'</option>'. '</select>'. '<input type="text" size="4" name="total_'.$id. - '" value="'.&Apache::lonhtmlcommon::entity_encode($categories{$id.'_total'}).'" /></td>'); + '" value="'.&Apache::lonhtmlcommon::entity_encode($categories{$id.'_total'}).'" />'); + } } else { - $r->print('<td>'.($categories{$id.'_totaltype'} eq 'default'?&mt('default'):$categories{$id.'_total'}).'</td>'); + if ($output) { + $r->print('<td>'.($categories{$id.'_totaltype'} eq 'default'?&mt('default'):$categories{$id.'_total'})); + } + } +# Adjust total points + if ($categories{$id.'_totaltype'} eq 'typein') { + $totalpossible=1.*$categories{$id.'_total'}; + } + if ($output) { + $r->print('<p><b>'.&mt('Adjusted raw points: [_1]/[_2]',&numberout($totalcorrect),&numberout($totalpossible)).'</b></p>'); } # Calculation - $r->print('<td><ul>'); + if ($output) { $r->print('<td><ul>'); } foreach my $calcrule (split(/\,/,$categories{$id.'_calculations'})) { - $r->print('<li>'); + if ($output) { $r->print('<li>'); } my ($code,$value)=split(/\:/,$calcrule); - $r->print(&pretty_prt_rule($cangrade,$id,$code,$value)); + if ($output) { $r->print(&pretty_prt_rule($cangrade,$id,$code,$value)); } if ($cangrade) { - $r->print(' <a href="javascript:storecmd(\'delrule_'.$id.'______'.$code.'\');">'.&mt('Delete').'</a>'); + if ($output) { $r->print(' <a href="javascript:storecmd(\'delrule_'.$id.'______'.$code.'\');">'.&mt('Delete').'</a>'); } } - $r->print('</li>'); - } - $r->print('</ul>'); - if ($cangrade) { - $r->print('<br />'.&new_calc_rule_form($id)); + if ($code eq 'capabove') { + if ($totalpossible>0) { + if ($totalcorrect/$totalpossible>$value/100.) { + $totalcorrect=$totalpossible*$value/100.; + } + } + } elsif ($code eq 'capbelow') { + if ($totalpossible>0) { + if ($totalcorrect/$totalpossible<$value/100.) { + $totalcorrect=$totalpossible*$value/100.; + } + } + } elsif ($code eq 'droplow') { + ($totalpossible,$totalcorrect,@individual)=&drop(0,0,$value,@individual); + } elsif ($code eq 'drophigh') { + ($totalpossible,$totalcorrect,@individual)=&drop(1,0,$value,@individual); + } elsif ($code eq 'droplowperc') { + ($totalpossible,$totalcorrect,@individual)=&drop(0,1,$value,@individual); + } elsif ($code eq 'drophighperc') { + ($totalpossible,$totalcorrect,@individual)=&drop(1,1,$value,@individual); + } + if ($output) { $r->print('</li>'); } + } +# Re-adjust total points if force total + if ($categories{$id.'_totaltype'} eq 'typein') { + $totalpossible=1.*$categories{$id.'_total'}; + } + + if ($output) { + $r->print('</ul>'); + if ($cangrade) { $r->print('<br />'.&new_calc_rule_form($id)); } + $r->print('<p><b>'.&mt('Calculated points: [_1]/[_2]',&numberout($totalcorrect),&numberout($totalpossible)).'</b></p>'); + $r->print('</td>'); } - $r->print('</td>'); - +# +# Prepare for export +# # Weight + my $weight=$categories{$id.'_weight'}; + unless (1.*$weight>0) { $weight=0; } if ($cangrade) { - $r->print('<td>'. + if ($output) { + $r->print('<td>'. '<input type="text" size="4" name="weight_'.$id. - '" value="'.&Apache::lonhtmlcommon::entity_encode($categories{$id.'_weight'}).'" /></td>'); + '" value="'.&Apache::lonhtmlcommon::entity_encode($weight).'" /></td>'); + } } else { - $r->print('<td>'.$categories{$id.'_weight'}.'</td>'); + if ($output) { + $r->print('<td>'.$weight.'</td>'); + } } # Achieved - $r->print('<td>'); - if ($cangrade) { - $r->print('<select name="displayachieved_'.$id.'">'. - '<option value="percent"'.($categories{$id.'_displayachieved'} eq 'percent'?' selected="selected"':'').'>'.&mt('percent').'</option>'. - '<option value="points"'.($categories{$id.'_displayachieved'} eq 'points'?' selected="selected"':'').'>'.&mt('points').'</option>'. + my $type=$categories{$id.'_displayachieved'}; + unless (($type eq 'percent') || ($type eq 'points')) { $type='points'; } + if ($output) { $r->print('<td>'); } + if ($cangrade) { + if ($output) { + $r->print('<select name="displayachieved_'.$id.'">'. + '<option value="percent"'.($type eq 'percent'?' selected="selected"':'').'>'.&mt('percent').'</option>'. + '<option value="points"'.($type eq 'points'?' selected="selected"':'').'>'.&mt('points').'</option>'. '</select>'); - } else { - if ($categories{$id.'_displayachieved'} eq 'percent') { - $r->print(&mt('percent')); + } + } + if ($output) { + $r->print('<p><b>'); + if ($type eq 'percent') { + my $perc='---'; + if ($totalpossible) { + $perc=100.*$totalcorrect/$totalpossible; + } + $r->print(&mt('[_1] percent',&numberout($perc))); } else { - $r->print(&mt('points')); + $r->print(&mt('[_1]/[_2] points',&numberout($totalcorrect),&numberout($totalpossible))); } + $r->print('</b></p>'); } - $r->print('</td>'); + if ($output) { $r->print('</td>'); } - return ($value,$weight); + return ($totalcorrect,$totalpossible,$type,$weight); } # +# Drop folders and problems +# + +sub drop { + my ($high,$percent,$n,@individual)=@_; +# Sort assignments by points or percent + my @newindividual=sort { + my ($pa,$ca)=split(/\:/,$a); + my ($pb,$cb)=split(/\:/,$b); + if ($percent) { + my $perca=0; + if ($pa>0) { $perca=$ca/$pa; } + my $percb=0; + if ($pb>0) { $percb=$cb/$pb; } + $perca<=>$percb; + } else { + $ca<=>$cb; + } + } @individual; +# Drop the ones we don't want + if ($#newindividual>=$n) { + if ($high) { + splice(@newindividual,$#newindividual+1-$n,$n); + } else { + splice(@newindividual,0,$n); + } + } else { + @newindividual=(); + } +# Re-calculate how many points possible and achieved + my $newpossible=0; + my $newcorrect=0; + for my $score (@newindividual) { + my ($thispossible,$thiscorrect)=(split(/\:/,$score)); + $newpossible+=$thispossible; + $newcorrect+=$thiscorrect; + } + return ($newpossible,$newcorrect,@newindividual); +} +# # Bottom line with grades # sub bottom_line_category { - my ($r,$cangrade,$sum,$total)=@_; + my ($r,$cangrade,$perc)=@_; $r->print(&Apache::loncommon::start_data_table_row()); if ($cangrade) { $r->print('<td colspan="3"><a href="javascript:storecmd(\'createnewcat\');">'.&mt('Create New Category').'</a></td>'); } - $r->print('<td colspan="6">'.&mt('Current:').$sum.'<br />'.&mt('Total:').$total.'<br /></td>'); + $r->print('<td colspan="6"><b>'.&mt('Total: [_1] percent',&numberout($perc)).'</b></td>'); } +sub numberout { + my ($number)=@_; + my $printout=sprintf("%.3f", $number); + $printout=~s/0+$//; + $printout=~s/\.$//; + return $printout; +} # # Make one new category # @@ -759,8 +909,10 @@ sub make_new_category { sub category_rule_codes { return &Apache::lonlocal::texthash( - 'droplow' => 'Drop N lowest grade assignments', - 'drophigh' => 'Drop N highest grade assignments', + 'droplowperc' => 'Drop N lowest grade percentage problems/folders', + 'drophighperc' => 'Drop N highest grade percentage problems/folderss', + 'droplow' => 'Drop N lowest point problems/folders', + 'drophigh' => 'Drop N highest point problems/folders', 'capabove' => 'Cap percentage above N percent', 'capbelow' => 'Cap percentage below N percent'); } @@ -844,6 +996,7 @@ sub set_category_rules { sub add_category_content { my ($id,$cangrade,$newcontent,%categories)=@_; unless ($cangrade) { return %categories; } + &Apache::lonnet::logthis("In here $newcontent"); my %newcontent=($newcontent => 1); foreach my $current (split(/\,/,$categories{$id.'_content'})) { $newcontent{$current}=1;