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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2778 - (view) (download) (as text)
Original Path: trunk/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/Stats.pm

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9