[system] / branches / rel-2-1-a1 / webwork-modperl / lib / WeBWorK / ContentGenerator / Instructor / StudentProgress.pm Repository:
ViewVC logotype

Annotation of /branches/rel-2-1-a1/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/StudentProgress.pm

Parent Directory Parent Directory | Revision Log Revision Log


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

1 : apizer 2159 ################################################################################
2 :     # WeBWorK Online Homework Delivery System
3 :     # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
4 : glarose 2977 # $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/Instructor/StudentProgress.pm,v 1.4.2.2 2004/07/26 15:11:18 gage Exp $
5 : apizer 2159 #
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::StudentProgress;
18 :     use base qw(WeBWorK::ContentGenerator::Instructor);
19 :    
20 :     =head1 NAME
21 :    
22 :     WeBWorK::ContentGenerator::Instructor::StudentProgress - Display Student Progress.
23 :    
24 :     =cut
25 :    
26 :     use strict;
27 :     use warnings;
28 :     use CGI qw();
29 :     use WeBWorK::Utils qw(readDirectory list2hash max sortByName);
30 :     use WeBWorK::DB::Record::Set;
31 :     use WeBWorK::ContentGenerator::Grades;
32 :     # The table format has been borrowed from the Grades.pm module
33 :     sub initialize {
34 :     my $self = shift;
35 :     # FIXME are there args here?
36 :     my @components = @_;
37 :     my $r = $self->{r};
38 :     my $urlpath = $r->urlpath;
39 :     my $type = $urlpath->arg("statType") || '';
40 :     my $db = $self->{db};
41 :     my $ce = $self->{ce};
42 :     my $authz = $self->{authz};
43 :     my $courseName = $urlpath->arg('courseID');
44 :     my $user = $r->param('user');
45 :    
46 : gage 2536 # Check permissions
47 :     return unless $authz->hasPermissions($user, "access_instructor_tools");
48 :    
49 : apizer 2159 $self->{type} = $type;
50 :     if ($type eq 'student') {
51 :     my $studentName = $r->urlpath->arg("userID") || $user;
52 :     $self->{studentName } = $studentName;
53 :    
54 :     } elsif ($type eq 'set') {
55 :     my $setName = $r->urlpath->arg("setID") || 0;
56 :     $self->{setName} = $setName;
57 :     my $setRecord = $db->getGlobalSet($setName); # checked
58 :     die "global set $setName not found." unless $setRecord;
59 :     $self->{set_due_date} = $setRecord->due_date;
60 :     $self->{setRecord} = $setRecord;
61 :     }
62 :     }
63 :    
64 :    
65 :     sub title {
66 :     my ($self) = @_;
67 : gage 2536 my $r = $self->r;
68 :     my $authz = $r->authz;
69 :     my $user = $r->param('user');
70 :    
71 :     # Check permissions
72 :     return "" unless $authz->hasPermissions($user, "access_instructor_tools");
73 :    
74 : apizer 2159 my $type = $self->{type};
75 :     my $string = "Student Progress for ".$self->{ce}->{courseName}." ";
76 :     if ($type eq 'student') {
77 :     $string .= "student ".$self->{studentName};
78 :     } elsif ($type eq 'set' ) {
79 :     $string .= "set ".$self->{setName};
80 :     $string .= ".    Due ". WeBWorK::Utils::formatDateTime($self->{set_due_date});
81 :     }
82 :     return $string;
83 :     }
84 :     sub siblings {
85 :     my ($self) = @_;
86 :     my $r = $self->r;
87 :     my $db = $r->db;
88 : gage 2536 my $authz = $r->authz;
89 :     my $user = $r->param('user');
90 : apizer 2159 my $urlpath = $r->urlpath;
91 :    
92 : gage 2536 # Check permissions
93 :     return "" unless $authz->hasPermissions($user, "access_instructor_tools");
94 : apizer 2159
95 :     my $courseID = $urlpath->arg("courseID");
96 :     my $eUserID = $r->param("effectiveUser");
97 :     my @setIDs = sort $db->listGlobalSets;
98 :    
99 : apizer 2169 my $progress = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::StudentProgress",
100 : apizer 2159 courseID => $courseID);
101 :    
102 :     print CGI::start_ul({class=>"LinksMenu"});
103 :     print CGI::start_li();
104 : apizer 2169 print CGI::span({style=>"font-size:larger"}, CGI::a({href=>$self->systemLink($progress)}, 'Student Progress'));
105 : apizer 2159 print CGI::start_ul();
106 :    
107 :     foreach my $setID (@setIDs) {
108 : apizer 2169 my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::StudentProgress",
109 : apizer 2159 courseID => $courseID, setID => $setID,statType => 'set',);
110 :     print CGI::li(CGI::a({href=>$self->systemLink($problemPage)}, "Set $setID"));
111 :     }
112 :    
113 :     print CGI::end_ul();
114 :     print CGI::end_li();
115 :     print CGI::end_ul();
116 :    
117 :     return "";
118 :     }
119 :     sub body {
120 :     my $self = shift;
121 :     my $r = $self->r;
122 :     my $urlpath = $r->urlpath;
123 :     my $db = $r->db;
124 :     my $ce = $r->ce;
125 :     my $authz = $r->authz;
126 : gage 2536 my $user = $r->param('user');
127 : apizer 2159 my $courseName = $urlpath->arg("courseID");
128 :     my $type = $self->{type};
129 : gage 2536
130 :     # Check permissions
131 :     return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to access instructor tools"))
132 :     unless $authz->hasPermissions($user, "access_instructor_tools");
133 : apizer 2159
134 :     if ($type eq 'student') {
135 :     my $studentName = $self->{studentName};
136 :    
137 :     my $studentRecord = $db->getUser($studentName); # checked
138 :     die "record for user $studentName not found" unless $studentRecord;
139 :    
140 :     my $fullName = join("", $studentRecord->first_name," ", $studentRecord->last_name);
141 :    
142 :     my $courseHomePage = $urlpath->new(type => 'set_list',
143 :     args => {courseID => $courseName}
144 :     );
145 :     my $act_as_student_url = $self->systemLink($courseHomePage,
146 :     params => { effectiveUser => $studentName }
147 :     );
148 :    
149 :     my $email = $studentRecord->email_address;
150 :     print
151 :     CGI::a({-href=>"mailto:$email"},$email),CGI::br(),
152 :     "Section: ", $studentRecord->section, CGI::br(),
153 :     "Recitation: ", $studentRecord->recitation,CGI::br(),
154 :     'Act as: ',
155 :     CGI::a({-href=>$act_as_student_url},$studentRecord->user_id);
156 :     WeBWorK::ContentGenerator::Grades::displayStudentStats($self,$studentName);
157 :    
158 :     # The table format has been borrowed from the Grades.pm module
159 :     } elsif( $type eq 'set') {
160 :     my $setName = $self->{setName};
161 :     $self->displaySets($self->{setName});
162 :     } elsif ($type eq '') {
163 :    
164 :     $self->index;
165 :     } else {
166 :     warn "Don't recognize statistics display type: |$type|";
167 :    
168 :     }
169 :    
170 :    
171 :     return '';
172 :    
173 :     }
174 :     sub index {
175 :     my $self = shift;
176 :     my $r = $self->r;
177 :     my $urlpath = $r->urlpath;
178 :     my $ce = $r->ce;
179 :     my $db = $r->db;
180 :     my $courseName = $urlpath->arg("courseID");
181 :    
182 :     my @studentList = sort $db->listUsers;
183 :     my @setList = sort $db->listGlobalSets;
184 : mschmitt 2204
185 :     ## Edit to filter out students you aren't allowed to see
186 :     #
187 :     my @myUsers;
188 :     my @studentRecords = $db->getUsers;
189 :     my $user = $r->param("user");
190 : apizer 2159
191 : mschmitt 2204 my (@viewable_sections, @viewable_recitations);
192 :     if (defined @{$ce->{viewable_sections}->{$user}})
193 :     {@viewable_sections = @{$ce->{viewable_sections}->{$user}};}
194 :     if (defined @{$ce->{viewable_recitations}->{$user}})
195 :     {@viewable_recitations = @{$ce->{viewable_recitations}->{$user}};}
196 :     if (@viewable_sections or @viewable_recitations){
197 :     foreach my $studentL (@studentList){
198 :     my $keep = 0;
199 :     my $student = $db->getUser($studentL);
200 :     foreach my $sec (@viewable_sections){
201 :     if ($student->section() eq $sec){$keep = 1; last;}
202 :     }
203 :     foreach my $rec (@viewable_recitations){
204 :     if ($student->recitation() eq $rec){$keep = 1; last;}
205 :     }
206 :     if ($keep) {push @myUsers, $studentL;}
207 :     }
208 :     # @studentList = @myUsers;
209 :     }
210 :     else {@myUsers = @studentList;}
211 : apizer 2159 my @setLinks = ();
212 :     my @studentLinks = ();
213 :     foreach my $set (@setList) {
214 :     my $setStatisticsPage = $urlpath->newFromModule($urlpath->module,
215 :     courseID => $courseName,
216 :     statType => 'set',
217 :     setID => $set
218 :     );
219 :     push @setLinks, CGI::a({-href=>$self->systemLink($setStatisticsPage) },"set $set" );
220 :     }
221 :    
222 : mschmitt 2204 foreach my $student (@myUsers) {
223 : apizer 2159 my $userStatisticsPage = $urlpath->newFromModule($urlpath->module,
224 :     courseID => $courseName,
225 :     statType => 'student',
226 :     userID => $student
227 :     );
228 :     push @studentLinks, CGI::a({-href=>$self->systemLink($userStatisticsPage,
229 :     prams=>{effectiveUser => $student}
230 :     )}," $student" ),;
231 :     }
232 :     print join("",
233 :     CGI::start_table({-border=>2, -cellpadding=>20}),
234 :     CGI::Tr(
235 :     CGI::td({-valign=>'top'},
236 :     CGI::h3({-align=>'center'},'View student progress by set'),
237 :     CGI::ul( CGI::li( [@setLinks] ) ),
238 :     ),
239 :     CGI::td({-valign=>'top'},
240 :     CGI::h3({-align=>'center'},'View student progress by student'),
241 :     CGI::ul(CGI::li( [ @studentLinks ] ) ),
242 :     ),
243 :     ),
244 :     CGI::end_table(),
245 :     );
246 :    
247 :     }
248 :     ###################################################
249 :     sub displaySets {
250 :     my $self = shift;
251 :     my $r = $self->r;
252 :     my $urlpath = $r->urlpath;
253 :     my $db = $r->db;
254 :     my $ce = $r->ce;
255 :     my $authz = $r->authz;
256 :     my $courseName = $urlpath->arg("courseID");
257 :     my $setName = $urlpath->arg("setID");
258 :     my $user = $r->param('user');
259 :     my $setRecord = $self->{setRecord};
260 :     my $root = $ce->{webworkURLs}->{root};
261 :    
262 :     my $setStatsPage = $urlpath->newFromModule($urlpath->module,courseID=>$courseName,statType=>'sets',setID=>$setName);
263 :     my $sort_method_name = $r->param('sort');
264 :     my @studentList = $db->listUsers;
265 : glarose 3130
266 :     # another versioning/gateway change. in many cases we don't want or need
267 :     # all of the columns that are put in here by default, so we add a set of
268 :     # flags for which columns to show. for versioned sets we may also want to
269 :     # only see the best score, so we include that as an option also.
270 :     # defaults:
271 :     my %showColumns = ( 'name' => 1, 'score' => 1, 'outof' => 1,
272 : glarose 3275 'date' => 0, 'testtime' => 1, 'index' => 1,
273 :     'problems' => 1, 'section' => 1, 'recit' => 1,
274 :     'login' => 1 );
275 : glarose 3130 my $showBestOnly = 0;
276 : apizer 2159
277 :     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 :     my %number_of_attempts_for_problem = (); # the total number of attempst for this problem (sum of above list)
281 :     my %number_of_students_attempting_problem = (); # the number of students attempting this problem.
282 :     my %correct_answers_for_problem = (); # the number of students correctly answering this problem (partial correctness allowed)
283 :     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 : apizer 2197 return $a->{recitation} cmp $b->{recitation} if $sort_method_name eq 'recitation';
290 :     return $a->{user_id} cmp $b->{user_id} if $sort_method_name eq 'user_id';
291 : apizer 2159 if ($sort_method_name =~/p(\d+)/) {
292 :     my $left = $b->{problemData}->{$1} ||0;
293 :     my $right = $a->{problemData}->{$1} ||0;
294 :     return $left <=> $right; # sort by number of attempts.
295 :     }
296 :    
297 :     };
298 :    
299 : glarose 2977 # get versioning information
300 :     my $GlobalSet = $db->getGlobalSet($setName);
301 :     my $setIsVersioned =
302 :     ( defined($GlobalSet->assignment_type()) &&
303 :     $GlobalSet->assignment_type() =~ /gateway/ ) ? 1 : 0;
304 :    
305 : glarose 3130 # reset column view options based on whether the set is versioned and, if so,
306 :     # input parameters
307 :     if ( $setIsVersioned ) {
308 :     # the returning parameter lets us set defaults for versioned sets
309 :     my $ret = $r->param('returning');
310 :     $showColumns{'date'} = $ret ? $r->param('show_date') : 1;
311 : glarose 3278 $showColumns{'testtime'} = $ret ? $r->param('show_testtime') : 1;
312 : glarose 3130 $showColumns{'index'} = $ret ? $r->param('show_index') : 0;
313 :     $showColumns{'problems'} = $ret ? $r->param('show_problems') : 0;
314 :     $showColumns{'section'} = $ret? $r->param('show_section') : 0;
315 :     $showColumns{'recit'} = $ret ? $r->param('show_recitation') : 0;
316 :     $showColumns{'login'} = $ret ? $r->param('show_login') : 0;
317 :     $showBestOnly = $ret ? $r->param('show_best_only') : 0;
318 :     }
319 :    
320 : apizer 2159 ###############################################################
321 :     # Print tables
322 :     ###############################################################
323 :    
324 :     my $max_num_problems = 0;
325 :     # get user records
326 :     $WeBWorK::timer->continue("Begin obtaining user records for set $setName") if defined($WeBWorK::timer);
327 :     my @userRecords = $db->getUsers(@studentList);
328 :     $WeBWorK::timer->continue("End obtaining user records for set $setName") if defined($WeBWorK::timer);
329 :     $WeBWorK::timer->continue("begin main loop") if defined($WeBWorK::timer);
330 :     my @augmentedUserRecords = ();
331 :     my $number_of_active_students;
332 : mschmitt 2204
333 :     ## Edit to filter out students
334 :     #
335 :     my @myUsers;
336 :     my $ActiveUser = $r->param("user");
337 :     my (@viewable_sections, @viewable_recitations);
338 :     if (defined @{$ce->{viewable_sections}->{$user}})
339 :     {@viewable_sections = @{$ce->{viewable_sections}->{$user}};}
340 :     if (defined @{$ce->{viewable_recitations}->{$user}})
341 :     {@viewable_recitations = @{$ce->{viewable_recitations}->{$user}};}
342 :     if (@viewable_sections or @viewable_recitations){
343 :     foreach my $student (@userRecords){
344 :     my $keep = 0;
345 :     foreach my $sec (@viewable_sections){
346 :     if ($student->section() eq $sec){$keep = 1; last;}
347 :     }
348 :     foreach my $rec (@viewable_recitations){
349 :     if ($student->recitation() eq $rec){$keep = 1; last;}
350 :     }
351 :     if ($keep) {push @myUsers, $student;}
352 :     }
353 :     }
354 :     else {@myUsers = @userRecords;}
355 : glarose 3130
356 : mschmitt 2204 foreach my $studentRecord (@myUsers) {
357 : apizer 2159 next unless ref($studentRecord);
358 :     my $student = $studentRecord->user_id;
359 :     next if $studentRecord->last_name =~/^practice/i; # don't show practice users
360 :     next if $studentRecord->status !~/C/; # don't show dropped students FIXME
361 :     $number_of_active_students++;
362 : glarose 2977
363 :     # build list of versioned sets for this student user
364 :     my @allSetNames = ();
365 :     if ( $setIsVersioned ) {
366 :     my $numVersions = $db->getUserSetVersionNumber($student,$setName);
367 :     for ( my $i=1; $i<=$numVersions; $i++ ) {
368 :     $allSetNames[$i-1] = "$setName,v$i";
369 :     }
370 :     } else {
371 :     @allSetNames = ( "$setName" );
372 :     }
373 :    
374 : glarose 3130 # for versioned sets, we might be keeping only the high score
375 :     my $maxScore = -1;
376 :     my $max_hash = {};
377 :     # make this global to the student loop
378 :     my $act_as_student_url = '';
379 :    
380 : glarose 2977 foreach my $sN ( @allSetNames ) {
381 :    
382 : apizer 2159 my $status = 0;
383 :     my $attempted = 0;
384 :     my $longStatus = '';
385 :     my $string = '';
386 :     my $twoString = '';
387 :     my $totalRight = 0;
388 :     my $total = 0;
389 :     my $num_of_attempts = 0;
390 :     my %h_problemData = ();
391 :     my $probNum = 0;
392 :    
393 :     $WeBWorK::timer->continue("Begin obtaining problem records for user $student set $setName") if defined($WeBWorK::timer);
394 :    
395 : glarose 2977 my @problemRecords = sort {$a->problem_id <=> $b->problem_id } $db->getAllUserProblems( $student, $sN );
396 :     $WeBWorK::timer->continue("End obtaining problem records for user $student set $sN") if defined($WeBWorK::timer);
397 : apizer 2159 my $num_of_problems = @problemRecords;
398 :     $max_num_problems = ($max_num_problems>= $num_of_problems) ? $max_num_problems : $num_of_problems;
399 :    
400 :     foreach my $problemRecord (@problemRecords) {
401 :     next unless ref($problemRecord);
402 :     my $probID = $problemRecord->problem_id;
403 :    
404 :     my $valid_status = 0;
405 :     unless (defined($problemRecord) ){
406 : glarose 2977 # warn "Can't find record for problem $prob in set $sN for $student";
407 : apizer 2159 # FIXME check the legitimate reasons why a student record might not be defined
408 :     next;
409 :     }
410 :     $status = $problemRecord->status || 0;
411 :     $attempted = $problemRecord->attempted;
412 :     if (!$attempted){
413 :     $longStatus = '. ';
414 :     }
415 :     elsif ($status >= 0 and $status <=1 ) {
416 :     $valid_status = 1;
417 :     $longStatus = int(100*$status+.5);
418 :     if ($longStatus == 100) {
419 :     $longStatus = 'C ';
420 :     }
421 :     else {
422 : glarose 3130 # change here to give a more reasonable output for gateways,
423 :     # and other versioned sets with only one attempt per version
424 :     # FIXME this isn't good in general, when versioned sets might
425 :     # FIXME allow multiple submissions
426 :     $longStatus = ($setIsVersioned)?'X ':
427 :     &threeSpaceFill($longStatus);
428 : apizer 2159 }
429 :     }
430 :     else {
431 :     $longStatus = 'X ';
432 :     }
433 :    
434 :     my $num_correct = $problemRecord->num_correct || 0;
435 :     my $num_incorrect = $problemRecord->num_incorrect || 0;
436 :     # It's possible that $incorrect is an empty or blank string instead of 0 the || clause fixes this and prevents
437 :     # warning messages in the comparison below.
438 :     $string .= $longStatus;
439 :     $twoString .= threeSpaceFill($num_incorrect);
440 :     my $probValue = $problemRecord->value;
441 : gage 2536 $probValue = 1 unless defined($probValue) and $probValue ne ""; # FIXME?? set defaults here?
442 : apizer 2159 $total += $probValue;
443 :     $totalRight += round_score($status*$probValue) if $valid_status;
444 :    
445 :     $num_of_attempts += $num_correct + $num_incorrect;
446 :    
447 :     $h_problemData{$probID} = $num_incorrect;
448 :    
449 :     $correct_answers_for_problem{$probID} = 0 unless defined($correct_answers_for_problem{$probID});
450 :     # add on the scores for this problem
451 :     if (defined($attempted) and $attempted) {
452 :     $number_of_students_attempting_problem{$probID}++;
453 :     push( @{ $attempts_list_for_problem{$probID} } , $num_of_attempts);
454 :     $number_of_attempts_for_problem{$probID} += $num_of_attempts;
455 :     $correct_answers_for_problem{$probID} += $status;
456 :     }
457 :    
458 :     }
459 : glarose 3130
460 : glarose 3275 # for versioned tests we might be displaying the test date and test time
461 : glarose 3130 my $dateOfTest = '';
462 : glarose 3275 my $testTime = '';
463 : glarose 3130 # annoyingly, this is a set property, so get the set
464 : glarose 3275 if ( $setIsVersioned &&
465 :     ( $showColumns{'date'} || $showColumns{'testtime'} ) ) {
466 : glarose 3130 my @userSet =
467 :     $db->getMergedVersionedSets( [ $studentRecord->user_id, $setName, $sN ] );
468 :     if ( defined( $userSet[0] ) ) { # if this isn't defined, something's wrong
469 : glarose 3275 $dateOfTest =
470 :     localtime( $userSet[0]->version_creation_time() );
471 :     my $gradeTime = '';
472 :     if ( defined( $userSet[0]->version_last_attempt_time() ) &&
473 :     $userSet[0]->version_last_attempt_time() ) {
474 :     $testTime = ( $userSet[0]->version_last_attempt_time() -
475 :     $userSet[0]->version_creation_time() ) /
476 :     60;
477 :     $testTime = sprintf("%3.1f min", $testTime);
478 :     } else {
479 :     $testTime = 'time limit exceeded';
480 :     }
481 : glarose 3130 } else {
482 :     $dateOfTest = '???';
483 : glarose 3275 $testTime = '???';
484 : glarose 3130 }
485 :     }
486 : apizer 2159
487 : glarose 3130 $act_as_student_url = $self->systemLink($urlpath->new(type=>'set_list',args=>{courseID=>$courseName}),
488 : apizer 2159 params=>{effectiveUser => $studentRecord->user_id}
489 :     );
490 : glarose 3130 # update for versioned sets
491 :     # if ( $setIsVersioned ) {
492 :     # $act_as_student_url =~ s/\/$courseName/\/$courseName\/quiz_mode/;
493 :     # }
494 :    
495 : apizer 2159 my $email = $studentRecord->email_address;
496 :     # FIXME this needs formatting
497 : glarose 3130
498 :     # change to give better output for gateways
499 :     # FIXME this assumes that all versioned sets only allow one attempt
500 :     # FIXME per version, which is the case for gateways but probably not others
501 :     my $longtwo = ( $setIsVersioned ) ? $string :
502 :     "$string\n$twoString";
503 : apizer 2159
504 :     my $avg_num_attempts = ($num_of_problems) ? $num_of_attempts/$num_of_problems : 0;
505 :     my $successIndicator = ($avg_num_attempts) ? ($totalRight/$total)**2/$avg_num_attempts : 0 ;
506 :     my $temp_hash = { user_id => $studentRecord->user_id,
507 :     last_name => $studentRecord->last_name,
508 :     first_name => $studentRecord->first_name,
509 :     score => $totalRight,
510 :     total => $total,
511 :     index => $successIndicator,
512 :     section => $studentRecord->section,
513 :     recitation => $studentRecord->recitation,
514 : glarose 3130 problemString => "<pre>$longtwo</pre>",
515 : apizer 2159 act_as_student => $act_as_student_url,
516 :     email_address => $studentRecord->email_address,
517 :     problemData => {%h_problemData},
518 : glarose 3130 date => $dateOfTest,
519 : glarose 3275 testtime => $testTime,
520 : apizer 2159 };
521 : glarose 3130
522 :     # keep track of best score
523 :     if ( $totalRight > $maxScore ) {
524 :     $maxScore = $totalRight;
525 :     $max_hash = { %$temp_hash };
526 :     }
527 :    
528 :     # if we're showing all records, add it in to the list
529 :     if ( ! $showBestOnly ) {
530 : apizer 2159 # add this data to the list of total scores (out of 100)
531 :     # add this data to the list of success indices.
532 :     push( @index_list, $temp_hash->{index});
533 :     push( @score_list, ($temp_hash->{total}) ?$temp_hash->{score}/$temp_hash->{total} : 0 ) ;
534 :     push( @augmentedUserRecords, $temp_hash );
535 : glarose 2977 }
536 : glarose 3130 }
537 :     # if we're showing only the best score, add the best score now
538 :     if ( $showBestOnly ) {
539 :     if ( ! %$max_hash ) { # then we have no tests---e.g., for proctors
540 :     next; # if we could exclude proctors, etc., we
541 :     # might want to keep these:
542 :     $max_hash = { user_id => $studentRecord->user_id(),
543 :     last_name => $studentRecord->last_name(),
544 :     first_name => $studentRecord->first_name(),
545 :     score => 0,
546 :     total => 'n/a',
547 :     index => 0,
548 :     section => $studentRecord->section(),
549 :     recitation => $studentRecord->recitation(),
550 :     problemString => 'no attempt recorded',
551 :     act_as_student => $act_as_student_url,
552 :     email_address => $studentRecord->email_address(),
553 :     problemData => {},
554 :     date => 'none',
555 :     }
556 :     }
557 :     push( @index_list, $max_hash->{index} );
558 :     push( @score_list,
559 :     ($max_hash->{total} && $max_hash->{total} ne 'n/a') ?
560 :     $max_hash->{score}/$max_hash->{total} : 0 );
561 :     push( @augmentedUserRecords, $max_hash );
562 :     }
563 :    
564 :     }
565 :    
566 : apizer 2159 $WeBWorK::timer->continue("end mainloop") if defined($WeBWorK::timer);
567 :    
568 :     @augmentedUserRecords = sort { &$sort_method($a,$b)
569 :     ||
570 :     lc($a->{last_name}) cmp lc($b->{last_name} ) } @augmentedUserRecords;
571 :    
572 :     #####################################################################################
573 :     # construct header
574 :     my $problem_header = '';
575 : apizer 2169
576 : glarose 3130 # changes for gateways/versioned sets here. in this case we allow instructors
577 :     # to modify the appearance of output, which we do with a form. so paste in the
578 :     # form header here, and make appropriate modifications
579 :     my $verSelectors = '';
580 :     if ( $setIsVersioned ) {
581 :     print CGI::start_form({'method' => 'post',
582 :     'action' => $self->systemLink($urlpath,
583 :     authen=>0),
584 :     'name' => 'StudentProgress'});
585 :     print $self->hidden_authen_fields();
586 :    
587 :     $verSelectors = CGI::p({'style'=>'background-color:#eeeeee;color:black;'},
588 :     "Display options: Show ",
589 :     CGI::hidden(-name=>'returning', -value=>'1'),
590 :     CGI::checkbox(-name=>'show_best_only', -value=>'1',
591 :     -checked=>$showBestOnly,
592 :     -label=>' only best scores; '),
593 :     CGI::checkbox(-name=>'show_index', -value=>'1',
594 :     -checked=>$showColumns{'index'},
595 :     -label=>' success indicator; '),
596 :     CGI::checkbox(-name=>'show_date', -value=>'1',
597 :     -checked=>$showColumns{'date'},
598 :     -label=>' test date; '),
599 : glarose 3275 CGI::checkbox(-name=>'show_testtime', -value=>'1',
600 :     -checked=>$showColumns{'testtime'},
601 :     -label=>' test time; '),
602 : glarose 3130 CGI::checkbox(-name=>'show_problems', -value=>'1',
603 :     -checked=>$showColumns{'problems'},
604 :     -label=>'problems;'), "\n", CGI::br(), "\n",
605 :     CGI::checkbox(-name=>'show_section', -value=>'1',
606 :     -checked=>$showColumns{'section'},
607 :     -label=>' section #; '),
608 :     CGI::checkbox(-name=>'show_recitation', -value=>'1',
609 :     -checked=>$showColumns{'recit'},
610 :     -label=>' recitation #; '),
611 :     CGI::checkbox(-name=>'show_login', -value=>'1',
612 :     -checked=>$showColumns{'login'},
613 :     -label=>'login'), "\n", CGI::br(), "\n",
614 :     CGI::submit(-value=>'Update Display')
615 :     );
616 :     }
617 :    
618 :     # set up a column header variable to allow us to drop certain columns
619 :     # for versioned set viewing
620 :     my @columnHdrs = ();
621 :     # define this for use on all of the sorts
622 : glarose 3131 my %paramList = ();
623 :     if ( $setIsVersioned ) {
624 :     my $rtng = (defined( $r->param('returning') )) ?
625 :     $r->param('returning') : 0;
626 :     %paramList = ( 'returning' => $rtng,
627 :     'show_best_only' => $showBestOnly,
628 :     'show_index' => $showColumns{'index'},
629 :     'show_date' => $showColumns{'date'},
630 : glarose 3275 'show_testtime' => $showColumns{'testtime'},
631 : glarose 3131 'show_problems' => $showColumns{'problems'},
632 :     'show_section' => $showColumns{'section'},
633 :     'show_recitation' => $showColumns{'recit'},
634 :     'show_login' => $showColumns{'login'} );
635 :     }
636 :     push(@columnHdrs, (! defined($sort_method_name) || ($sort_method_name ne 'name' && $sort_method_name ne '')) ? CGI::a({'href'=>$self->systemLink($setStatsPage,params=>{'sort'=>'name', %paramList})},'Name') : 'Name') if ( $showColumns{'name'} );
637 :     push(@columnHdrs, (! defined($sort_method_name) || $sort_method_name ne 'score') ? CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{'sort'=>'score', %paramList})},'Score') : 'Score') if ( $showColumns{'score'} );
638 : glarose 3130 push(@columnHdrs, 'Out'.CGI::br().'Of') if ( $showColumns{'outof'} );
639 :     push(@columnHdrs, 'Date') if ( $showColumns{'date'} );
640 : glarose 3275 push(@columnHdrs, 'TestTime') if ( $showColumns{'testtime'} );
641 : glarose 3131 push(@columnHdrs, (! defined($sort_method_name) || $sort_method_name ne 'index') ? CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{'sort'=>'index', %paramList})},'Ind') : 'Ind') if ( $showColumns{'index'} );
642 : glarose 3130 push(@columnHdrs, 'Problems'.CGI::br().$problem_header) if ( $showColumns{'problems'} );
643 : glarose 3131 push(@columnHdrs, (! defined($sort_method_name) || $sort_method_name ne 'section') ? CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{'sort'=>'section', %paramList})},'Section') : 'Section') if ( $showColumns{'section'} );
644 :     push(@columnHdrs, (! defined($sort_method_name) || $sort_method_name ne 'recitation') ? CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{'sort'=>'recitation', %paramList})},'Recitation'):'Recitation') if ( $showColumns{'recit'} );
645 :     push(@columnHdrs, (! defined($sort_method_name) || $sort_method_name ne 'user_id') ? CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{'sort'=>'user_id', %paramList})},'Login Name'):'Login Name') if ( $showColumns{'login'} );
646 : glarose 3130
647 : apizer 2159 print
648 : apizer 2169 CGI::br(),
649 :     CGI::br(),
650 :     CGI::p('A period (.) indicates a problem has not been attempted, a &quot;C&quot; indicates
651 :     a problem has been answered 100% correctly, and a number from 0 to 99
652 :     indicates the percentage of partial credit earned. The number on the
653 :     second line gives the number of incorrect attempts. The success indicator,'
654 :     ,CGI::i('Ind'),', for each student is calculated as',
655 :     CGI::br(),
656 :     '100*(totalNumberOfCorrectProblems / totalNumberOfProblems)^2 / (AvgNumberOfAttemptsPerProblem)',CGI::br(),
657 :     'or 0 if there are no attempts.'
658 : apizer 2159 ),
659 : apizer 2169 CGI::br(),
660 :     "Click on student's name to see the student's version of the problem set;
661 :     Click heading to sort table. ",
662 :     CGI::br(),
663 : glarose 3130 $verSelectors,
664 : apizer 2169 CGI::br(),
665 :     defined($sort_method_name) ?" sort method is $sort_method_name":"",
666 : apizer 2159 CGI::start_table({-border=>5,style=>'font-size:smaller'}),
667 : glarose 3130 CGI::Tr(CGI::td( {-align=>'left'}, [ @columnHdrs ]));
668 : glarose 2977
669 :     my $prevFullName = '';
670 :     my $vNum = 1;
671 : apizer 2159 foreach my $rec (@augmentedUserRecords) {
672 :     my $fullName = join("", $rec->{first_name}," ", $rec->{last_name});
673 :     my $email = $rec->{email_address};
674 :     my $twoString = $rec->{twoString};
675 : glarose 2977
676 :     # revision to make versioned sets' format nicer
677 :     my $nameEntry = '';
678 :     if ( $fullName eq $prevFullName ) {
679 :     $vNum++;
680 : glarose 3130 $nameEntry = CGI::span({-style=>"text-align:right;"},
681 :     "(v$vNum)");
682 : glarose 2977 } else {
683 : glarose 3130 $nameEntry =
684 :     CGI::a({-href=>$rec->{act_as_student}},$fullName) .
685 :     ($setIsVersioned && ! $showBestOnly ? ' (v1)' : ' ') .
686 :     CGI::br() . CGI::a({-href=>"mailto:$email"},$email);
687 : glarose 2977 $vNum = 1;
688 :     $prevFullName = $fullName;
689 :     }
690 : glarose 3130 # build set of columns that we want
691 :     my @cols = ();
692 :     push(@cols, $nameEntry) if ($showColumns{'name'});
693 :     push(@cols, sprintf("%0.2f",$rec->{score})) if ($showColumns{'score'});
694 :     push(@cols, $rec->{total}) if ( $showColumns{'outof'} );
695 :     push(@cols, $self->nbsp($rec->{date})) if ($showColumns{'date'});
696 : glarose 3275 push(@cols, $self->nbsp($rec->{testtime})) if ($showColumns{'testtime'});
697 : glarose 3130 push(@cols, sprintf("%0.0f",100*($rec->{index}))) if ($showColumns{'index'});
698 :     push(@cols, $rec->{problemString}) if ($showColumns{'problems'});
699 :     push(@cols, $self->nbsp($rec->{section})) if ($showColumns{'section'});
700 :     push(@cols, $self->nbsp($rec->{recitation})) if ($showColumns{'recit'});
701 :     push(@cols, $rec->{user_id}) if ($showColumns{'login'});
702 : glarose 2977
703 : glarose 3130 print CGI::Tr( CGI::td( [ @cols ] ) );
704 : glarose 2977 # CGI::td(CGI::a({-href=>$rec->{act_as_student}},$fullName), CGI::br(), CGI::a({-href=>"mailto:$email"},$email)),
705 : glarose 3130
706 : apizer 2159 }
707 :    
708 :     print CGI::end_table();
709 :    
710 :    
711 :    
712 :    
713 :     return "";
714 :     }
715 :    
716 :    
717 :     #################################
718 :     # Utility function NOT a method
719 :     #################################
720 :     sub threeSpaceFill {
721 :     my $num = shift @_ || 0;
722 :    
723 :     if (length($num)<=1) {return "$num".'&nbsp;&nbsp;';}
724 :     elsif (length($num)==2) {return "$num".'&nbsp;';}
725 :     else {return "## ";}
726 :     }
727 :     sub round_score{
728 :     return shift;
729 :     }
730 :     1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9