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

Annotation of /trunk/webwork2/lib/WeBWorK/ContentGenerator/Instructor/Stats.pm

Parent Directory Parent Directory | Revision Log Revision Log


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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9