[system] / branches / rel-2-2-dev / webwork2 / lib / WeBWorK / ContentGenerator / Instructor / StudentProgress.pm Repository:
ViewVC logotype

View of /branches/rel-2-2-dev/webwork2/lib/WeBWorK/ContentGenerator/Instructor/StudentProgress.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4018 - (download) (as text) (annotate)
Thu Feb 9 23:20:05 2006 UTC (7 years, 3 months ago) by sh002i
File size: 34764 byte(s)
Preserve gateway test display options in colum header links.

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/Instructor/StudentProgress.pm,v 1.22.2.3 2006/02/02 22:38:53 sh002i Exp $
    5 #
    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::Debug;
   30 use WeBWorK::ContentGenerator::Grades;
   31 use WeBWorK::DB::Record::Set;
   32 use WeBWorK::Utils qw(readDirectory list2hash max sortByName);
   33 use WeBWorK::Utils::SortRecords qw/sortRecords/;
   34 
   35 
   36 # The table format has been borrowed from the Grades.pm module
   37 sub initialize {
   38   my $self     = shift;
   39   # FIXME  are there args here?
   40   my @components = @_;
   41   my $r          = $self->{r};
   42   my $urlpath    = $r->urlpath;
   43   my $type       = $urlpath->arg("statType") || '';
   44   my $db         = $self->{db};
   45   my $ce         = $self->{ce};
   46   my $authz      = $self->{authz};
   47   my $courseName = $urlpath->arg('courseID');
   48   my $user       = $r->param('user');
   49 
   50   # Check permissions
   51   return unless $authz->hasPermissions($user, "access_instructor_tools");
   52 
   53   $self->{type}  = $type;
   54   if ($type eq 'student') {
   55     my $studentName = $r->urlpath->arg("userID") || $user;
   56     $self->{studentName } = $studentName;
   57 
   58   } elsif ($type eq 'set') {
   59     my $setName = $r->urlpath->arg("setID") || 0;
   60     $self->{setName}     = $setName;
   61     my $setRecord  = $db->getGlobalSet($setName); # checked
   62     die "global set $setName  not found." unless $setRecord;
   63     $self->{set_due_date} = $setRecord->due_date;
   64     $self->{setRecord}   = $setRecord;
   65   }
   66 }
   67 
   68 
   69 sub title {
   70   my ($self) = @_;
   71   my $r = $self->r;
   72   my $authz = $r->authz;
   73   my $user = $r->param('user');
   74 
   75   # Check permissions
   76   return "" unless $authz->hasPermissions($user, "access_instructor_tools");
   77 
   78   my $type                = $self->{type};
   79   my $string              = "Student Progress for ".$self->{ce}->{courseName}." ";
   80   if ($type eq 'student') {
   81     $string             .= "student ".$self->{studentName};
   82   } elsif ($type eq 'set' ) {
   83     $string             .= "set   ".$self->{setName};
   84     $string             .= ".    Due ". $self->formatDateTime($self->{set_due_date});
   85   }
   86   return $string;
   87 }
   88 sub siblings {
   89   my ($self) = @_;
   90   my $r = $self->r;
   91   my $db = $r->db;
   92   my $authz = $r->authz;
   93   my $user = $r->param('user');
   94   my $urlpath = $r->urlpath;
   95 
   96   # Check permissions
   97   return "" unless $authz->hasPermissions($user, "access_instructor_tools");
   98 
   99   my $courseID = $urlpath->arg("courseID");
  100   my $eUserID  = $r->param("effectiveUser");
  101   my @setIDs   = sort  $db->listGlobalSets;
  102 
  103   my $progress     = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::StudentProgress",
  104                                           courseID => $courseID);
  105 
  106   print CGI::start_div({class=>"info-box", id=>"fisheye"});
  107   print CGI::h2("Student Progress");
  108   #print CGI::start_ul({class=>"LinksMenu"});
  109   #print CGI::start_li();
  110   #print CGI::span({style=>"font-size:larger"}, CGI::a({href=>$self->systemLink($stats)}, 'Statistics'));
  111   print CGI::start_ul();
  112 
  113   foreach my $setID (@setIDs) {
  114     my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::StudentProgress",
  115       courseID => $courseID, setID => $setID,statType => 'set',);
  116     print CGI::li(CGI::a({href=>$self->systemLink($problemPage)}, WeBWorK::ContentGenerator::underscore2nbsp($setID)));
  117   }
  118 
  119   print CGI::end_ul();
  120   #print CGI::end_li();
  121   #print CGI::end_ul();
  122   print CGI::end_div();
  123 
  124   return "";
  125 }
  126 sub body {
  127   my $self       = shift;
  128   my $r          = $self->r;
  129   my $urlpath    = $r->urlpath;
  130   my $db         = $r->db;
  131   my $ce         = $r->ce;
  132   my $authz      = $r->authz;
  133   my $user       = $r->param('user');
  134   my $courseName = $urlpath->arg("courseID");
  135   my $type       = $self->{type};
  136 
  137   # Check permissions
  138   return CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to access instructor tools"))
  139     unless $authz->hasPermissions($user, "access_instructor_tools");
  140 
  141   if ($type eq 'student') {
  142     my $studentName = $self->{studentName};
  143     my $studentRecord = $db->getUser($studentName) # checked
  144       or die "record for user $studentName not found";
  145     my $fullName = $studentRecord->full_name;
  146         my $courseHomePage = $urlpath->new(type  => 'set_list',
  147           args => {courseID=>$courseName});
  148     my $email = $studentRecord->email_address;
  149 
  150     print CGI::a({-href=>"mailto:$email"}, $email), CGI::br(),
  151       "Section: ", $studentRecord->section, CGI::br(),
  152       "Recitation: ", $studentRecord->recitation, CGI::br();
  153 
  154     if ($authz->hasPermissions($user, "become_student")) {
  155       my $act_as_student_url = $self->systemLink($courseHomePage,
  156         params => {effectiveUser=>$studentName});
  157 
  158       print 'Act as: ', CGI::a({-href=>$act_as_student_url},$studentRecord->user_id);
  159     }
  160 
  161     print WeBWorK::ContentGenerator::Grades::displayStudentStats($self,$studentName);
  162   } elsif( $type eq 'set') {
  163     $self->displaySets($self->{setName});
  164   } elsif ($type eq '') {
  165 
  166     $self->index;
  167   } else {
  168     warn "Don't recognize statistics display type: |$type|";
  169 
  170   }
  171 
  172 
  173   return '';
  174 
  175 }
  176 sub index {
  177   my $self          = shift;
  178   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   my @studentList   = sort $db->listUsers;
  185   my @setList       = sort  $db->listGlobalSets;
  186 
  187 ## Edit to filter out students you aren't allowed to see
  188 #
  189   my @myUsers;
  190 # my @studentRecords = $db->getUsers;  #this is never used
  191   my $user = $r->param("user");
  192 
  193   my (@viewable_sections, @viewable_recitations);
  194   if (defined @{$ce->{viewable_sections}->{$user}})
  195     {@viewable_sections = @{$ce->{viewable_sections}->{$user}};}
  196   if (defined @{$ce->{viewable_recitations}->{$user}})
  197     {@viewable_recitations = @{$ce->{viewable_recitations}->{$user}};}
  198   if (@viewable_sections or @viewable_recitations){
  199     foreach my $studentL (@studentList){
  200       my $keep = 0;
  201       my $student = $db->getUser($studentL);
  202       foreach my $sec (@viewable_sections){
  203         if ($student->section() eq $sec){$keep = 1; last;}
  204       }
  205       foreach my $rec (@viewable_recitations){
  206         if ($student->recitation() eq $rec){$keep = 1; last;}
  207       }
  208       if ($keep) {push @myUsers, $studentL;}
  209     }
  210 # @studentList = @myUsers;
  211   }
  212   else {@myUsers = @studentList;}
  213 
  214   my @studentRecords = $db->getUsers(@myUsers);
  215   my @sortedStudentRecords = sortRecords({fields=>[qw/last_name first_name user_id/]}, @studentRecords);
  216 
  217   my @setLinks      = ();
  218   my @studentLinks  = ();
  219   foreach my $set (@setList) {
  220       my $setStatisticsPage   = $urlpath->newFromModule($urlpath->module,
  221                                                         courseID => $courseName,
  222                                                         statType => 'set',
  223                                                         setID    => $set
  224       );
  225     push @setLinks, CGI::a({-href=>$self->systemLink($setStatisticsPage) }, WeBWorK::ContentGenerator::underscore2nbsp($set));
  226   }
  227 
  228   foreach my $studentRecord (@sortedStudentRecords) {
  229     my $first_name = $studentRecord->first_name;
  230     my $last_name = $studentRecord->last_name;
  231     my $user_id = $studentRecord->user_id;
  232     my $userStatisticsPage  = $urlpath->newFromModule($urlpath->module,
  233                                                         courseID => $courseName,
  234                                                         statType => 'student',
  235                                                         userID   => $user_id
  236       );
  237 
  238     push @studentLinks, CGI::a({-href=>$self->systemLink($userStatisticsPage,
  239                                                          prams=>{effectiveUser => $studentRecord->user_id}
  240                                                          )},"  $last_name, $first_name  ($user_id)" ),;
  241   }
  242   print join("",
  243     CGI::start_table({-border=>2, -cellpadding=>20}),
  244     CGI::Tr(
  245       CGI::td({-valign=>'top'},
  246         CGI::h3({-align=>'center'},'View student progress by set'),
  247         CGI::ul(  CGI::li( [@setLinks] ) ),
  248       ),
  249       CGI::td({-valign=>'top'},
  250         CGI::h3({-align=>'center'},'View student progress by student'),
  251         CGI::ul(CGI::li( [ @studentLinks ] ) ),
  252       ),
  253     ),
  254     CGI::end_table(),
  255   );
  256 
  257 }
  258 ###################################################
  259 sub displaySets {
  260   my $self             = shift;
  261   my $r                = $self->r;
  262   my $urlpath          = $r->urlpath;
  263   my $db               = $r->db;
  264   my $ce               = $r->ce;
  265   my $authz            = $r->authz;
  266   my $courseName       = $urlpath->arg("courseID");
  267   my $setName          = $urlpath->arg("setID");
  268   my $user             = $r->param('user');
  269   my $setRecord        = $self->{setRecord};
  270   my $root             = $ce->{webworkURLs}->{root};
  271 
  272   my $setStatsPage     = $urlpath->newFromModule($urlpath->module,courseID=>$courseName,statType=>'sets',setID=>$setName);
  273   my $primary_sort_method_name = $r->param('primary_sort');
  274   my $secondary_sort_method_name = $r->param('secondary_sort');
  275   my $ternary_sort_method_name = $r->param('ternary_sort');
  276 
  277   my @studentList      = $db->listUsers;
  278 
  279 # another versioning/gateway change.  in many cases we don't want or need
  280 # all of the columns that are put in here by default, so we add a set of
  281 # flags for which columns to show.  for versioned sets we may also want to
  282 # only see the best score, so we include that as an option also.
  283 # these are ignored for non-versioned sets
  284   my %showColumns = ( 'name' => 1, 'score' => 1, 'outof' => 1,
  285           'date' => 0, 'testtime' => 0, 'index' => 1,
  286           'problems' => 1, 'section' => 1, 'recit' => 1,
  287           'login' => 1 );
  288   my $showBestOnly = 0;
  289 
  290     my @index_list                           = ();  # list of all student index
  291   my @score_list                           = ();  # list of all student total percentage scores
  292     my %attempts_list_for_problem            = ();  # a list of the number of attempts for each problem
  293     my %number_of_attempts_for_problem       = ();  # the total number of attempst for this problem (sum of above list)
  294     my %number_of_students_attempting_problem = ();  # the number of students attempting this problem.
  295     my %correct_answers_for_problem          = ();  # the number of students correctly answering this problem (partial correctness allowed)
  296   my $sort_method = sub {
  297     my ($a,$b,$sort_method_name) = @_;
  298     return 0 unless defined($sort_method_name);
  299     return  lc($a->{last_name}) cmp lc($b->{last_name}) if $sort_method_name eq 'last_name';
  300     return  lc($a->{first_name}) cmp lc($b->{first_name}) if $sort_method_name eq 'first_name';
  301     return  lc($a->{email_address}) cmp lc($b->{email_address}) if $sort_method_name eq 'email_address';
  302     return $b->{score} <=> $a->{score} if $sort_method_name eq 'score';
  303     return $b->{index} <=> $a->{index} if $sort_method_name eq 'index';
  304     return lc($a->{section}) cmp lc($b->{section}) if $sort_method_name eq 'section';
  305     return lc($a->{recitation}) cmp lc($b->{recitation}) if $sort_method_name eq 'recitation';
  306     return lc($a->{user_id}) cmp lc($b->{user_id}) if $sort_method_name eq 'user_id';
  307     if ($sort_method_name =~/p(\d+)/) {
  308         my $left  =  $b->{problemData}->{$1} ||0;
  309         my $right =  $a->{problemData}->{$1} ||0;
  310       return $left <=> $right;  # sort by number of attempts.
  311     }
  312 
  313   };
  314   my %display_sort_method_name = (
  315     last_name => 'last name',
  316     first_name => 'first name',
  317     email_address => 'email address',
  318     score => 'score',
  319     index => 'success indicator',
  320     section => 'section',
  321     recitation => 'recitation',
  322     user_id => 'login name',
  323   );
  324 
  325 # get versioning information
  326     my $GlobalSet = $db->getGlobalSet($setName);
  327     my $setIsVersioned =
  328   ( defined($GlobalSet->assignment_type()) &&
  329     $GlobalSet->assignment_type() =~ /gateway/ ) ? 1 : 0;
  330 
  331 # reset column view options based on whether the set is versioned and, if so,
  332 # the input parameters
  333     if ( $setIsVersioned ) {
  334   # the returning parameter lets us set defaults for versioned sets
  335   my $ret = $r->param('returning');
  336   $showColumns{'date'} = $ret ? $r->param('show_date') : 1;
  337   $showColumns{'testtime'} = $ret ? $r->param('show_testtime') : 1;
  338   $showColumns{'index'} = $ret ? $r->param('show_index') : 0;
  339   $showColumns{'problems'} = $ret ? $r->param('show_problems') : 0;
  340   $showColumns{'section'} = $ret? $r->param('show_section') : 0;
  341   $showColumns{'recit'} = $ret ? $r->param('show_recitation') : 0;
  342   $showColumns{'login'} = $ret ? $r->param('show_login') : 0;
  343   $showBestOnly = $ret ? $r->param('show_best_only') : 0;
  344     }
  345 
  346 ###############################################################
  347 #  Print tables
  348 ###############################################################
  349 
  350   my $max_num_problems  = 0;
  351   # get user records
  352   debug("Begin obtaining user records for set $setName");
  353   my @userRecords  = $db->getUsers(@studentList);
  354   debug("End obtaining user records for set $setName");
  355     debug("begin main loop");
  356   my @augmentedUserRecords    = ();
  357   my $number_of_active_students;
  358 
  359 ## Edit to filter out students
  360 #
  361   my @myUsers;
  362   my $ActiveUser = $r->param("user");
  363   my (@viewable_sections, @viewable_recitations);
  364   if (defined @{$ce->{viewable_sections}->{$user}})
  365     {@viewable_sections = @{$ce->{viewable_sections}->{$user}};}
  366   if (defined @{$ce->{viewable_recitations}->{$user}})
  367     {@viewable_recitations = @{$ce->{viewable_recitations}->{$user}};}
  368   if (@viewable_sections or @viewable_recitations){
  369     foreach my $student (@userRecords){
  370       my $keep = 0;
  371       foreach my $sec (@viewable_sections){
  372         if ($student->section() eq $sec){$keep = 1; last;}
  373       }
  374       foreach my $rec (@viewable_recitations){
  375         if ($student->recitation() eq $rec){$keep = 1; last;}
  376       }
  377       if ($keep) {push @myUsers, $student;}
  378     }
  379   }
  380   else {@myUsers = @userRecords;}
  381   foreach my $studentRecord (@myUsers)   {
  382     next unless ref($studentRecord);
  383     my $student = $studentRecord->user_id;
  384     next if $studentRecord->last_name =~/^practice/i;  # don't show practice users
  385     next unless $ce->status_abbrev_has_behavior($studentRecord->status, "include_in_stats");
  386     $number_of_active_students++;
  387 
  388 # build list of versioned sets for this student user
  389       my @allSetNames = ();
  390       if ( $setIsVersioned ) {
  391     my $numVersions = $db->getUserSetVersionNumber($student,$setName);
  392     for ( my $i=1; $i<=$numVersions; $i++ ) {
  393         $allSetNames[$i-1] = "$setName,v$i";
  394     }
  395       } else {
  396     @allSetNames = ( "$setName" );
  397       }
  398 
  399   # for versioned sets, we might be keeping only the high score
  400     my $maxScore = -1;
  401     my $max_hash = {};
  402   # make this global to the student loop (there was a reason for this for
  403   # versioned sets, at least, though I'm not seeing it now -glr)
  404     my $act_as_student_url = '';
  405 
  406     foreach my $sN ( @allSetNames ) {
  407 
  408       my $status          = 0;
  409       my $attempted       = 0;
  410       my $longStatus      = '';
  411       my $string          = '';
  412       my $twoString       = '';
  413       my $totalRight      = 0;
  414       my $total           = 0;
  415     my $total_num_of_attempts_for_set = 0;
  416     my %h_problemData   = ();
  417     my $probNum         = 0;
  418 
  419     debug("Begin obtaining problem records for user $student set $setName");
  420 
  421     my @problemRecords = sort {$a->problem_id <=> $b->problem_id } $db->getAllMergedUserProblems( $student, $sN );
  422     debug("End obtaining problem records for user $student set $setName");
  423     my $num_of_problems = @problemRecords;
  424     $max_num_problems = ($max_num_problems>= $num_of_problems) ? $max_num_problems : $num_of_problems;
  425      ########################################
  426     # Notes for factoring the calculation in this loop.
  427     #
  428     # Inputs include:
  429     #
  430     #
  431     # @problemRecords
  432     # returns
  433     #       $num_of_attempts
  434     #       $status
  435     # updates
  436     #       $number_of_students_attempting_problem{$probID}++;
  437     #     @{ $attempts_list_for_problem{$probID} }
  438     #     $number_of_attempts_for_problem{$probID}
  439     #     $total_num_of_attempts_for_set
  440     #     $correct_answers_for_problem{$probID}
  441     #
  442     #       $string (formatting output)
  443     #       $twoString (more formatted output)
  444                 #       $longtwo (a combination of $string and $twostring)
  445     #       $total
  446     #       $totalRight
  447     ###################################
  448 
  449     foreach my $problemRecord (@problemRecords) {
  450       next unless ref($problemRecord);
  451         # warn "Can't find record for problem $prob in set $setName for $student";
  452         # FIXME check the legitimate reasons why a student record might not be defined
  453       ####################################################################
  454       # Grab data from the database
  455       ####################################################################
  456       # It's possible that $problemRecord->num_correct or $problemRecord->num_correct
  457       # or $problemRecord->status is an empty
  458       # or blank string instead of 0.  The || clause fixes this and prevents
  459       # warning messages in the comparisons below.
  460 
  461       my $probID             = $problemRecord->problem_id;
  462       my $attempted          = $problemRecord->attempted;
  463       my $num_correct        = $problemRecord->num_correct     || 0;
  464       my $num_incorrect      = $problemRecord->num_incorrect   || 0;
  465       my $num_of_attempts    = $num_correct + $num_incorrect;
  466 
  467         # initialize the number of correct answers for this problem
  468         # if the value has not been defined.
  469           $correct_answers_for_problem{$probID}  = 0 unless defined($correct_answers_for_problem{$probID});
  470 
  471 
  472       my $probValue          = $problemRecord->value;   ## This doesn't work - Fix it
  473       # set default problem value here
  474       $probValue             = 1 unless defined($probValue) and $probValue ne "";  # FIXME?? set defaults here?
  475 
  476       my $status             = $problemRecord->status          || 0;
  477 
  478       # sanity check that the status (score) is between 0 and 1
  479           my $valid_status       = ($status >= 0 and $status <=1 ) ? 1 : 0;
  480 
  481           ###################################################################
  482           # Determine the string $longStatus which will display the student's current score
  483           ###################################################################
  484           my $longStatus = '';
  485       if (!$attempted){
  486         $longStatus     = '.';
  487       } elsif   ($valid_status) {
  488         $longStatus     = int(100*$status+.5);
  489                  # we change $longStatus to give more reasonable output for
  490                  #   gateways (actually all versioned sets; this might get us
  491                  #   into trouble at some later date).
  492         if ( $longStatus == 100 ) {
  493             $longStatus = 'C';
  494         } elsif ( $setIsVersioned ) {
  495             $longStatus = ( $longStatus == 0 ) ?
  496           'X' : $longStatus;
  497         }
  498       } else  {
  499         $longStatus   = 'X';
  500       }
  501 
  502       $string          .= threeSpaceFill($longStatus);
  503       $twoString       .= threeSpaceFill($num_incorrect);
  504 
  505       $total           += $probValue;
  506       $totalRight      += round_score($status*$probValue) if $valid_status;
  507 
  508        # add on the scores for this problem
  509       if (defined($attempted) and $attempted) {
  510         $number_of_students_attempting_problem{$probID}++;
  511         push( @{ $attempts_list_for_problem{$probID} } ,     $num_of_attempts);
  512         $number_of_attempts_for_problem{$probID}             += $num_of_attempts;
  513         $h_problemData{$probID}                               = $num_incorrect;
  514         $total_num_of_attempts_for_set                       += $num_of_attempts;
  515         $correct_answers_for_problem{$probID}                += $status;
  516       }
  517 
  518         }  # end of problem record loop
  519 
  520     # for versioned tests we might be displaying the test date and test time
  521       my $dateOfTest = '';
  522       my $testTime = '';
  523         # annoyingly, this is a set property, so get the set
  524       if ( $setIsVersioned &&
  525      ( $showColumns{'date'} || $showColumns{'testtime'} ) ) {
  526     my @userSet =
  527         $db->getMergedVersionedSets( [ $studentRecord->user_id, $setName, $sN ] );
  528     if ( defined( $userSet[0] ) ) {  # if this isn't defined, something's wrong
  529         $dateOfTest =
  530       localtime( $userSet[0]->version_creation_time() );
  531         my $gradeTime = '';
  532         if ( defined( $userSet[0]->version_last_attempt_time() ) &&
  533        $userSet[0]->version_last_attempt_time() ) {
  534       $testTime = ( $userSet[0]->version_last_attempt_time() -
  535               $userSet[0]->version_creation_time() ) /
  536               60;
  537       $testTime = sprintf("%3.1f min", $testTime);
  538         } else {
  539       $testTime = 'time limit exceeded';
  540         }
  541     } else {
  542         $dateOfTest = '???';
  543         $testTime = '???';
  544     }
  545       }
  546 
  547 
  548     $act_as_student_url = $self->systemLink($urlpath->new(type=>'set_list',args=>{courseID=>$courseName}),
  549                                                params=>{effectiveUser => $studentRecord->user_id}
  550     );
  551     my $email              = $studentRecord->email_address;
  552     # FIXME  this needs formatting
  553 
  554     # change to give better output for gateways; this just reports the result
  555     # for each problem, not the number of attempts.  if versioned sets are
  556     # used where multiple attempts are allowed per version this may not be
  557     # as desirable
  558           my $longtwo = ( $setIsVersioned ) ? $string :
  559         "$string\n$twoString";
  560 
  561     my $avg_num_attempts = ($num_of_problems) ? $total_num_of_attempts_for_set/$num_of_problems : 0;
  562     my $successIndicator = ($avg_num_attempts && $total) ? ($totalRight/$total)**2/$avg_num_attempts : 0 ;
  563 
  564     my $temp_hash         = {         user_id        => $studentRecord->user_id,
  565                                       last_name      => $studentRecord->last_name,
  566                                       first_name     => $studentRecord->first_name,
  567                                       score          => $totalRight,
  568                                       total          => $total,
  569                                       index          => $successIndicator,
  570                                       section        => $studentRecord->section,
  571                                       recitation     => $studentRecord->recitation,
  572                                       problemString  => "<pre>$longtwo</pre>",
  573                                       act_as_student => $act_as_student_url,
  574                                       email_address  => $studentRecord->email_address,
  575                                       problemData    => {%h_problemData},
  576               date           => $dateOfTest,
  577               testtime       => $testTime,
  578     };
  579 
  580     # keep track of best score
  581       if ( $totalRight > $maxScore ) {
  582     $maxScore = $totalRight;
  583     $max_hash = { %$temp_hash };
  584       }
  585 
  586     # if we're showing all records, add it in to the list
  587       if ( ! $showBestOnly ) {
  588     # add this data to the list of total scores (out of 100)
  589     # add this data to the list of success indices.
  590     push( @index_list, $temp_hash->{index});
  591     push( @score_list, ($temp_hash->{total}) ?$temp_hash->{score}/$temp_hash->{total} : 0 ) ;
  592     push( @augmentedUserRecords, $temp_hash );
  593       }
  594 
  595   } # this closes the loop through all set versions
  596 
  597     # if we're showing only the best score, add the best score now
  598   if ( $showBestOnly ) {
  599       if ( ! %$max_hash ) {  # then we have no tests---e.g., for proctors
  600     next;
  601         # if we could exclude proctors and instructors, we might want to keep
  602         # these, e.g., with something like the following
  603   # $max_hash = { user_id => $studentRecord->user_id(),
  604   #         last_name => $studentRecord->last_name(),
  605   #         first_name => $studentRecord->first_name(),
  606   #         score => 0,
  607   #         total => 'n/a',
  608   #         index => 0,
  609   #         section => $studentRecord->section(),
  610   #         recitation => $studentRecord->recitation(),
  611   #         problemString => 'no attempt recorded',
  612   #         act_as_student => $act_as_student_url,
  613   #         email_address => $studentRecord->email_address(),
  614   #         problemData => {},
  615   #         date => 'none',
  616   #         testtime => 'none',
  617   #     }
  618       }
  619 
  620       push( @index_list, $max_hash->{index} );
  621       push( @score_list,
  622       ($max_hash->{total} && $max_hash->{total} ne 'n/a') ?
  623       $max_hash->{score}/$max_hash->{total} : 0 );
  624       push( @augmentedUserRecords, $max_hash );
  625 
  626   }
  627 
  628         } # this closes the loop through all student records
  629 
  630   debug("end mainloop");
  631 
  632   @augmentedUserRecords = sort {
  633     &$sort_method($a,$b,$primary_sort_method_name)
  634       ||
  635     &$sort_method($a,$b,$secondary_sort_method_name)
  636       ||
  637     &$sort_method($a,$b,$ternary_sort_method_name)
  638       ||
  639     lc($a->{last_name}) cmp lc($b->{last_name})
  640       ||
  641     lc($a->{first_name}) cmp lc($b->{first_name})
  642       ||
  643     lc($a->{user_id}) cmp lc($b->{user_id})
  644     }
  645     @augmentedUserRecords;
  646 
  647 
  648   # construct header
  649   my $problem_header = '';
  650   my @list_problems = sort {$a<=> $b } $db->listGlobalProblems($setName );
  651   $problem_header = '<pre>'.join("", map {&threeSpaceFill($_)}  @list_problems  ).'</pre>';
  652 
  653 # changes for gateways/versioned sets here.  in this case we allow instructors
  654 # to modify the appearance of output, which we do with a form.  so paste in the
  655 # form header here, and make appropriate modifications
  656         my $verSelectors = '';
  657   if ( $setIsVersioned ) {
  658       print CGI::start_form({'method' => 'post',
  659            'action' => $self->systemLink($urlpath,
  660                  authen=>0),
  661            'name' => 'StudentProgress'});
  662       print $self->hidden_authen_fields();
  663 
  664 #     $verSelectors = CGI::p({'style'=>'background-color:#eeeeee;color:black;'},
  665       print CGI::p({'style'=>'background-color:#eeeeee;color:black;'},
  666           "Display options: Show ",
  667     CGI::hidden(-name=>'returning', -value=>'1'),
  668     CGI::checkbox(-name=>'show_best_only', -value=>'1',
  669             -checked=>$showBestOnly,
  670             -label=>' only best scores; '),
  671     CGI::checkbox(-name=>'show_index', -value=>'1',
  672             -checked=>$showColumns{'index'},
  673             -label=>' success indicator; '),
  674     CGI::checkbox(-name=>'show_date', -value=>'1',
  675             -checked=>$showColumns{'date'},
  676             -label=>' test date; '),
  677     CGI::checkbox(-name=>'show_testtime', -value=>'1',
  678             -checked=>$showColumns{'testtime'},
  679             -label=>' test time; '),
  680     CGI::checkbox(-name=>'show_problems', -value=>'1',
  681             -checked=>$showColumns{'problems'},
  682             -label=>'problems;'), "\n", CGI::br(), "\n",
  683     CGI::checkbox(-name=>'show_section', -value=>'1',
  684             -checked=>$showColumns{'section'},
  685             -label=>' section #; '),
  686     CGI::checkbox(-name=>'show_recitation', -value=>'1',
  687             -checked=>$showColumns{'recit'},
  688             -label=>' recitation #; '),
  689     CGI::checkbox(-name=>'show_login', -value=>'1',
  690             -checked=>$showColumns{'login'},
  691             -label=>'login'), "\n", CGI::br(), "\n",
  692     CGI::submit(-value=>'Update Display'),
  693       );
  694       print CGI::end_form();
  695   }
  696 
  697 #####################################################################################
  698   print
  699 #   CGI::br(),
  700     CGI::br(),
  701     CGI::p('A period (.) indicates a problem has not been attempted, a &quot;C&quot; indicates
  702     a problem has been answered 100% correctly, and a number from 0 to 99
  703     indicates the percentage of partial credit earned. The number on the
  704     second line gives the number of incorrect attempts.  The success indicator,'
  705     ,CGI::i('Ind'),', for each student is calculated as',
  706     CGI::br(),
  707     '100*(totalNumberOfCorrectProblems / totalNumberOfProblems)^2 / (AvgNumberOfAttemptsPerProblem)',CGI::br(),
  708     'or 0 if there are no attempts.'
  709     ),
  710     CGI::br(),
  711     "Click on student's name to see the student's version of the homework set. &nbsp; &nbsp;&nbsp;
  712     Click heading to sort table. ",
  713     CGI::br(),
  714     CGI::br(),
  715     defined($primary_sort_method_name) ?" Entries are sorted by $display_sort_method_name{$primary_sort_method_name}":'',
  716     defined($secondary_sort_method_name) ?", then by $display_sort_method_name{$secondary_sort_method_name}":'',
  717     defined($ternary_sort_method_name) ?", then by $display_sort_method_name{$ternary_sort_method_name}":'',
  718     defined($primary_sort_method_name) ?'.':'',
  719   ;
  720   # calculate secondary and ternary sort methods parameters if appropriate
  721   my %past_sort_methods = ();
  722   %past_sort_methods = (secondary_sort => "$primary_sort_method_name",) if defined($primary_sort_method_name);
  723   %past_sort_methods = (%past_sort_methods, ternary_sort => "$secondary_sort_method_name",) if defined($secondary_sort_method_name);
  724 
  725   # continue with outputing of table
  726   if ( ! $setIsVersioned ) {
  727       print
  728     CGI::start_table({-border=>5,style=>'font-size:smaller'}),
  729     CGI::Tr(CGI::td(  {-align=>'left'},
  730       ['Name'.CGI::br().CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{primary_sort=>'first_name', %past_sort_methods})},'First').
  731          '&nbsp;&nbsp;&nbsp;'.CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{primary_sort=>'last_name', %past_sort_methods })},'Last').CGI::br().
  732          CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{primary_sort=>'email_address', %past_sort_methods })},'Email'),
  733       CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{primary_sort=>'score', %past_sort_methods})},'Score'),
  734       'Out'.CGI::br().'Of',
  735       CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{primary_sort=>'index', %past_sort_methods})},'Ind'),
  736       'Problems'.CGI::br().$problem_header,
  737       CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{primary_sort=>'section', %past_sort_methods})},'Section'),
  738       CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{primary_sort=>'recitation', %past_sort_methods})},'Recitation'),
  739       CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{primary_sort=>'user_id', %past_sort_methods})},'Login Name'),
  740       ])
  741 
  742     ),
  743     ;
  744   } else {
  745     # we need to preserve display options when the sort headers are clicked
  746     my %display_options = (
  747       returning       => 1,
  748       show_best_only  => $showBestOnly,
  749       show_index      => $showColumns{index},
  750       show_date       => $showColumns{date},
  751       show_testtime   => $showColumns{testtime},
  752       show_problems   => $showColumns{problems},
  753       show_section    => $showColumns{section},
  754       show_recitation => $showColumns{recitation},
  755       show_login      => $showColumns{login},
  756     );
  757     my %params = (%past_sort_methods, %display_options);
  758       my @columnHdrs = ();
  759       push( @columnHdrs, 'Name'.CGI::br().CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{primary_sort=>'first_name', %params})},'First').
  760       '&nbsp;&nbsp;&nbsp;'.CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{primary_sort=>'last_name', %params })},'Last') );
  761       push( @columnHdrs, CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{primary_sort=>'score', %params})},'Score') );
  762       push( @columnHdrs, 'Out'.CGI::br().'Of' );
  763       push( @columnHdrs, 'Date' ) if ( $showColumns{ 'date' } );
  764       push( @columnHdrs, 'TestTime' ) if ( $showColumns{ 'testtime' } );
  765       push( @columnHdrs, CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{primary_sort=>'index', %params})},'Ind') )
  766     if ( $showColumns{ 'index' } );
  767       push( @columnHdrs, 'Problems'.CGI::br().$problem_header )
  768     if ( $showColumns{ 'problems' } );
  769       push( @columnHdrs, CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{primary_sort=>'section', %params})},'Section') )
  770     if ( $showColumns{ 'section' } );
  771       push( @columnHdrs, CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{primary_sort=>'recitation', %params})},'Recitation') )
  772     if ( $showColumns{ 'recit' } );
  773       push( @columnHdrs, CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{primary_sort=>'user_id', %params})},'Login Name') )
  774     if ( $showColumns{ 'login' } );
  775 
  776       print CGI::start_table({-border=>5,style=>'font-size:smaller'}),
  777           CGI::Tr(CGI::td(  {-align=>'left'},
  778         [ @columnHdrs ] ) ),
  779       ;
  780   }
  781 
  782     # variables to keep track of versioned sets
  783   my $prevFullName = '';
  784   my $vNum = 1;
  785 
  786   foreach my $rec (@augmentedUserRecords) {
  787     my $fullName = join("", $rec->{first_name}," ", $rec->{last_name});
  788     my $email    = $rec->{email_address};
  789     my $twoString  = $rec->{twoString};
  790     if ( ! $setIsVersioned ) {
  791         print CGI::Tr(
  792       CGI::td(CGI::a({-href=>$rec->{act_as_student}},$fullName), CGI::br(), CGI::a({-href=>"mailto:$email"},$email)),
  793       CGI::td( sprintf("%0.2f",$rec->{score}) ), # score
  794       CGI::td($rec->{total}), # out of
  795       CGI::td(sprintf("%0.0f",100*($rec->{index}) )),   # indicator
  796       CGI::td($rec->{problemString}), # problems
  797       CGI::td($self->nbsp($rec->{section})),
  798       CGI::td($self->nbsp($rec->{recitation})),
  799       CGI::td($rec->{user_id}),
  800         );
  801     } else {
  802             # separate versioned sets so that we can restrict what columns
  803             # we show
  804         my @cols = ();
  805             # revise names to make versioned sets' format nicer
  806         my $nameEntry = '';
  807         if ( $fullName eq $prevFullName ) {
  808       $vNum++;
  809       $nameEntry = CGI::span({-style=>"text-align:right;"},
  810                  "(v$vNum)");
  811         } else {
  812       $nameEntry =
  813           CGI::a({-href=>$rec->{act_as_student}},$fullName) .
  814           ($setIsVersioned && ! $showBestOnly ? ' (v1)':' ') .
  815           CGI::br() . CGI::a({-href=>"mailto:$email"},$email);
  816       $vNum = 1;
  817       $prevFullName = $fullName;
  818         }
  819 
  820       # build columns to show
  821         push(@cols, $nameEntry, sprintf("%0.2f",$rec->{score}),
  822        $rec->{total});
  823         push(@cols, $self->nbsp($rec->{date}))
  824       if ($showColumns{'date'});
  825         push(@cols, $self->nbsp($rec->{testtime}))
  826       if ($showColumns{'testtime'});
  827         push(@cols, sprintf("%0.0f",$rec->{index}))
  828       if ($showColumns{'index'});
  829         push(@cols, $self->nbsp($rec->{problemString}))
  830       if ($showColumns{'problems'});
  831         push(@cols, $self->nbsp($rec->{section}))
  832       if ($showColumns{'section'});
  833         push(@cols, $self->nbsp($rec->{recitation}))
  834       if ($showColumns{'recit'});
  835         push(@cols, $rec->{user_id}) if ($showColumns{'login'});
  836 
  837         print CGI::Tr( CGI::td( [ @cols ] ) );
  838     }
  839   }
  840 
  841   print CGI::end_table();
  842 
  843   return "";
  844 }
  845 
  846 
  847 #################################
  848 # Utility function NOT a method
  849 #################################
  850 sub threeSpaceFill {
  851   my $num = shift @_ || 0;
  852 
  853   if (length($num)<=1) {return "$num".'&nbsp;&nbsp;';}
  854   elsif (length($num)==2) {return "$num".'&nbsp;';}
  855   else {return "## ";}
  856 }
  857 sub round_score{
  858   return shift;
  859 }
  860 
  861 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9