File:  [LON-CAPA] / loncom / interface / lonquickgrades.pm
Revision 1.47: download - view: text, annotated - select for diffs
Thu Feb 26 16:17:30 2009 UTC (15 years, 2 months ago) by schafran
Branches: MAIN
CVS tags: HEAD, BZ5434-fox
Box title is now the main title of the next page and the link text or the former page title will be set to the subtitle in the near future.
Group: consistent wording

    1: # The LearningOnline Network with CAPA
    2: # Quick Student Grades Display
    3: #
    4: # $Id: lonquickgrades.pm,v 1.47 2009/02/26 16:17:30 schafran 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: # Created Nov. 14, 2002 by Jeremy Bowers
   29: 
   30: package Apache::lonquickgrades;
   31: 
   32: use strict;
   33: use Apache::Constants qw(:common :http);
   34: use POSIX;
   35: use Apache::loncommon;
   36: use Apache::lonlocal;
   37: use Apache::lonnet;
   38: use Apache::grades;
   39: 
   40: sub handler {
   41:     my $r = shift;
   42:     return real_handler($r);
   43: }
   44: 
   45: sub real_handler {
   46:     my $r = shift;
   47: 
   48:     &Apache::loncommon::get_unprocessed_cgi($ENV{QUERY_STRING});
   49: 
   50:     # Handle header-only request
   51:     if ($env{'browser.mathml'}) {
   52: 	&Apache::loncommon::content_type($r,'text/xml');
   53:     } else {
   54: 	&Apache::loncommon::content_type($r,'text/html');
   55:     }
   56:     if ($r->header_only) {
   57: 	$r->send_http_header;
   58:         return OK;
   59:     }
   60: 
   61:     # Send header, don't cache this page
   62:     &Apache::loncommon::no_cache($r);
   63:     $r->send_http_header;
   64: 
   65:     my $showPoints = 
   66:         $env{'course.'.$env{'request.course.id'}.'.grading'} eq 'standard';
   67:     my $notshowSPRSlink = 
   68:         $env{'course.'.$env{'request.course.id'}.'.grading'} eq 'external';
   69: 
   70:     # Create the nav map
   71:     my $navmap = Apache::lonnavmaps::navmap->new();
   72: 
   73:     if (!defined($navmap)) {
   74:         my $requrl = $r->uri;
   75:         $env{'user.error.msg'} = "$requrl:bre:0:0:Navamp initialization failed.";
   76:         return HTTP_NOT_ACCEPTABLE;
   77:     }
   78: 
   79:     # Keep this hash in sync with %statusIconMap in lonnavmaps; they
   80:     # should match color/icon
   81:     my $res = $navmap->firstResource(); # temp resource to access constants
   82:  
   83:     # Header
   84:     my $title = "Grading and Statistics";#$showPoints ? "Points Display" : "Completed Problems Display";
   85:     my $brcrum = [{href=>"/adm/quickgrades",text => "Points Display"}];
   86:     $r->print(&Apache::loncommon::start_page($title,undef,
   87:                                             {'bread_crumbs' => $brcrum})
   88:              );
   89: 
   90:     if (!$showPoints && !$notshowSPRSlink ) {
   91:         $r->print('<p>'
   92:                  .&mt('This screen shows how many problems (or problem parts) you have completed'
   93:                      .', and how many you have not yet done.'
   94:                      .' You can also look at [_1]a detailed score sheet[_2].'
   95:                      ,'<a href="/adm/studentcalc">','</a>')
   96:                  .'</p>');
   97:     }
   98: 
   99:     $r->print('<p class="LC_info">'.&mt('This may take a few moments to display.').'</p>');
  100: 
  101:     $r->rflush();
  102: 
  103:     # End navmap using boilerplate
  104: 
  105:     my $iterator = $navmap->getIterator(undef, undef, undef, 1);
  106:     my $depth = 1;
  107:     $iterator->next(); # ignore first BEGIN_MAP
  108:     my $curRes = $iterator->next();
  109:     
  110:     # General overview of the following: Walk along the course resources.
  111:     # For every problem in the resource, tell its parent maps how many
  112:     # parts and how many parts correct it has. After that, each map will
  113:     # have a count of the total parts underneath it, correct and otherwise.
  114:     # After that, we will walk through the course again and read off
  115:     # maps in order, with their data. 
  116:     # (If in the future people decide not to be cumulative, only add
  117:     #  the counts to the parent map.)
  118:     # For convenience, "totalParts" is also "totalPoints" when we're looking
  119:     #  at points; I can't come up with a variable name that makes sense
  120:     #  equally for both cases.
  121: 
  122:     my $totalParts = 0; my $totalPossible = 0; my $totalRight = 0;
  123:     my $totalAttempted = 0;
  124:     my $now = time();
  125:     my $topLevelParts = 0; my $topLevelRight = 0; my $topLevelAttempted = 0;
  126: 
  127:     # Pre-run: Count parts correct
  128:     while ( $depth > 0 ) {
  129:         if ($curRes == $iterator->BEGIN_MAP()) {$depth++;}
  130:         if ($curRes == $iterator->END_MAP()) { $depth--; }
  131: 
  132:         if (ref($curRes) && $curRes->is_problem() && !$curRes->randomout)
  133:         {
  134:             # Get number of correct, incorrect parts
  135:             my $parts = $curRes->parts();
  136:             my $partsRight = 0;
  137: 	    my $partsCount = 0;
  138: 	    my $partsAttempted = 0;
  139:             my $stack = $iterator->getStack();
  140:             
  141:             for my $part (@{$parts}) {
  142: 		my $completionStatus = $curRes->getCompletionStatus($part);
  143: 		my $dateStatus = $curRes->getDateStatus($part);
  144: 		
  145:                 if ($completionStatus == $curRes->EXCUSED()) {
  146:                     next;
  147:                 }
  148: 		if ($showPoints) {
  149: 		    my $score = 0;
  150: 		    # If we're not telling status and the answer date isn't passed yet, 
  151: 		    # it's an "attempted" point
  152: 		    if ((($curRes->problemstatus($part) eq 'no') ||
  153:                         ($curRes->problemstatus($part) eq 'no_feedback_ever')) &&
  154: 			($dateStatus != $curRes->ANSWER_OPEN)) {
  155: 			my $status = $curRes->simpleStatus($part);
  156: 			if ($status == $curRes->ATTEMPTED) {
  157: 			    $partsAttempted += $curRes->weight($part);
  158: 			    $totalAttempted += $partsAttempted;
  159: 			}
  160: 		    } else {
  161: 			$score = &Apache::grades::compute_points($curRes->weight($part), $curRes->awarded($part));
  162: 		    }
  163: 		    $partsRight += $score;
  164: 		    $totalRight += $score;
  165: 		    $partsCount += $curRes->weight($part);
  166: 
  167: 		    if ($curRes->opendate($part) < $now) {
  168: 			$totalPossible += $curRes->weight($part);
  169: 		    }
  170: 		    $totalParts += $curRes->weight($part);
  171: 		} else {
  172: 		    my $status = $curRes->simpleStatus($part);
  173: 		    my $thisright = 0;
  174: 		    $partsCount++;
  175: 		    if ($status == $curRes->CORRECT ||
  176: 			$status == $curRes->PARTIALLY_CORRECT ) {
  177: 			$partsRight++;
  178: 			$totalRight++;
  179: 			$thisright = 1;
  180: 		    }
  181: 
  182: 		    if ($status == $curRes->ATTEMPTED) {
  183: 			$partsAttempted++;
  184: 			$totalAttempted++;
  185: 		    }
  186: 		    
  187: 		    my $dateStatus = $curRes->getDateStatus($part);
  188: 		    $totalParts++;
  189: 		    if ($curRes->opendate($part) < $now) {
  190: 			$totalPossible++;
  191: 		    }
  192: 		}
  193:             }
  194: 
  195:             if ($depth == 1) { # in top-level only
  196: 		$topLevelParts += $partsCount;
  197: 		$topLevelRight += $partsRight;
  198: 		$topLevelAttempted += $partsAttempted;
  199: 	    }
  200: 
  201:             # Crawl down stack and record parts correct and total
  202:             for my $res (@{$stack}) {
  203:                 if (ref($res) && $res->is_map()) {
  204:                     if (!defined($res->{DATA}->{CHILD_PARTS})) {
  205:                         $res->{DATA}->{CHILD_PARTS} = 0;
  206:                         $res->{DATA}->{CHILD_CORRECT} = 0;
  207: 			$res->{DATA}->{CHILD_ATTEMPTED} = 0;
  208:                     }
  209:                     
  210:                     $res->{DATA}->{CHILD_PARTS} += $partsCount;
  211:                     $res->{DATA}->{CHILD_CORRECT} += $partsRight;
  212: 		    $res->{DATA}->{CHILD_ATTEMPTED} += $partsAttempted;
  213:                 }
  214:             }
  215:         }
  216:         $curRes = $iterator->next();
  217:     }
  218: 
  219:     $iterator = $navmap->getIterator(undef, undef, undef, 1);
  220:     $depth = 1;
  221:     $iterator->next(); # ignore first BEGIN_MAP
  222:     $curRes = $iterator->next();
  223: 
  224:     my @start = (255, 255, 192);
  225:     my @end   = (0, 192, 0);
  226: 
  227:     my $indentString = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
  228: 
  229:     # Second pass: Print the maps.
  230:     $r->print(&Apache::loncommon::start_data_table()
  231:              .&Apache::loncommon::start_data_table_header_row()
  232:              .'<th>'.&mt('Folder').'</th>');
  233:     $title = &mt($showPoints ? "Points Scored" : "Done");
  234:     if ($totalAttempted) {
  235: 	$title .= " / " . &mt("Attempted");
  236:     }
  237:     $r->print("<th>$title / ".&mt('Total').'</td>'
  238:              .&Apache::loncommon::end_data_table_header_row());
  239:     while ($depth > 0) {
  240:         if ($curRes == $iterator->BEGIN_MAP()) {$depth++;}
  241:         if ($curRes == $iterator->END_MAP()) { $depth--; }
  242: 
  243:         if (ref($curRes) && $curRes->is_map()) {
  244:             my $title = $curRes->compTitle();
  245:             
  246:             my $correct = $curRes->{DATA}->{CHILD_CORRECT};
  247:             my $total = $curRes->{DATA}->{CHILD_PARTS};
  248: 	    my $attempted = $curRes->{DATA}->{CHILD_ATTEMPTED};
  249: 
  250:             if ($total > 0) {
  251:                 my $ratio;
  252:                 $ratio = $correct / $total;
  253:                 my $color = mixColors(\@start, \@end, $ratio);
  254:                 $r->print(&Apache::loncommon::start_data_table_row()
  255:                          .'<td style="background-color:'.$color.';">');
  256:                 
  257: 		my $thisIndent = '';
  258:                 for (my $i = 1; $i < $depth; $i++) { $thisIndent .= $indentString; }
  259:                 
  260:                 $r->print("$thisIndent$title</td>");
  261: 		if ($totalAttempted) {
  262: 		    $r->print('<td valign="top">'
  263:                              .$thisIndent
  264:                              .'<span class="LC_nobreak">'
  265:                              .$correct.' / '.$attempted.' / '.$total
  266:                              .'</span></td>'
  267:                              .&Apache::loncommon::end_data_table_row()
  268:                     );
  269: 		} else {
  270: 		    $r->print('<td valign="top">'
  271:                              .$thisIndent
  272:                              .'<span class="LC_nobreak">'
  273:                              .$correct.' / '.$total
  274:                              .'</span></td>'
  275:                              .&Apache::loncommon::end_data_table_row());
  276: 		}
  277:             }
  278:         }
  279: 
  280:         $curRes = $iterator->next();
  281:     }
  282: 
  283:     # If there were any problems at the top level, print an extra "catchall"
  284:     if ($topLevelParts > 0) {
  285:         my $ratio = $topLevelRight / $topLevelParts;
  286:         my $color = mixColors(\@start, \@end, $ratio);
  287:         $r->print(&Apache::loncommon::start_data_table_row()
  288:                  .'<td style="background-color:'.$color.';">');
  289:         $r->print(&mt("Problems Not Contained In A Folder")."</td><td>");
  290:         $r->print("$topLevelRight / $topLevelParts</td>"
  291:                  .&Apache::loncommon::end_data_table_row());
  292:     }
  293: 
  294:     if ($showPoints) {
  295: 	my $maxHelpLink = Apache::loncommon::help_open_topic("Quick_Grades_Possibly_Correct");
  296: 
  297: 	$title = $showPoints ? "Points" : "Parts Done";
  298: 	my $totaltitle = $showPoints ? &mt("Awarded Total Points") : &mt("Total Parts Done");
  299: 	$r->print(&Apache::loncommon::start_data_table_row()
  300:                  .'<td colspan="2" align="right">'.$totaltitle.': <b>'.$totalRight.'</b><br>');
  301: 	$r->print(&mt("Max Possible To Date")." $maxHelpLink: <b>$totalPossible</b><br>");
  302: 	$title = $showPoints ? "Points" : "Parts";
  303: 	$r->print(&mt("Total $title In Course").': <b>'.$totalParts.'</b></td>'
  304:                  .&Apache::loncommon::end_data_table_row());
  305:     }
  306: 
  307:     $r->print(&Apache::loncommon::end_data_table()
  308:              .&Apache::loncommon::end_page());
  309: 
  310:     return OK;
  311: }
  312: 
  313: # Pass this two refs to arrays for the start and end color, and a number
  314: # from 0 to 1 for how much of the latter you want to mix in. It will
  315: # return a string ready to show ("#FFC309");
  316: sub mixColors {
  317:     my $start = shift;
  318:     my $end = shift;
  319:     my $ratio = shift;
  320:     
  321:     my ($a,$b);
  322:     my $final = "";
  323:     $a = $start->[0]; $b = $end->[0];
  324:     my $mix1 = POSIX::floor((1-$ratio)*$a + $ratio*$b);
  325:     $a = $start->[1]; $b = $end->[1];
  326:     my $mix2 = POSIX::floor((1-$ratio)*$a + $ratio*$b);
  327:     $a = $start->[2]; $b = $end->[2];
  328:     my $mix3 = POSIX::floor((1-$ratio)*$a + $ratio*$b);
  329: 
  330:     $final = sprintf "%02x%02x%02x", $mix1, $mix2, $mix3;
  331:     return "#" . $final;
  332: }
  333: 
  334: 1;

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>