[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 3131 - (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 :     'date' => 0, 'index' => 1, 'problems' => 1,
273 :     'section' => 1, 'recit' => 1, 'login' => 1 );
274 :     my $showBestOnly = 0;
275 : apizer 2159
276 :     my @index_list = (); # list of all student index
277 :     my @score_list = (); # list of all student total percentage scores
278 :     my %attempts_list_for_problem = (); # a list of the number of attempts for each problem
279 :     my %number_of_attempts_for_problem = (); # the total number of attempst for this problem (sum of above list)
280 :     my %number_of_students_attempting_problem = (); # the number of students attempting this problem.
281 :     my %correct_answers_for_problem = (); # the number of students correctly answering this problem (partial correctness allowed)
282 :     my $sort_method = sub {
283 :     my ($a,$b) = @_;
284 :     return 0 unless defined($sort_method_name);
285 :     return $b->{score} <=> $a->{score} if $sort_method_name eq 'score';
286 :     return $b->{index} <=> $a->{index} if $sort_method_name eq 'index';
287 :     return $a->{section} cmp $b->{section} if $sort_method_name eq 'section';
288 : apizer 2197 return $a->{recitation} cmp $b->{recitation} if $sort_method_name eq 'recitation';
289 :     return $a->{user_id} cmp $b->{user_id} if $sort_method_name eq 'user_id';
290 : apizer 2159 if ($sort_method_name =~/p(\d+)/) {
291 :     my $left = $b->{problemData}->{$1} ||0;
292 :     my $right = $a->{problemData}->{$1} ||0;
293 :     return $left <=> $right; # sort by number of attempts.
294 :     }
295 :    
296 :     };
297 :    
298 : glarose 2977 # get versioning information
299 :     my $GlobalSet = $db->getGlobalSet($setName);
300 :     my $setIsVersioned =
301 :     ( defined($GlobalSet->assignment_type()) &&
302 :     $GlobalSet->assignment_type() =~ /gateway/ ) ? 1 : 0;
303 :    
304 : glarose 3130 # reset column view options based on whether the set is versioned and, if so,
305 :     # input parameters
306 :     if ( $setIsVersioned ) {
307 :     # the returning parameter lets us set defaults for versioned sets
308 :     my $ret = $r->param('returning');
309 :     $showColumns{'date'} = $ret ? $r->param('show_date') : 1;
310 :     $showColumns{'index'} = $ret ? $r->param('show_index') : 0;
311 :     $showColumns{'problems'} = $ret ? $r->param('show_problems') : 0;
312 :     $showColumns{'section'} = $ret? $r->param('show_section') : 0;
313 :     $showColumns{'recit'} = $ret ? $r->param('show_recitation') : 0;
314 :     $showColumns{'login'} = $ret ? $r->param('show_login') : 0;
315 :     $showBestOnly = $ret ? $r->param('show_best_only') : 0;
316 :     }
317 :    
318 : apizer 2159 ###############################################################
319 :     # Print tables
320 :     ###############################################################
321 :    
322 :     my $max_num_problems = 0;
323 :     # get user records
324 :     $WeBWorK::timer->continue("Begin obtaining user records for set $setName") if defined($WeBWorK::timer);
325 :     my @userRecords = $db->getUsers(@studentList);
326 :     $WeBWorK::timer->continue("End obtaining user records for set $setName") if defined($WeBWorK::timer);
327 :     $WeBWorK::timer->continue("begin main loop") if defined($WeBWorK::timer);
328 :     my @augmentedUserRecords = ();
329 :     my $number_of_active_students;
330 : mschmitt 2204
331 :     ## Edit to filter out students
332 :     #
333 :     my @myUsers;
334 :     my $ActiveUser = $r->param("user");
335 :     my (@viewable_sections, @viewable_recitations);
336 :     if (defined @{$ce->{viewable_sections}->{$user}})
337 :     {@viewable_sections = @{$ce->{viewable_sections}->{$user}};}
338 :     if (defined @{$ce->{viewable_recitations}->{$user}})
339 :     {@viewable_recitations = @{$ce->{viewable_recitations}->{$user}};}
340 :     if (@viewable_sections or @viewable_recitations){
341 :     foreach my $student (@userRecords){
342 :     my $keep = 0;
343 :     foreach my $sec (@viewable_sections){
344 :     if ($student->section() eq $sec){$keep = 1; last;}
345 :     }
346 :     foreach my $rec (@viewable_recitations){
347 :     if ($student->recitation() eq $rec){$keep = 1; last;}
348 :     }
349 :     if ($keep) {push @myUsers, $student;}
350 :     }
351 :     }
352 :     else {@myUsers = @userRecords;}
353 : glarose 3130
354 : mschmitt 2204 foreach my $studentRecord (@myUsers) {
355 : apizer 2159 next unless ref($studentRecord);
356 :     my $student = $studentRecord->user_id;
357 :     next if $studentRecord->last_name =~/^practice/i; # don't show practice users
358 :     next if $studentRecord->status !~/C/; # don't show dropped students FIXME
359 :     $number_of_active_students++;
360 : glarose 2977
361 :     # build list of versioned sets for this student user
362 :     my @allSetNames = ();
363 :     if ( $setIsVersioned ) {
364 :     my $numVersions = $db->getUserSetVersionNumber($student,$setName);
365 :     for ( my $i=1; $i<=$numVersions; $i++ ) {
366 :     $allSetNames[$i-1] = "$setName,v$i";
367 :     }
368 :     } else {
369 :     @allSetNames = ( "$setName" );
370 :     }
371 :    
372 : glarose 3130 # for versioned sets, we might be keeping only the high score
373 :     my $maxScore = -1;
374 :     my $max_hash = {};
375 :     # make this global to the student loop
376 :     my $act_as_student_url = '';
377 :    
378 : glarose 2977 foreach my $sN ( @allSetNames ) {
379 :    
380 : apizer 2159 my $status = 0;
381 :     my $attempted = 0;
382 :     my $longStatus = '';
383 :     my $string = '';
384 :     my $twoString = '';
385 :     my $totalRight = 0;
386 :     my $total = 0;
387 :     my $num_of_attempts = 0;
388 :     my %h_problemData = ();
389 :     my $probNum = 0;
390 :    
391 :     $WeBWorK::timer->continue("Begin obtaining problem records for user $student set $setName") if defined($WeBWorK::timer);
392 :    
393 : glarose 2977 my @problemRecords = sort {$a->problem_id <=> $b->problem_id } $db->getAllUserProblems( $student, $sN );
394 :     $WeBWorK::timer->continue("End obtaining problem records for user $student set $sN") if defined($WeBWorK::timer);
395 : apizer 2159 my $num_of_problems = @problemRecords;
396 :     $max_num_problems = ($max_num_problems>= $num_of_problems) ? $max_num_problems : $num_of_problems;
397 :    
398 :     foreach my $problemRecord (@problemRecords) {
399 :     next unless ref($problemRecord);
400 :     my $probID = $problemRecord->problem_id;
401 :    
402 :     my $valid_status = 0;
403 :     unless (defined($problemRecord) ){
404 : glarose 2977 # warn "Can't find record for problem $prob in set $sN for $student";
405 : apizer 2159 # FIXME check the legitimate reasons why a student record might not be defined
406 :     next;
407 :     }
408 :     $status = $problemRecord->status || 0;
409 :     $attempted = $problemRecord->attempted;
410 :     if (!$attempted){
411 :     $longStatus = '. ';
412 :     }
413 :     elsif ($status >= 0 and $status <=1 ) {
414 :     $valid_status = 1;
415 :     $longStatus = int(100*$status+.5);
416 :     if ($longStatus == 100) {
417 :     $longStatus = 'C ';
418 :     }
419 :     else {
420 : glarose 3130 # change here to give a more reasonable output for gateways,
421 :     # and other versioned sets with only one attempt per version
422 :     # FIXME this isn't good in general, when versioned sets might
423 :     # FIXME allow multiple submissions
424 :     $longStatus = ($setIsVersioned)?'X ':
425 :     &threeSpaceFill($longStatus);
426 : apizer 2159 }
427 :     }
428 :     else {
429 :     $longStatus = 'X ';
430 :     }
431 :    
432 :     my $num_correct = $problemRecord->num_correct || 0;
433 :     my $num_incorrect = $problemRecord->num_incorrect || 0;
434 :     # It's possible that $incorrect is an empty or blank string instead of 0 the || clause fixes this and prevents
435 :     # warning messages in the comparison below.
436 :     $string .= $longStatus;
437 :     $twoString .= threeSpaceFill($num_incorrect);
438 :     my $probValue = $problemRecord->value;
439 : gage 2536 $probValue = 1 unless defined($probValue) and $probValue ne ""; # FIXME?? set defaults here?
440 : apizer 2159 $total += $probValue;
441 :     $totalRight += round_score($status*$probValue) if $valid_status;
442 :    
443 :     $num_of_attempts += $num_correct + $num_incorrect;
444 :    
445 :     $h_problemData{$probID} = $num_incorrect;
446 :    
447 :     $correct_answers_for_problem{$probID} = 0 unless defined($correct_answers_for_problem{$probID});
448 :     # add on the scores for this problem
449 :     if (defined($attempted) and $attempted) {
450 :     $number_of_students_attempting_problem{$probID}++;
451 :     push( @{ $attempts_list_for_problem{$probID} } , $num_of_attempts);
452 :     $number_of_attempts_for_problem{$probID} += $num_of_attempts;
453 :     $correct_answers_for_problem{$probID} += $status;
454 :     }
455 :    
456 :     }
457 : glarose 3130
458 :     # for versioned tests we might be displaying the test date
459 :     my $dateOfTest = '';
460 :     # annoyingly, this is a set property, so get the set
461 :     if ( $setIsVersioned && $showColumns{'date'} ) {
462 :     my @userSet =
463 :     $db->getMergedVersionedSets( [ $studentRecord->user_id, $setName, $sN ] );
464 :     if ( defined( $userSet[0] ) ) { # if this isn't defined, something's wrong
465 :     $dateOfTest = localtime( $userSet[0]->version_creation_time() );
466 :     } else {
467 :     $dateOfTest = '???';
468 :     }
469 :     }
470 : apizer 2159
471 : glarose 3130 $act_as_student_url = $self->systemLink($urlpath->new(type=>'set_list',args=>{courseID=>$courseName}),
472 : apizer 2159 params=>{effectiveUser => $studentRecord->user_id}
473 :     );
474 : glarose 3130 # update for versioned sets
475 :     # if ( $setIsVersioned ) {
476 :     # $act_as_student_url =~ s/\/$courseName/\/$courseName\/quiz_mode/;
477 :     # }
478 :    
479 : apizer 2159 my $email = $studentRecord->email_address;
480 :     # FIXME this needs formatting
481 : glarose 3130
482 :     # change to give better output for gateways
483 :     # FIXME this assumes that all versioned sets only allow one attempt
484 :     # FIXME per version, which is the case for gateways but probably not others
485 :     my $longtwo = ( $setIsVersioned ) ? $string :
486 :     "$string\n$twoString";
487 : apizer 2159
488 :     my $avg_num_attempts = ($num_of_problems) ? $num_of_attempts/$num_of_problems : 0;
489 :     my $successIndicator = ($avg_num_attempts) ? ($totalRight/$total)**2/$avg_num_attempts : 0 ;
490 :     my $temp_hash = { user_id => $studentRecord->user_id,
491 :     last_name => $studentRecord->last_name,
492 :     first_name => $studentRecord->first_name,
493 :     score => $totalRight,
494 :     total => $total,
495 :     index => $successIndicator,
496 :     section => $studentRecord->section,
497 :     recitation => $studentRecord->recitation,
498 : glarose 3130 problemString => "<pre>$longtwo</pre>",
499 : apizer 2159 act_as_student => $act_as_student_url,
500 :     email_address => $studentRecord->email_address,
501 :     problemData => {%h_problemData},
502 : glarose 3130 date => $dateOfTest,
503 : apizer 2159 };
504 : glarose 3130
505 :     # keep track of best score
506 :     if ( $totalRight > $maxScore ) {
507 :     $maxScore = $totalRight;
508 :     $max_hash = { %$temp_hash };
509 :     }
510 :    
511 :     # if we're showing all records, add it in to the list
512 :     if ( ! $showBestOnly ) {
513 : apizer 2159 # add this data to the list of total scores (out of 100)
514 :     # add this data to the list of success indices.
515 :     push( @index_list, $temp_hash->{index});
516 :     push( @score_list, ($temp_hash->{total}) ?$temp_hash->{score}/$temp_hash->{total} : 0 ) ;
517 :     push( @augmentedUserRecords, $temp_hash );
518 : glarose 2977 }
519 : glarose 3130 }
520 :     # if we're showing only the best score, add the best score now
521 :     if ( $showBestOnly ) {
522 :     if ( ! %$max_hash ) { # then we have no tests---e.g., for proctors
523 :     next; # if we could exclude proctors, etc., we
524 :     # might want to keep these:
525 :     $max_hash = { user_id => $studentRecord->user_id(),
526 :     last_name => $studentRecord->last_name(),
527 :     first_name => $studentRecord->first_name(),
528 :     score => 0,
529 :     total => 'n/a',
530 :     index => 0,
531 :     section => $studentRecord->section(),
532 :     recitation => $studentRecord->recitation(),
533 :     problemString => 'no attempt recorded',
534 :     act_as_student => $act_as_student_url,
535 :     email_address => $studentRecord->email_address(),
536 :     problemData => {},
537 :     date => 'none',
538 :     }
539 :     }
540 :     push( @index_list, $max_hash->{index} );
541 :     push( @score_list,
542 :     ($max_hash->{total} && $max_hash->{total} ne 'n/a') ?
543 :     $max_hash->{score}/$max_hash->{total} : 0 );
544 :     push( @augmentedUserRecords, $max_hash );
545 :     }
546 :    
547 :     }
548 :    
549 : apizer 2159 $WeBWorK::timer->continue("end mainloop") if defined($WeBWorK::timer);
550 :    
551 :     @augmentedUserRecords = sort { &$sort_method($a,$b)
552 :     ||
553 :     lc($a->{last_name}) cmp lc($b->{last_name} ) } @augmentedUserRecords;
554 :    
555 :     #####################################################################################
556 :     # construct header
557 :     my $problem_header = '';
558 : apizer 2169
559 : glarose 3130 # changes for gateways/versioned sets here. in this case we allow instructors
560 :     # to modify the appearance of output, which we do with a form. so paste in the
561 :     # form header here, and make appropriate modifications
562 :     my $verSelectors = '';
563 :     if ( $setIsVersioned ) {
564 :     print CGI::start_form({'method' => 'post',
565 :     'action' => $self->systemLink($urlpath,
566 :     authen=>0),
567 :     'name' => 'StudentProgress'});
568 :     print $self->hidden_authen_fields();
569 :    
570 :     $verSelectors = CGI::p({'style'=>'background-color:#eeeeee;color:black;'},
571 :     "Display options: Show ",
572 :     CGI::hidden(-name=>'returning', -value=>'1'),
573 :     CGI::checkbox(-name=>'show_best_only', -value=>'1',
574 :     -checked=>$showBestOnly,
575 :     -label=>' only best scores; '),
576 :     CGI::checkbox(-name=>'show_index', -value=>'1',
577 :     -checked=>$showColumns{'index'},
578 :     -label=>' success indicator; '),
579 :     CGI::checkbox(-name=>'show_date', -value=>'1',
580 :     -checked=>$showColumns{'date'},
581 :     -label=>' test date; '),
582 :     CGI::checkbox(-name=>'show_problems', -value=>'1',
583 :     -checked=>$showColumns{'problems'},
584 :     -label=>'problems;'), "\n", CGI::br(), "\n",
585 :     CGI::checkbox(-name=>'show_section', -value=>'1',
586 :     -checked=>$showColumns{'section'},
587 :     -label=>' section #; '),
588 :     CGI::checkbox(-name=>'show_recitation', -value=>'1',
589 :     -checked=>$showColumns{'recit'},
590 :     -label=>' recitation #; '),
591 :     CGI::checkbox(-name=>'show_login', -value=>'1',
592 :     -checked=>$showColumns{'login'},
593 :     -label=>'login'), "\n", CGI::br(), "\n",
594 :     CGI::submit(-value=>'Update Display')
595 :     );
596 :     }
597 :    
598 :     # set up a column header variable to allow us to drop certain columns
599 :     # for versioned set viewing
600 :     my @columnHdrs = ();
601 :     # define this for use on all of the sorts
602 : glarose 3131 my %paramList = ();
603 :     if ( $setIsVersioned ) {
604 :     my $rtng = (defined( $r->param('returning') )) ?
605 :     $r->param('returning') : 0;
606 :     %paramList = ( 'returning' => $rtng,
607 :     'show_best_only' => $showBestOnly,
608 :     'show_index' => $showColumns{'index'},
609 :     'show_date' => $showColumns{'date'},
610 :     'show_problems' => $showColumns{'problems'},
611 :     'show_section' => $showColumns{'section'},
612 :     'show_recitation' => $showColumns{'recit'},
613 :     'show_login' => $showColumns{'login'} );
614 :     }
615 :     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'} );
616 :     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'} );
617 : glarose 3130 push(@columnHdrs, 'Out'.CGI::br().'Of') if ( $showColumns{'outof'} );
618 :     push(@columnHdrs, 'Date') if ( $showColumns{'date'} );
619 : 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'} );
620 : glarose 3130 push(@columnHdrs, 'Problems'.CGI::br().$problem_header) if ( $showColumns{'problems'} );
621 : 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'} );
622 :     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'} );
623 :     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'} );
624 : glarose 3130
625 : apizer 2159 print
626 : apizer 2169 CGI::br(),
627 :     CGI::br(),
628 :     CGI::p('A period (.) indicates a problem has not been attempted, a &quot;C&quot; indicates
629 :     a problem has been answered 100% correctly, and a number from 0 to 99
630 :     indicates the percentage of partial credit earned. The number on the
631 :     second line gives the number of incorrect attempts. The success indicator,'
632 :     ,CGI::i('Ind'),', for each student is calculated as',
633 :     CGI::br(),
634 :     '100*(totalNumberOfCorrectProblems / totalNumberOfProblems)^2 / (AvgNumberOfAttemptsPerProblem)',CGI::br(),
635 :     'or 0 if there are no attempts.'
636 : apizer 2159 ),
637 : apizer 2169 CGI::br(),
638 :     "Click on student's name to see the student's version of the problem set;
639 :     Click heading to sort table. ",
640 :     CGI::br(),
641 : glarose 3130 $verSelectors,
642 : apizer 2169 CGI::br(),
643 :     defined($sort_method_name) ?" sort method is $sort_method_name":"",
644 : apizer 2159 CGI::start_table({-border=>5,style=>'font-size:smaller'}),
645 : glarose 3130 CGI::Tr(CGI::td( {-align=>'left'}, [ @columnHdrs ]));
646 : glarose 2977
647 :     my $prevFullName = '';
648 :     my $vNum = 1;
649 : apizer 2159 foreach my $rec (@augmentedUserRecords) {
650 :     my $fullName = join("", $rec->{first_name}," ", $rec->{last_name});
651 :     my $email = $rec->{email_address};
652 :     my $twoString = $rec->{twoString};
653 : glarose 2977
654 :     # revision to make versioned sets' format nicer
655 :     my $nameEntry = '';
656 :     if ( $fullName eq $prevFullName ) {
657 :     $vNum++;
658 : glarose 3130 $nameEntry = CGI::span({-style=>"text-align:right;"},
659 :     "(v$vNum)");
660 : glarose 2977 } else {
661 : glarose 3130 $nameEntry =
662 :     CGI::a({-href=>$rec->{act_as_student}},$fullName) .
663 :     ($setIsVersioned && ! $showBestOnly ? ' (v1)' : ' ') .
664 :     CGI::br() . CGI::a({-href=>"mailto:$email"},$email);
665 : glarose 2977 $vNum = 1;
666 :     $prevFullName = $fullName;
667 :     }
668 : glarose 3130 # build set of columns that we want
669 :     my @cols = ();
670 :     push(@cols, $nameEntry) if ($showColumns{'name'});
671 :     push(@cols, sprintf("%0.2f",$rec->{score})) if ($showColumns{'score'});
672 :     push(@cols, $rec->{total}) if ( $showColumns{'outof'} );
673 :     push(@cols, $self->nbsp($rec->{date})) if ($showColumns{'date'});
674 :     push(@cols, sprintf("%0.0f",100*($rec->{index}))) if ($showColumns{'index'});
675 :     push(@cols, $rec->{problemString}) if ($showColumns{'problems'});
676 :     push(@cols, $self->nbsp($rec->{section})) if ($showColumns{'section'});
677 :     push(@cols, $self->nbsp($rec->{recitation})) if ($showColumns{'recit'});
678 :     push(@cols, $rec->{user_id}) if ($showColumns{'login'});
679 : glarose 2977
680 : glarose 3130 print CGI::Tr( CGI::td( [ @cols ] ) );
681 : glarose 2977 # CGI::td(CGI::a({-href=>$rec->{act_as_student}},$fullName), CGI::br(), CGI::a({-href=>"mailto:$email"},$email)),
682 : glarose 3130
683 : apizer 2159 }
684 :    
685 :     print CGI::end_table();
686 :    
687 :    
688 :    
689 :    
690 :     return "";
691 :     }
692 :    
693 :    
694 :     #################################
695 :     # Utility function NOT a method
696 :     #################################
697 :     sub threeSpaceFill {
698 :     my $num = shift @_ || 0;
699 :    
700 :     if (length($num)<=1) {return "$num".'&nbsp;&nbsp;';}
701 :     elsif (length($num)==2) {return "$num".'&nbsp;';}
702 :     else {return "## ";}
703 :     }
704 :     sub round_score{
705 :     return shift;
706 :     }
707 :     1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9