[system] / trunk / webwork-modperl / lib / WeBWorK / ContentGenerator / Instructor / Stats.pm Repository:
ViewVC logotype

Annotation of /trunk/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/Stats.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1970 - (view) (download) (as text)

1 : gage 1430 ################################################################################
2 : sh002i 1663 # WeBWorK Online Homework Delivery System
3 :     # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
4 : gage 1970 # $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/Stats.pm,v 1.34 2004/04/04 04:00:10 gage Exp $
5 : sh002i 1663 #
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 : gage 1430 ################################################################################
16 :    
17 :     package WeBWorK::ContentGenerator::Instructor::Stats;
18 :     use base qw(WeBWorK::ContentGenerator::Instructor);
19 :    
20 :     =head1 NAME
21 :    
22 : sh002i 1619 WeBWorK::ContentGenerator::Instructor::Stats - Display statistics by user or
23 :     problem set.
24 : gage 1430
25 :     =cut
26 :    
27 :     use strict;
28 :     use warnings;
29 :     use CGI qw();
30 :     use WeBWorK::Utils qw(readDirectory list2hash max);
31 :     use WeBWorK::DB::Record::Set;
32 : gage 1859 use WeBWorK::ContentGenerator::Grades;
33 :     # The table format has been borrowed from the Grades.pm module
34 : gage 1430 sub initialize {
35 : gage 1432 my $self = shift;
36 :     # FIXME are there args here?
37 : gage 1430 my @components = @_;
38 : gage 1938 my $r = $self->{r};
39 :     my $urlpath = $r->urlpath;
40 :     my $type = $urlpath->arg("statType") || '';
41 :     my $db = $self->{db};
42 :     my $ce = $self->{ce};
43 :     my $authz = $self->{authz};
44 :     my $courseName = $urlpath->arg('courseID');
45 :     my $user = $r->param('user');
46 : gage 1810
47 :    
48 : gage 1432 $self->{type} = $type;
49 :     if ($type eq 'student') {
50 : sh002i 1841 my $studentName = $r->urlpath->arg("userID") || $user;
51 :     $self->{studentName } = $studentName;
52 : gage 1432
53 :     } elsif ($type eq 'set') {
54 : sh002i 1841 my $setName = $r->urlpath->arg("setID") || 0;
55 :     $self->{setName} = $setName;
56 : gage 1810 my $setRecord = $db->getGlobalSet($setName); # checked
57 :     die "global set $setName not found." unless $setRecord;
58 :     $self->{set_due_date} = $setRecord->due_date;
59 :     $self->{setRecord} = $setRecord;
60 : gage 1432 }
61 :    
62 :    
63 : gage 1430 }
64 :    
65 : gage 1928 # sub path {
66 :     # my $self = shift;
67 :     # my $args = $_[-1];
68 :     # my $ce = $self->{ce};
69 :     # my $root = $ce->{webworkURLs}->{root};
70 :     # my $courseName = $ce->{courseName};
71 :     #
72 :     # return $self->pathMacro($args,
73 :     # "Home" => "$root",
74 :     # $courseName => "$root/$courseName",
75 :     # 'Instructor Tools' => "$root/$courseName/instructor",
76 :     # 'Statistics' =>
77 :     # ($self->{type}
78 :     # ? "$root/$courseName/instructor/stats/"
79 :     # : ""
80 :     # ),
81 :     # ($self->{type} eq 'set'
82 :     # ? ("set ".$self->{setName} => '')
83 :     # : ()
84 :     # ),
85 :     # ($self->{type} eq 'student'
86 :     # ? ("user ".$self->{studentName} => '')
87 :     # : ()
88 :     # ),
89 :     # );
90 :     # }
91 : gage 1430
92 : gage 1432 sub title {
93 : sh002i 1841 my ($self) = @_;
94 : gage 1432 my $type = $self->{type};
95 :     my $string = "Statistics for ".$self->{ce}->{courseName}." ";
96 :     if ($type eq 'student') {
97 :     $string .= "student ".$self->{studentName};
98 :     } elsif ($type eq 'set' ) {
99 :     $string .= "set ".$self->{setName};
100 : gage 1810 $string .= ".    Due ". WeBWorK::Utils::formatDateTime($self->{set_due_date});
101 : gage 1432 }
102 :     return $string;
103 : gage 1430 }
104 : gage 1432 sub body {
105 :     my $self = shift;
106 : gage 1928 my $r = $self->r;
107 :     my $urlpath = $r->urlpath;
108 :     my $db = $r->db;
109 :     my $ce = $r->ce;
110 :     my $courseName = $urlpath->arg("courseID");
111 : gage 1432 my $type = $self->{type};
112 : gage 1859
113 : gage 1432 if ($type eq 'student') {
114 : gage 1859 my $studentName = $self->{studentName};
115 : gage 1928
116 : gage 1859 my $studentRecord = $db->getUser($studentName); # checked
117 :     die "record for user $studentName not found" unless $studentRecord;
118 :    
119 :     my $fullName = join("", $studentRecord->first_name," ", $studentRecord->last_name);
120 : gage 1938
121 :     my $courseHomePage = $urlpath->new(type => 'set_list',
122 :     args => {courseID => $courseName}
123 :     );
124 :     my $act_as_student_url = $self->systemLink($courseHomePage,
125 :     params => { effectiveUser => $studentName }
126 :     );
127 :    
128 : gage 1859 my $email = $studentRecord->email_address;
129 :     print
130 :     CGI::a({-href=>"mailto:$email"},$email),CGI::br(),
131 :     "Section: ", $studentRecord->section, CGI::br(),
132 :     "Recitation: ", $studentRecord->recitation,CGI::br(),
133 :     'Act as: ',
134 :     CGI::a({-href=>$act_as_student_url},$studentRecord->user_id);
135 : gage 1938 WeBWorK::ContentGenerator::Grades::displayStudentStats($self,$studentName);
136 : gage 1859
137 :     # The table format has been borrowed from the Grades.pm module
138 : gage 1432 } elsif( $type eq 'set') {
139 :     my $setName = $self->{setName};
140 :     $self->displaySets($self->{setName});
141 :     } elsif ($type eq '') {
142 :     $self->index;
143 :     } else {
144 :     warn "Don't recognize statistics display type: |$type|";
145 : gage 1430
146 : gage 1432 }
147 :    
148 :    
149 :     return '';
150 :    
151 :     }
152 :     sub index {
153 :     my $self = shift;
154 : gage 1928 my $r = $self->r;
155 :     my $urlpath = $r->urlpath;
156 :     my $ce = $r->ce;
157 :     my $db = $r->db;
158 :     my $courseName = $urlpath->arg("courseID");
159 :    
160 : gage 1432 my @studentList = sort $db->listUsers;
161 :     my @setList = sort $db->listGlobalSets;
162 : gage 1938
163 :    
164 : gage 1433 my @setLinks = ();
165 :     my @studentLinks = ();
166 : gage 1432 foreach my $set (@setList) {
167 : gage 1938 my $setStatisticsPage = $urlpath->newFromModule($urlpath->module,
168 :     courseID => $courseName,
169 :     statType => 'set',
170 :     setID => $set
171 :     );
172 :     push @setLinks, CGI::a({-href=>$self->systemLink($setStatisticsPage) },"set $set" );
173 : gage 1432 }
174 : gage 1433
175 : gage 1432 foreach my $student (@studentList) {
176 : gage 1938 my $userStatisticsPage = $urlpath->newFromModule($urlpath->module,
177 :     courseID => $courseName,
178 :     statType => 'student',
179 :     userID => $student
180 :     );
181 :     push @studentLinks, CGI::a({-href=>$self->systemLink($userStatisticsPage,
182 :     prams=>{effectiveUser => $student}
183 :     )}," $student" ),;
184 : gage 1432 }
185 : gage 1433 print join("",
186 : gage 1434 CGI::start_table({-border=>2, -cellpadding=>20}),
187 : gage 1433 CGI::Tr(
188 :     CGI::td({-valign=>'top'},
189 :     CGI::h3({-align=>'center'},'View statistics by set'),
190 :     CGI::ul( CGI::li( [@setLinks] ) ),
191 :     ),
192 :     CGI::td({-valign=>'top'},
193 :     CGI::h3({-align=>'center'},'View statistics by student'),
194 :     CGI::ul(CGI::li( [ @studentLinks ] ) ),
195 :     ),
196 :     ),
197 :     CGI::end_table(),
198 :     );
199 : gage 1432
200 :     }
201 : gage 1847 ###################################################
202 :     # Determines the percentage of students whose score is greater than a given value
203 :     # The percentages are fixed at 75, 50, 25 and 5%
204 : gage 1810 sub determine_percentiles {
205 : gage 1847 my $percent_brackets = shift;
206 :     my @list_of_scores = @_;
207 :     @list_of_scores = sort {$a<=>$b} @list_of_scores;
208 :     my %percentiles = ();
209 :     my $num_students = $#list_of_scores;
210 :     foreach my $percentage (@{$percent_brackets}) {
211 :     $percentiles{$percentage} = @list_of_scores[int( (100-$percentage)*$num_students/100)];
212 :     }
213 :     # for example
214 :     # $percentiles{75} = @list_of_scores[int( 25*$num_students/100)];
215 :     # means that 75% of the students received this score ($percentiles{75}) or higher
216 :     %percentiles;
217 : gage 1810 }
218 : gage 1432 sub displaySets {
219 : gage 1928 my $self = shift;
220 :     my $r = $self->r;
221 :     my $urlpath = $r->urlpath;
222 :     my $db = $r->db;
223 :     my $ce = $r->ce;
224 :     my $authz = $r->authz;
225 :     my $courseName = $urlpath->arg("courseID");
226 :     my $setName = $urlpath->arg("setID");
227 :     my $user = $r->param('user');
228 :     my $setRecord = $self->{setRecord};
229 :     my $root = $ce->{webworkURLs}->{root};
230 : gage 1938
231 :     my $setStatsPage = $urlpath->newFromModule($urlpath->module,courseID=>$courseName,statType=>'sets',setID=>$setName);
232 : gage 1590 my $sort_method_name = $r->param('sort');
233 : gage 1928 my @studentList = $db->listUsers;
234 : gage 1938
235 : gage 1850 my @index_list = (); # list of all student index
236 :     my @score_list = (); # list of all student total percentage scores
237 :     my %attempts_list_for_problem = (); # a list of the number of attempts for each problem
238 : gage 1970 my %number_of_students_attempting_problem = (); # the number of students attempting this problem.
239 : gage 1850 my %correct_answers_for_problem = (); # the number of students correctly answering this problem (partial correctness allowed)
240 : gage 1590 my $sort_method = sub {
241 :     my ($a,$b) = @_;
242 :     return 0 unless defined($sort_method_name);
243 :     return $b->{score} <=> $a->{score} if $sort_method_name eq 'score';
244 :     return $b->{index} <=> $a->{index} if $sort_method_name eq 'index';
245 :     return $a->{section} cmp $b->{section} if $sort_method_name eq 'section';
246 :     if ($sort_method_name =~/p(\d+)/) {
247 : gage 1766 my $left = $b->{problemData}->{$1} ||0;
248 :     my $right = $a->{problemData}->{$1} ||0;
249 :     return $left <=> $right; # sort by number of attempts.
250 : gage 1590 }
251 : gage 1432
252 : gage 1590 };
253 : gage 1810
254 : gage 1430 ###############################################################
255 : gage 1850 # Print tables
256 : gage 1430 ###############################################################
257 : gage 1809
258 :     my $max_num_problems = 0;
259 : gage 1487 # get user records
260 : gage 1632 $WeBWorK::timer->continue("Begin obtaining user records for set $setName") if defined($WeBWorK::timer);
261 : gage 1590 my @userRecords = $db->getUsers(@studentList);
262 : gage 1632 $WeBWorK::timer->continue("End obtaining user records for set $setName") if defined($WeBWorK::timer);
263 : gage 1769 $WeBWorK::timer->continue("begin main loop") if defined($WeBWorK::timer);
264 : gage 1590 my @augmentedUserRecords = ();
265 : gage 1847 my $number_of_active_students;
266 :    
267 : gage 1487 foreach my $studentRecord (@userRecords) {
268 : gage 1691 next unless ref($studentRecord);
269 : gage 1487 my $student = $studentRecord->user_id;
270 : gage 1430 next if $studentRecord->last_name =~/^practice/i; # don't show practice users
271 :     next if $studentRecord->status !~/C/; # don't show dropped students FIXME
272 : gage 1847 $number_of_active_students++;
273 :     my $status = 0;
274 :     my $attempted = 0;
275 :     my $longStatus = '';
276 :     my $string = '';
277 :     my $twoString = '';
278 :     my $totalRight = 0;
279 :     my $total = 0;
280 : gage 1430 my $num_of_attempts = 0;
281 : gage 1847 my %h_problemData = ();
282 : gage 1590 my $probNum = 0;
283 : gage 1810
284 : gage 1632 $WeBWorK::timer->continue("Begin obtaining problem records for user $student set $setName") if defined($WeBWorK::timer);
285 : gage 1810
286 : gage 1809 my @problemRecords = sort {$a->problem_id <=> $b->problem_id } $db->getAllUserProblems( $student, $setName );
287 : gage 1632 $WeBWorK::timer->continue("End obtaining problem records for user $student set $setName") if defined($WeBWorK::timer);
288 : gage 1809 my $num_of_problems = @problemRecords;
289 : gage 1938 $max_num_problems = ($max_num_problems>= $num_of_problems) ? $max_num_problems : $num_of_problems;
290 : gage 1632
291 :     foreach my $problemRecord (@problemRecords) {
292 : gage 1691 next unless ref($problemRecord);
293 : gage 1847 my $probID = $problemRecord->problem_id;
294 :    
295 : gage 1430 my $valid_status = 0;
296 :     unless (defined($problemRecord) ){
297 :     # warn "Can't find record for problem $prob in set $setName for $student";
298 :     # FIXME check the legitimate reasons why a student record might not be defined
299 :     next;
300 :     }
301 :     $status = $problemRecord->status || 0;
302 :     $attempted = $problemRecord->attempted;
303 :     if (!$attempted){
304 :     $longStatus = '. ';
305 :     }
306 :     elsif ($status >= 0 and $status <=1 ) {
307 :     $valid_status = 1;
308 :     $longStatus = int(100*$status+.5);
309 :     if ($longStatus == 100) {
310 :     $longStatus = 'C ';
311 :     }
312 :     else {
313 :     $longStatus = &threeSpaceFill($longStatus);
314 :     }
315 :     }
316 :     else {
317 :     $longStatus = 'X ';
318 :     }
319 : gage 1938
320 :     my $num_correct = $problemRecord->num_correct || 0;
321 :     my $num_incorrect = $problemRecord->num_incorrect || 0;
322 : gage 1590 # It's possible that $incorrect is an empty or blank string instead of 0 the || clause fixes this and prevents
323 :     # warning messages in the comparison below.
324 : gage 1430 $string .= $longStatus;
325 : gage 1938 $twoString .= threeSpaceFill($num_incorrect);
326 : gage 1430 my $probValue = $problemRecord->value;
327 :     $probValue = 1 unless defined($probValue); # FIXME?? set defaults here?
328 :     $total += $probValue;
329 :     $totalRight += round_score($status*$probValue) if $valid_status;
330 : gage 1938
331 : gage 1430 $num_of_attempts += $num_correct + $num_incorrect;
332 : gage 1850
333 : gage 1938 $h_problemData{$probID} = $num_incorrect;
334 :    
335 : gage 1847 $correct_answers_for_problem{$probID} = 0 unless defined($correct_answers_for_problem{$probID});
336 : gage 1850 # add on the scores for this problem
337 :     if (defined($attempted) and $attempted) {
338 : gage 1970 $number_of_students_attempting_problem{$probID}++;
339 : gage 1850 push( @{ $attempts_list_for_problem{$probID} } , $num_correct + $num_incorrect);
340 :     $correct_answers_for_problem{$probID} += $status;
341 :     }
342 :    
343 : gage 1430 }
344 :    
345 : gage 1590
346 : gage 1938 my $act_as_student_url = $self->systemLink($urlpath->new(type=>'set_list',args=>{courseID=>$courseName}),
347 :     params=>{effectiveUser => $studentRecord->user_id}
348 :     );
349 :     my $email = $studentRecord->email_address;
350 : gage 1430 # FIXME this needs formatting
351 :    
352 :     my $avg_num_attempts = ($num_of_problems) ? $num_of_attempts/$num_of_problems : 0;
353 :     my $successIndicator = ($avg_num_attempts) ? ($totalRight/$total)**2/$avg_num_attempts : 0 ;
354 : gage 1938 my $temp_hash = { user_id => $studentRecord->user_id,
355 : gage 1590 last_name => $studentRecord->last_name,
356 :     first_name => $studentRecord->first_name,
357 :     score => $totalRight,
358 :     total => $total,
359 :     index => $successIndicator,
360 :     section => $studentRecord->section,
361 :     recitation => $studentRecord->recitation,
362 :     problemString => "<pre>$string\n$twoString</pre>",
363 :     act_as_student => $act_as_student_url,
364 :     email_address => $studentRecord->email_address,
365 :     problemData => {%h_problemData},
366 :     };
367 : gage 1847 # add this data to the list of total scores (out of 100)
368 :     # add this data to the list of success indices.
369 : gage 1810 push( @index_list, $temp_hash->{index});
370 :     push( @score_list, ($temp_hash->{total}) ?$temp_hash->{score}/$temp_hash->{total} : 0 ) ;
371 : gage 1590 push( @augmentedUserRecords, $temp_hash );
372 :    
373 :     }
374 : gage 1766 $WeBWorK::timer->continue("end mainloop") if defined($WeBWorK::timer);
375 : gage 1430
376 : gage 1590 @augmentedUserRecords = sort { &$sort_method($a,$b)
377 :     ||
378 :     lc($a->{last_name}) cmp lc($b->{last_name} ) } @augmentedUserRecords;
379 : gage 1810
380 : gage 1847 # sort the problem IDs
381 :     my @problemIDs = sort {$a<=>$b} keys %correct_answers_for_problem;
382 : gage 1810 # determine index quartiles
383 : gage 1847 my @brackets = (75, 50,25,5);
384 :     my %index_percentiles = determine_percentiles(\@brackets, @index_list);
385 :     my %score_percentiles = determine_percentiles(\@brackets, @score_list);
386 :     my %attempts_percentiles_for_problem = ();
387 :     foreach my $probID (@problemIDs) {
388 :     $attempts_percentiles_for_problem{$probID} = {
389 : gage 1849 determine_percentiles([@brackets, 0], @{$attempts_list_for_problem{$probID}})
390 : gage 1847 };
391 :     }
392 : gage 1810
393 : gage 1847 #####################################################################################
394 :     # Table showing the percentage of students with correct answers for each problems
395 :     #####################################################################################
396 : gage 1970
397 : gage 1847 print
398 :    
399 : gage 1850 CGI::p('The percentage of active students with correct answers for each problem'),
400 : gage 1847 CGI::start_table({-border=>1}),
401 :     CGI::Tr(CGI::td(
402 :     ['Problem #', @problemIDs]
403 :     )),
404 :     CGI::Tr(CGI::td(
405 : gage 1970 [ '% correct',map {($number_of_students_attempting_problem{$_})
406 :     ? sprintf("%0.0f",100*$correct_answers_for_problem{$_}/$number_of_students_attempting_problem{$_})
407 :     : '-'}
408 : gage 1850 @problemIDs
409 :     ]
410 : gage 1847 )),
411 :     CGI::end_table();
412 :    
413 :     #####################################################################################
414 :     # table showing percentile statistics for scores and success indices
415 :     #####################################################################################
416 : gage 1810 print
417 : gage 1847
418 : gage 1850 CGI::p('The percentage of active students whose percentage scores and success indices are greater than the given values.'),
419 : gage 1810 CGI::start_table({-border=>1}),
420 :     CGI::Tr(
421 : gage 1847 CGI::td( ['% students',
422 :     (map { "&nbsp;$_" } @brackets) ,
423 :     'top score ',
424 :     ]
425 : gage 1810 )
426 :     ),
427 :     CGI::Tr(
428 :     CGI::td( [
429 :     'Score',
430 : gage 1850 (map { '&ge; '.sprintf("%0.0f",100*$score_percentiles{$_}) } @brackets),
431 : gage 1847 sprintf("%0.0f",100),
432 : gage 1810 ]
433 :     )
434 :     ),
435 :     CGI::Tr(
436 :     CGI::td( [
437 :     'Success Index',
438 : gage 1850 (map { '&ge; '.sprintf("%0.0f",100*$index_percentiles{$_}) } @brackets),
439 : gage 1847 sprintf("%0.0f",100),
440 : gage 1810 ]
441 :     )
442 : gage 1847 )
443 :     ;
444 :    
445 :     print CGI::end_table(),
446 :    
447 : gage 1810 ;
448 : gage 1847
449 :     #####################################################################################
450 :     # table showing percentile statistics for scores and success indices
451 :     #####################################################################################
452 :     print
453 :    
454 : gage 1850 CGI::p('The percentage of active students with no more than the indicated number of total attempts'),
455 : gage 1847 CGI::start_table({-border=>1}),
456 :     CGI::Tr(
457 :     CGI::td( ['% students',
458 : gage 1848 (map { "&nbsp;".(100-$_) } @brackets, 0) ,
459 : gage 1847
460 :     ]
461 :     )
462 :     );
463 :    
464 :    
465 :     foreach my $probID (@problemIDs) {
466 :     print CGI::Tr(
467 :     CGI::td( [
468 :     "Prob $probID",
469 : gage 1848 (map { '&le; '.sprintf("%0.0f",$attempts_percentiles_for_problem{$probID}->{$_}) } @brackets, 0),
470 : gage 1847
471 :     ]
472 :     )
473 :     );
474 : gage 1809
475 : gage 1847 }
476 :     print CGI::end_table();
477 :     #####################################################################################
478 :     # construct header
479 :     my $problem_header = '';
480 : gage 1810
481 : gage 1809 foreach my $i (1..$max_num_problems) {
482 : gage 1938 $problem_header .= CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{sort=>"p$i"})},threeSpaceFill($i) );
483 : gage 1590 }
484 :     print
485 : gage 1847 CGI::p("Details"),
486 : gage 1590 defined($sort_method_name) ?"sort method is $sort_method_name":"",
487 : gage 1766 CGI::start_table({-border=>5,style=>'font-size:smaller'}),
488 : gage 1938 CGI::Tr(CGI::td( {-align=>'left'},
489 :     [CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{sort=>'name' })},'Name'),
490 :     CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{sort=>'score'})},'Score'),
491 : gage 1590 'Out'.CGI::br().'Of',
492 : gage 1938 CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{sort=>'index'})},'Ind'),
493 :     'Problems'.CGI::br().$problem_header,
494 :     CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{sort=>'section'})},'Section'),
495 : gage 1590 'Recitation',
496 :     'login_name',
497 :     ])
498 :    
499 :     );
500 :    
501 :     foreach my $rec (@augmentedUserRecords) {
502 :     my $fullName = join("", $rec->{first_name}," ", $rec->{last_name});
503 :     my $email = $rec->{email_address};
504 :     my $twoString = $rec->{twoString};
505 : gage 1430 print CGI::Tr(
506 : gage 1590 CGI::td(CGI::a({-href=>$rec->{act_as_student}},$fullName), CGI::br(), CGI::a({-href=>"mailto:$email"},$email)),
507 :     CGI::td( sprintf("%0.2f",$rec->{score}) ), # score
508 :     CGI::td($rec->{total}), # out of
509 :     CGI::td(sprintf("%0.0f",100*($rec->{index}) )), # indicator
510 :     CGI::td($rec->{problemString}), # problems
511 : gage 1810 CGI::td($self->nbsp($rec->{section})),
512 :     CGI::td($self->nbsp($rec->{recitation})),
513 : gage 1590 CGI::td($rec->{user_id}),
514 : gage 1430
515 :     );
516 :     }
517 : gage 1590
518 : gage 1430 print CGI::end_table();
519 :    
520 :    
521 :    
522 :    
523 :     return "";
524 :     }
525 : gage 1497
526 : gage 1928
527 : gage 1430 #################################
528 :     # Utility function NOT a method
529 :     #################################
530 :     sub threeSpaceFill {
531 : gage 1436 my $num = shift @_ || 0;
532 :    
533 : gage 1938 if (length($num)<=1) {return "$num".'&nbsp;&nbsp;';}
534 :     elsif (length($num)==2) {return "$num".'&nbsp;';}
535 : gage 1856 else {return "## ";}
536 : gage 1430 }
537 :     sub round_score{
538 :     return shift;
539 :     }
540 :     1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9