[system] / branches / rel-2-3-dev / webwork-modperl / lib / WeBWorK / ContentGenerator / Instructor / Stats.pm Repository:
ViewVC logotype

View of /branches/rel-2-3-dev/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/Stats.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3973 - (download) (as text) (annotate)
Wed Jan 25 23:13:56 2006 UTC (7 years, 3 months ago) by sh002i
Original Path: trunk/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/Stats.pm
File size: 22384 byte(s)
forward-port from rel-2-2-dev: (update copyright date range -- 2000-2006.
this is probably overkill, since there are some files that were created
after 2000 and some files that were last modified before 2006.)

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/Instructor/Stats.pm,v 1.61 2006/01/24 23:27:02 sh002i Exp $
    5 #
    6 # This program is free software; you can redistribute it and/or modify it under
    7 # the terms of either: (a) the GNU General Public License as published by the
    8 # Free Software Foundation; either version 2, or (at your option) any later
    9 # version, or (b) the "Artistic License" which comes with this package.
   10 #
   11 # This program is distributed in the hope that it will be useful, but WITHOUT
   12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   13 # FOR A PARTICULAR PURPOSE.  See either the GNU General Public License or the
   14 # Artistic License for more details.
   15 ################################################################################
   16 
   17 package WeBWorK::ContentGenerator::Instructor::Stats;
   18 use base qw(WeBWorK::ContentGenerator::Instructor);
   19 
   20 =head1 NAME
   21 
   22 WeBWorK::ContentGenerator::Instructor::Stats - Display statistics by user or
   23 homework set.
   24 
   25 =cut
   26 
   27 use strict;
   28 use warnings;
   29 use CGI qw();
   30 use WeBWorK::Debug;
   31 use WeBWorK::ContentGenerator::Grades;
   32 use WeBWorK::DB::Record::Set;
   33 use WeBWorK::Utils qw(readDirectory list2hash max sortByName);
   34 
   35 # The table format has been borrowed from the Grades.pm module
   36 sub initialize {
   37   my $self     = shift;
   38   # FIXME  are there args here?
   39   my @components = @_;
   40   my $r          = $self->{r};
   41   my $urlpath    = $r->urlpath;
   42   my $type       = $urlpath->arg("statType") || '';
   43   my $db         = $self->{db};
   44   my $ce         = $self->{ce};
   45   my $authz      = $self->{authz};
   46   my $courseName = $urlpath->arg('courseID');
   47   my $user       = $r->param('user');
   48 
   49   # Check permissions
   50   return unless $authz->hasPermissions($user, "access_instructor_tools");
   51 
   52   $self->{type}  = $type;
   53   if ($type eq 'student') {
   54     my $studentName = $r->urlpath->arg("userID") || $user;
   55     $self->{studentName } = $studentName;
   56 
   57   } elsif ($type eq 'set') {
   58     my $setName = $r->urlpath->arg("setID") || 0;
   59     $self->{setName}     = $setName;
   60     my $setRecord  = $db->getGlobalSet($setName); # checked
   61     die "global set $setName  not found." unless $setRecord;
   62     $self->{set_due_date} = $setRecord->due_date;
   63     $self->{setRecord}   = $setRecord;
   64   }
   65 
   66 
   67 }
   68 
   69 
   70 sub title {
   71   my ($self) = @_;
   72   my $r = $self->r;
   73   my $authz = $r->authz;
   74   my $user = $r->param('user');
   75 
   76   return "" unless $authz->hasPermissions($user, "access_instructor_tools");
   77 
   78   my $type                = $self->{type};
   79   my $string              = "Statistics for ".$self->{ce}->{courseName}." ";
   80 
   81   if ($type eq 'student') {
   82     $string             .= "student ".$self->{studentName};
   83   } elsif ($type eq 'set' ) {
   84     $string             .= "set   ".$self->{setName};
   85     $string             .= ".    Due ". $self->formatDateTime($self->{set_due_date});
   86   }
   87   return $string;
   88 }
   89 sub siblings {
   90   my ($self) = @_;
   91   my $r = $self->r;
   92   my $db = $r->db;
   93   my $authz = $r->authz;
   94   my $user = $r->param('user');
   95   my $urlpath = $r->urlpath;
   96 
   97   # Check permissions
   98   return "" unless $authz->hasPermissions($user, "access_instructor_tools");
   99 
  100   my $courseID = $urlpath->arg("courseID");
  101   my $eUserID  = $r->param("effectiveUser");
  102   my @setIDs   = sort  $db->listGlobalSets;
  103 
  104   my $stats     = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::Stats",
  105                                           courseID => $courseID);
  106 
  107   print CGI::start_div({class=>"info-box", id=>"fisheye"});
  108   print CGI::h2("Statistics");
  109   #print CGI::start_ul({class=>"LinksMenu"});
  110   #print CGI::start_li();
  111   #print CGI::span({style=>"font-size:larger"}, CGI::a({href=>$self->systemLink($stats)}, 'Statistics'));
  112   print CGI::start_ul();
  113 
  114   foreach my $setID (@setIDs) {
  115     my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::Stats",
  116       courseID => $courseID, setID => $setID,statType => 'set',);
  117     print CGI::li(CGI::a({href=>$self->systemLink($problemPage)}, WeBWorK::ContentGenerator::underscore2nbsp($setID)));
  118   }
  119 
  120   print CGI::end_ul();
  121   #print CGI::end_li();
  122   #print CGI::end_ul();
  123   print CGI::end_div();
  124 
  125   return "";
  126 }
  127 sub body {
  128   my $self       = shift;
  129   my $r          = $self->r;
  130   my $urlpath    = $r->urlpath;
  131   my $db         = $r->db;
  132   my $ce         = $r->ce;
  133   my $authz      = $r->authz;
  134   my $courseName = $urlpath->arg("courseID");
  135   my $user       = $r->param('user');
  136   my $type       = $self->{type};
  137 
  138   # Check permissions
  139   return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to access instructor tools"))
  140     unless $authz->hasPermissions($user, "access_instructor_tools");
  141 
  142   if ($type eq 'student') {
  143     my $studentName = $self->{studentName};
  144     my $studentRecord = $db->getUser($studentName) # checked
  145       or die "record for user $studentName not found";
  146     my $fullName = $studentRecord->full_name;
  147         my $courseHomePage = $urlpath->new(type  => 'set_list',
  148           args => {courseID=>$courseName});
  149     my $email = $studentRecord->email_address;
  150 
  151     print CGI::a({-href=>"mailto:$email"}, $email), CGI::br(),
  152       "Section: ", $studentRecord->section, CGI::br(),
  153       "Recitation: ", $studentRecord->recitation, CGI::br();
  154 
  155     if ($authz->hasPermissions($user, "become_student")) {
  156       my $act_as_student_url = $self->systemLink($courseHomePage,
  157         params => {effectiveUser=>$studentName});
  158 
  159       print 'Act as: ', CGI::a({-href=>$act_as_student_url},$studentRecord->user_id);
  160     }
  161 
  162     print WeBWorK::ContentGenerator::Grades::displayStudentStats($self,$studentName);
  163   } elsif( $type eq 'set') {
  164     $self->displaySets($self->{setName});
  165   } elsif ($type eq '') {
  166     $self->index;
  167   } else {
  168     warn "Don't recognize statistics display type: |$type|";
  169   }
  170 
  171   return '';
  172 }
  173 sub index {
  174   my $self          = shift;
  175   my $r             = $self->r;
  176   my $urlpath       = $r->urlpath;
  177   my $ce            = $r->ce;
  178   my $db            = $r->db;
  179   my $courseName    = $urlpath->arg("courseID");
  180 
  181   my @studentList   = sort $db->listUsers;
  182   my @setList       = sort  $db->listGlobalSets;
  183 
  184 
  185   my @setLinks      = ();
  186   my @studentLinks  = ();
  187   foreach my $set (@setList) {
  188       my $setStatisticsPage   = $urlpath->newFromModule($urlpath->module,
  189                                                         courseID => $courseName,
  190                                                         statType => 'set',
  191                                                         setID    => $set
  192       );
  193     push @setLinks, CGI::a({-href=>$self->systemLink($setStatisticsPage) }, WeBWorK::ContentGenerator::underscore2nbsp($set));
  194   }
  195 
  196   foreach my $student (@studentList) {
  197       my $userStatisticsPage  = $urlpath->newFromModule($urlpath->module,
  198                                                         courseID => $courseName,
  199                                                         statType => 'student',
  200                                                         userID   => $student
  201       );
  202     push @studentLinks, CGI::a({-href=>$self->systemLink($userStatisticsPage,
  203                                                          prams=>{effectiveUser => $student}
  204                                                          )},"  $student" ),;
  205   }
  206   print join("",
  207     CGI::start_table({-border=>2, -cellpadding=>20}),
  208     CGI::Tr(
  209       CGI::td({-valign=>'top'},
  210         CGI::h3({-align=>'center'},'View statistics by set'),
  211         CGI::ul(  CGI::li( [@setLinks] ) ),
  212       ),
  213       CGI::td({-valign=>'top'},
  214         CGI::h3({-align=>'center'},'View statistics by student'),
  215         CGI::ul(CGI::li( [ @studentLinks ] ) ),
  216       ),
  217     ),
  218     CGI::end_table(),
  219   );
  220 
  221 }
  222 ###################################################
  223 # Determines the percentage of students whose score is greater than a given value
  224 # The percentages are fixed at 75, 50, 25 and 5%
  225 sub determine_percentiles {
  226   my $percent_brackets  = shift;
  227   my @list_of_scores    = @_;
  228   @list_of_scores       = sort {$a<=>$b} @list_of_scores;
  229   my %percentiles          = ();
  230   my $num_students      = $#list_of_scores;
  231   foreach my $percentage (@{$percent_brackets}) {
  232     $percentiles{$percentage} = @list_of_scores[int( (100-$percentage)*$num_students/100)];
  233     $percentiles{$percentage} =0 unless defined($percentiles{$percentage});  #in case no students have tried this question
  234   }
  235   # for example
  236   # $percentiles{75}  = @list_of_scores[int( 25*$num_students/100)];
  237   # means that 75% of the students received this score ($percentiles{75}) or higher
  238   %percentiles;
  239 }
  240 sub prevent_repeats {    # replace a string such as 0 0 0 86 86 100 100 100 by    0 - - 86 - 100 - -
  241   my @inarray   = @_;
  242   my @outarray = ();
  243   my $saved_item = shift @inarray;
  244   push @outarray, $saved_item;
  245   while (@inarray )   {
  246     my $current_item = shift @inarray;
  247     if ( $current_item == $saved_item ) {
  248       push @outarray, '&nbsp;-';
  249     } else {
  250       push @outarray, $current_item;
  251       $saved_item = $current_item;
  252     }
  253   }
  254   @outarray;
  255 }
  256 
  257 sub displaySets {
  258   my $self             = shift;
  259   my $r                = $self->r;
  260   my $urlpath          = $r->urlpath;
  261   my $db               = $r->db;
  262   my $ce               = $r->ce;
  263   my $authz            = $r->authz;
  264   my $courseName       = $urlpath->arg("courseID");
  265   my $setName          = $urlpath->arg("setID");
  266   my $user             = $r->param('user');
  267   my $setRecord        = $self->{setRecord};
  268   my $root             = $ce->{webworkURLs}->{root};
  269 
  270   my $setStatsPage     = $urlpath->newFromModule($urlpath->module,courseID=>$courseName,statType=>'sets',setID=>$setName);
  271   my $sort_method_name = $r->param('sort');
  272   my @studentList      = $db->listUsers;
  273 
  274     my @index_list                           = ();  # list of all student index
  275   my @score_list                           = ();  # list of all student total percentage scores
  276     my %attempts_list_for_problem            = ();  # a list of the number of attempts for each problem
  277     my %number_of_attempts_for_problem       = ();  # the total number of attempst for this problem (sum of above list)
  278     my %number_of_students_attempting_problem = ();  # the number of students attempting this problem.
  279     my %correct_answers_for_problem          = ();  # the number of students correctly answering this problem (partial correctness allowed)
  280   my $sort_method = sub {
  281     my ($a,$b) = @_;
  282     return 0 unless defined($sort_method_name);
  283     return $b->{score} <=> $a->{score} if $sort_method_name eq 'score';
  284     return $b->{index} <=> $a->{index} if $sort_method_name eq 'index';
  285     return $a->{section} cmp $b->{section} if $sort_method_name eq 'section';
  286     if ($sort_method_name =~/p(\d+)/) {
  287         my $left  =  $b->{problemData}->{$1} ||0;
  288         my $right =  $a->{problemData}->{$1} ||0;
  289       return $left <=> $right;  # sort by number of attempts.
  290     }
  291 
  292   };
  293 
  294 ###############################################################
  295 #  Print tables
  296 ###############################################################
  297 
  298   my $max_num_problems  = 0;
  299   # get user records
  300   debug("Begin obtaining problem records for  set $setName");
  301   my @userRecords  = $db->getUsers(@studentList);
  302   debug("End obtaining user records for set $setName");
  303     debug("begin main loop");
  304   my @augmentedUserRecords    = ();
  305   my $number_of_active_students;
  306 
  307     ########################################
  308     # Notes for factoring this calculation
  309     #
  310     # Inputs include:
  311     # $user
  312     # $setName
  313     # @userRecords
  314     #               @problemRecords  these are fetched for each student in @userRecords
  315     #
  316     ###################################
  317 
  318   foreach my $studentRecord (@userRecords)   {
  319     next unless ref($studentRecord);
  320     my $student = $studentRecord->user_id;
  321     next if $studentRecord->last_name =~/^practice/i;  # don't show practice users
  322     next unless $ce->status_abbrev_has_behavior($studentRecord->status, "include_in_stats");
  323     $number_of_active_students++;
  324       my $string          = '';
  325       my $twoString       = '';
  326       my $totalRight      = 0;
  327       my $total           = 0;
  328     my $total_num_of_attempts_for_set = 0;
  329     my %h_problemData   = ();
  330     my $probNum         = 0;
  331 
  332     debug("Begin obtaining problem records for user $student set $setName");
  333 
  334     my @problemRecords = sort {$a->problem_id <=> $b->problem_id } $db->getAllUserProblems( $student, $setName );
  335     debug("End obtaining problem records for user $student set $setName");
  336     my $num_of_problems = @problemRecords;
  337     $max_num_problems = ($max_num_problems>= $num_of_problems) ? $max_num_problems : $num_of_problems;
  338      ########################################
  339     # Notes for factoring the calculation in this loop.
  340     #
  341     # Inputs include:
  342     #
  343     #
  344     # @problemRecords
  345     # returns
  346     #       $num_of_attempts
  347     #       $status
  348     # updates
  349     #       $number_of_students_attempting_problem{$probID}++;
  350     #     @{ $attempts_list_for_problem{$probID} }
  351     #     $number_of_attempts_for_problem{$probID}
  352     #     $total_num_of_attempts_for_set
  353     #     $correct_answers_for_problem{$probID}
  354     #
  355     #       $string (formatting output)
  356     #       $twoString (more formatted output)
  357     #       $total
  358     #       $totalRight
  359     ###################################
  360 
  361     foreach my $problemRecord (@problemRecords) {
  362       next unless ref($problemRecord);
  363 
  364         # warn "Can't find record for problem $prob in set $setName for $student";
  365         # FIXME check the legitimate reasons why a student record might not be defined
  366       ####################################################################
  367       # Grab data from the database
  368       ####################################################################
  369       # It's possible that $problemRecord->num_correct or $problemRecord->num_correct
  370       # or $problemRecord->status is an empty
  371       # or blank string instead of 0.  The || clause fixes this and prevents
  372       # warning messages in the comparisons below.
  373 
  374       my $probID             = $problemRecord->problem_id;
  375       my $attempted          = $problemRecord->attempted;
  376       my $num_correct        = $problemRecord->num_correct     || 0;
  377       my $num_incorrect      = $problemRecord->num_incorrect   || 0;
  378       my $num_of_attempts    = $num_correct + $num_incorrect;
  379 
  380         # initialize the number of correct answers for this problem
  381         # if the value has not been defined.
  382           $correct_answers_for_problem{$probID}  = 0 unless defined($correct_answers_for_problem{$probID});
  383 
  384 
  385       my $probValue          = $problemRecord->value;
  386       # set default problem value here
  387       $probValue             = 1 unless defined($probValue) and $probValue ne "";  # FIXME?? set defaults here?
  388 
  389       my $status             = $problemRecord->status          || 0;
  390       # sanity check that the status (score) is between 0 and 1
  391           my $valid_status       = ($status >= 0 and $status <=1 ) ? 1 : 0;
  392 
  393           ###################################################################
  394           # Determine the string $longStatus which will display the student's current score
  395           ###################################################################
  396           my $longStatus = '';
  397       if (!$attempted){
  398         $longStatus     = '.';
  399       } elsif   ($valid_status) {
  400         $longStatus     = int(100*$status+.5);
  401         $longStatus     = ($longStatus == 100) ? 'C' : $longStatus;
  402       } else  {
  403         $longStatus   = 'X';
  404       }
  405 
  406       $string          .=  threeSpaceFill($longStatus);
  407       $twoString       .= threeSpaceFill($num_incorrect);
  408 
  409       $total           += $probValue;
  410       $totalRight      += round_score($status*$probValue) if $valid_status;
  411 
  412 
  413 
  414 
  415 
  416 
  417        # add on the scores for this problem
  418       if (defined($attempted) and $attempted) {
  419         $number_of_students_attempting_problem{$probID}++;
  420         push( @{ $attempts_list_for_problem{$probID} } ,     $num_of_attempts);
  421         $number_of_attempts_for_problem{$probID}             += $num_of_attempts;
  422         $h_problemData{$probID}                               = $num_incorrect;
  423         $total_num_of_attempts_for_set                       += $num_of_attempts;
  424         $correct_answers_for_problem{$probID}                += $status;
  425       }
  426 
  427     }
  428 
  429 
  430     my $act_as_student_url = $self->systemLink($urlpath->new(type=>'set_list',args=>{courseID=>$courseName}),
  431                                                params=>{effectiveUser => $studentRecord->user_id}
  432     );
  433     my $email              = $studentRecord->email_address;
  434     # FIXME  this needs formatting
  435 
  436     my $avg_num_attempts = ($num_of_problems) ? $total_num_of_attempts_for_set/$num_of_problems : 0;
  437     my $successIndicator = ($avg_num_attempts) ? ($totalRight/$total)**2/$avg_num_attempts : 0 ;
  438 
  439     my $temp_hash         = {         user_id        => $studentRecord->user_id,
  440                                       last_name      => $studentRecord->last_name,
  441                                       first_name     => $studentRecord->first_name,
  442                                       score          => $totalRight,
  443                                       total          => $total,
  444                                       index          => $successIndicator,
  445                                       section        => $studentRecord->section,
  446                                       recitation     => $studentRecord->recitation,
  447                                       problemString  => "<pre>$string\n$twoString</pre>",
  448                                       act_as_student => $act_as_student_url,
  449                                       email_address  => $studentRecord->email_address,
  450                                       problemData    => {%h_problemData},
  451     };
  452     # add this data to the list of total scores (out of 100)
  453     # add this data to the list of success indices.
  454     push( @index_list, $temp_hash->{index});
  455     push( @score_list, ($temp_hash->{total}) ?$temp_hash->{score}/$temp_hash->{total} : 0 ) ;
  456     push( @augmentedUserRecords, $temp_hash );
  457 
  458   }
  459   debug("end mainloop");
  460 
  461   @augmentedUserRecords = sort {           &$sort_method($a,$b)
  462                         ||
  463               lc($a->{last_name}) cmp lc($b->{last_name} ) } @augmentedUserRecords;
  464 
  465   # sort the problem IDs
  466   my @problemIDs         = sort {$a<=>$b} keys %correct_answers_for_problem;
  467   # determine index quartiles
  468     my @brackets1          = (90,80,70,60,50,40,30,20,10);  #% students having scores or indices above this cutoff value
  469     my @brackets2          = (95, 75,50,25,5,1);       # % students having this many incorrect attempts or more
  470   my %index_percentiles = determine_percentiles(\@brackets1, @index_list);
  471     my %score_percentiles = determine_percentiles(\@brackets1, @score_list);
  472     my %attempts_percentiles_for_problem = ();
  473     my %problemPage                      = (); # link to the problem page
  474     foreach my $probID (@problemIDs) {
  475       $attempts_percentiles_for_problem{$probID} =   {
  476         determine_percentiles([@brackets2], @{$attempts_list_for_problem{$probID}})
  477 
  478       };
  479       $problemPage{$probID} = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem",
  480       courseID => $courseName, setID => $setName, problemID => $probID);
  481 
  482     }
  483 
  484 #####################################################################################
  485 # Table showing the percentage of students with correct answers for each problems
  486 #####################################################################################
  487 
  488 print
  489 
  490      CGI::p('The percentage of active students with correct answers for each problem'),
  491     CGI::start_table({-border=>1}),
  492     CGI::Tr(CGI::td(
  493       ['Problem #',
  494          map {CGI::a({ href=>$self->systemLink($problemPage{$_}) },$_)} @problemIDs
  495       ]
  496     )),
  497     CGI::Tr(CGI::td(
  498       [ '% correct',map {($number_of_students_attempting_problem{$_})
  499                             ? sprintf("%0.0f",100*$correct_answers_for_problem{$_}/$number_of_students_attempting_problem{$_})
  500                             : '-'}
  501                              @problemIDs
  502       ]
  503     )),
  504     CGI::Tr(CGI::td(
  505       [ 'avg attempts',map {($number_of_students_attempting_problem{$_})
  506                             ? sprintf("%0.1f",$number_of_attempts_for_problem{$_}/$number_of_students_attempting_problem{$_})
  507                             : '-'}
  508                              @problemIDs
  509       ]
  510     )),
  511     CGI::end_table();
  512 
  513 #####################################################################################
  514 # table showing percentile statistics for scores and success indices
  515 #####################################################################################
  516   print
  517 
  518         CGI::p(CGI::i('The percentage of students receiving at least these scores.<br/>
  519                The median score is in the 50% column. ')),
  520       CGI::start_table({-border=>1}),
  521         CGI::Tr(
  522           CGI::td( ['% students',
  523                     (map {  "&nbsp;".$_   } @brackets1) ,
  524                     'top score ',
  525 
  526                    ]
  527           )
  528         ),
  529         CGI::Tr(
  530           CGI::td( [
  531             'Score',
  532             (prevent_repeats map { sprintf("%0.0f",100*$score_percentiles{$_})   } @brackets1),
  533             sprintf("%0.0f",100),
  534             ]
  535           )
  536         ),
  537         CGI::Tr(
  538           CGI::td( [
  539             'Success Index',
  540             (prevent_repeats  map { sprintf("%0.0f",100*$index_percentiles{$_})   } @brackets1),
  541             sprintf("%0.0f",100),
  542             ]
  543           )
  544         )
  545       ;
  546 
  547   print     CGI::end_table(),
  548 
  549     ;
  550 
  551 #####################################################################################
  552 # table showing percentile statistics for scores and success indices
  553 #####################################################################################
  554   print
  555 
  556         CGI::p(CGI::i('Percentile cutoffs for number of attempts. <br/> The 50% column shows the median number of attempts')),
  557       CGI::start_table({-border=>1}),
  558         CGI::Tr(
  559           CGI::td( ['% students',
  560                     (map {  "&nbsp;".($_)  } @brackets2) ,
  561 
  562                    ]
  563           )
  564         );
  565 
  566 
  567   foreach my $probID (@problemIDs) {
  568     print CGI::Tr(
  569           CGI::td( [
  570             CGI::a({ href=>$self->systemLink($problemPage{$probID}) },"Prob $probID"),
  571             ( prevent_repeats reverse map { sprintf("%0.0f",$attempts_percentiles_for_problem{$probID}->{$_})   } @brackets2),
  572 
  573             ]
  574           )
  575         );
  576 
  577   }
  578   print CGI::end_table();
  579 
  580   return "";
  581 }
  582 
  583 
  584 #################################
  585 # Utility function NOT a method
  586 #################################
  587 sub threeSpaceFill {
  588   my $num = shift @_ || 0;
  589 
  590   if (length($num)<=1) {return "$num".'&nbsp;&nbsp;';}
  591   elsif (length($num)==2) {return "$num".'&nbsp;';}
  592   else {return "## ";}
  593 }
  594 sub round_score{
  595   return shift;
  596 }
  597 
  598 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9