--- loncom/interface/lonstatistics.pm 2003/02/25 20:47:47 1.60 +++ loncom/interface/lonstatistics.pm 2003/10/08 15:32:00 1.85 @@ -1,6 +1,6 @@ # The LearningOnline Network with CAPA # -# $Id: lonstatistics.pm,v 1.60 2003/02/25 20:47:47 matthew Exp $ +# $Id: lonstatistics.pm,v 1.85 2003/10/08 15:32:00 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -51,8 +51,7 @@ Main handler for statistics and chart. use Apache::lonproblemstatistics; use Apache::lonstudentassessment; use Apache::lonpercentage; - use GDBM_File; - + use Apache::lonmysql; =over 4 =cut @@ -61,20 +60,31 @@ package Apache::lonstatistics; use strict; use Apache::Constants qw(:common :http); +use vars qw( + @FullClasslist + @Students + @Sections + @SelectedSections + %StudentData + @StudentDataOrder + @SelectedStudentData + $top_map + @Sequences + @SelectedMaps + @Assessments); + use Apache::lonnet(); use Apache::lonhomework; use Apache::loncommon; use Apache::loncoursedata; use Apache::lonhtmlcommon; -use Apache::lonproblemanalysis; -use Apache::lonproblemstatistics; -use Apache::lonstudentassessment; +use Apache::lonproblemanalysis(); +use Apache::lonproblemstatistics(); +use Apache::lonstudentassessment(); use Apache::lonpercentage; -use GDBM_File; - -use vars qw/@FullClasslist @Students @Sections @SelectedSections - $curr_student $prev_student $next_student - $top_map @Sequences @Assessments /; +use Apache::lonmysql; +use Apache::lonlocal; +use Time::HiRes; ####################################################### ####################################################### @@ -104,10 +114,6 @@ use vars qw/@FullClasslist @Students @Se # # Classlist variables # -my @FullClasslist; -my @Students; -my @Sections; -my @SelectedSections; my $curr_student; my $prev_student; my $next_student; @@ -131,6 +137,12 @@ undef the following package variables: =item @SelectedSections +=item %StudentData + +=item @StudentDataOrder + +=item @SelectedStudentData + =item $curr_student =item $prev_student @@ -148,6 +160,8 @@ sub clear_classlist_variables { undef(@Students); undef(@Sections); undef(@SelectedSections); + undef(%StudentData); + undef(@SelectedStudentData); undef($curr_student); undef($prev_student); undef($next_student); @@ -173,6 +187,10 @@ the following package variables: =item @SelectedSections +=item %StudentData + +=item @SelectedStudentData + =item $curr_student =item $prev_student @@ -189,7 +207,6 @@ upon the calling context. ####################################################### ####################################################### sub PrepareClasslist { - my $r = shift; my %Sections; &clear_classlist_variables(); # @@ -201,26 +218,49 @@ sub PrepareClasslist { $cdom,$cnum); if (exists($ENV{'form.Section'})) { if (ref($ENV{'form.Section'})) { - @SelectedSections = @$ENV{'form.Section'}; - # Remove the empty sections - for (my $i=0; $i<=$#SelectedSections; $i++) { - if ($SelectedSections[$i] =~ /^\s*$/) { - splice(@SelectedSections,$i,1); - } - } - } else { - if ($ENV{'form.Section'} !~ /^\s*$/) { - @SelectedSections = ($ENV{'form.Section'}); - } + @SelectedSections = @{$ENV{'form.Section'}}; + } elsif ($ENV{'form.Section'} !~ /^\s*$/) { + @SelectedSections = ($ENV{'form.Section'}); + } + } + @SelectedSections = ('all') if (! @SelectedSections); + foreach (@SelectedSections) { + if ($_ eq 'all') { + @SelectedSections = ('all'); } } - @SelectedSections = ('any') if (! @SelectedSections); + # + # Deal with instructors with restricted section access + if ($ENV{'request.course.sec'} !~ /^\s*$/) { + @SelectedSections = ($ENV{'request.course.sec'}); + } + # + # Set up %StudentData + @StudentDataOrder = qw/fullname username domain id section status/; + foreach my $field (@StudentDataOrder) { + $StudentData{$field}->{'title'} = $field; + $StudentData{$field}->{'base_width'} = length($field); + $StudentData{$field}->{'width'} = + $StudentData{$field}->{'base_width'}; + } + # + # get the status requested + my $requested_status = 'Active'; + $requested_status = $ENV{'form.Status'} if (exists($ENV{'form.Status'})); # # Process the classlist while (my ($student,$student_data) = each (%$classlist)) { my $studenthash = (); for (my $i=0; $i< scalar(@$field_names);$i++) { - $studenthash->{$field_names->[$i]}=$student_data->[$i]; + my $field = $field_names->[$i]; + # Store the data + $studenthash->{$field}=$student_data->[$i]; + # Keep track of the width of the fields + next if (! exists($StudentData{$field})); + my $length = length($student_data->[$i]); + if ($StudentData{$field}->{'width'} < $length) { + $StudentData{$field}->{'width'} = $length; + } } push (@FullClasslist,$studenthash); # @@ -234,7 +274,11 @@ sub PrepareClasslist { # # Only put in the list those students we are interested in foreach my $sect (@SelectedSections) { - if (($sect eq 'any') || ($section eq $sect)) { + if ( (($sect eq 'all') || + ($section eq $sect)) && + (($studenthash->{'status'} eq $requested_status) || + ($requested_status eq 'Any')) + ){ push (@Students,$studenthash); last; } @@ -242,20 +286,25 @@ sub PrepareClasslist { } # # Put the consolidated section data in the right place - @Sections = sort {$a cmp $b} keys(%Sections); + if ($ENV{'request.course.sec'} !~ /^\s*$/) { + @Sections = ($ENV{'request.course.sec'}); + } else { + @Sections = sort {$a cmp $b} keys(%Sections); + unshift(@Sections,'all'); # Put 'all' at the front of the list + } # # Sort the Students my $sortby = 'fullname'; $sortby = $ENV{'form.sort'} if (exists($ENV{'form.sort'})); my @TmpStudents = sort { $a->{$sortby} cmp $b->{$sortby} || $a->{'fullname'} cmp $b->{'fullname'} } @Students; - @Students = @TmpStudents; # # Now deal with that current student thing.... - if (exists($ENV{'form.StudentAssessmentStudent'})) { + $curr_student = undef; + if (exists($ENV{'form.SelectedStudent'})) { my ($current_uname,$current_dom) = - split(':',$ENV{'form.StudentAssessmentStudent'}); + split(':',$ENV{'form.SelectedStudent'}); my $i; for ($i = 0; $i<=$#Students; $i++) { next if (($Students[$i]->{'username'} ne $current_uname) || @@ -263,27 +312,113 @@ sub PrepareClasslist { $curr_student = $Students[$i]; last; # If we get here, we have our student. } - if ($i == 0) { - $prev_student = 'none'; - } else { - $prev_student = $Students[$i-1]; + if (defined($curr_student)) { + if ($i == 0) { + $prev_student = undef; + } else { + $prev_student = $Students[$i-1]; + } + if ($i == $#Students) { + $next_student = undef; + } else { + $next_student = $Students[$i+1]; + } } - if ($i == $#Students) { - $next_student = 'none'; + } + # + if (exists($ENV{'form.StudentData'})) { + if (ref($ENV{'form.StudentData'}) eq 'ARRAY') { + @SelectedStudentData = @{$ENV{'form.StudentData'}}; } else { - $next_student = $Students[$i+1]; + @SelectedStudentData = ($ENV{'form.StudentData'}); } + } else { + @SelectedStudentData = ('username'); } + foreach (@SelectedStudentData) { + if ($_ eq 'all') { + @SelectedStudentData = ('all'); + last; + } + } + # + return; } + ####################################################### ####################################################### -# -# Course Sequences variables -# -my $top_map; -my @Sequences; -my @Assessments; + +=pod + +=item get_students + +Returns a list of the selected students + +=cut + +####################################################### +####################################################### +sub get_students { + if (! @Students) { + &PrepareClasslist() + } + return @Students; +} + +####################################################### +####################################################### + +=pod + +=item ¤t_student() + +Returns a pointer to a hash containing data about the currently +selected student. + +=cut + +####################################################### +####################################################### +sub current_student { + return $curr_student; +} + +####################################################### +####################################################### + +=pod + +=item &previous_student() + +Returns a pointer to a hash containing data about the student prior +in the list of students. Or something. + +=cut + +####################################################### +####################################################### +sub previous_student { + return $prev_student; +} + +####################################################### +####################################################### + +=pod + +=item &next_student() + +Returns a pointer to a hash containing data about the next student +to be viewed. + +=cut + +####################################################### +####################################################### +sub next_student { + return $next_student; +} ####################################################### ####################################################### @@ -307,6 +442,59 @@ sub clear_sequence_variables { =pod +=item &SetSelectedMaps($elementname) + +Sets the @SelectedMaps array from $ENV{'form.'.$elementname}; + +=cut + +####################################################### +####################################################### +sub SetSelectedMaps { + my $elementname = shift; + if (exists($ENV{'form.'.$elementname})) { + if (ref($ENV{'form.'.$elementname})) { + @SelectedMaps = @{$ENV{'form.'.$elementname}}; + } else { + @SelectedMaps = ($ENV{'form.'.$elementname}); + } + } else { + @SelectedMaps = ('all'); + } +} + + +####################################################### +####################################################### + +=pod + +=item &Sequences_with_Assess() + +Returns an array containing the subset of @Sequences which contain +assessments. + +=cut + +####################################################### +####################################################### +sub Sequences_with_Assess { + my @Sequences_to_Show; + foreach my $map_symb (@SelectedMaps) { + foreach my $sequence (@Sequences) { + next if ($sequence->{'symb'} ne $map_symb && $map_symb ne 'all'); + next if ($sequence->{'num_assess'} < 1); + push (@Sequences_to_Show,$sequence); + } + } + return @Sequences_to_Show; +} + +####################################################### +####################################################### + +=pod + =item &PrepareCourseData($r) =cut @@ -316,7 +504,8 @@ sub clear_sequence_variables { sub PrepareCourseData { my ($r) = @_; &clear_sequence_variables(); - my ($top,$sequences,$assessments) = &Apache::loncoursedata::get_sequence_assessment_data(); + my ($top,$sequences,$assessments) = + &Apache::loncoursedata::get_sequence_assessment_data(); if (! defined($top) || ! ref($top)) { # There has been an error, better report it &Apache::lonnet::logthis('top is undefined'); @@ -324,29 +513,146 @@ sub PrepareCourseData { } $top_map = $top if (ref($top)); @Sequences = @{$sequences} if (ref($sequences) eq 'ARRAY'); - @Assessments = @{$assessments} if (ref($assessments) eq 'HASH'); + @Assessments = @{$assessments} if (ref($assessments) eq 'ARRAY'); + # + # Compute column widths + foreach my $seq (@Sequences) { + my $name_length = length($seq->{'title'}); + my $num_parts = $seq->{'num_assess_parts'}; + # + # Use 3 digits for each the sum and total, which means 7 total... + my $num_col = $num_parts+7; + if ($num_col < $name_length) { + $num_col = $name_length; + } + $seq->{'base_width'} = $name_length; + $seq->{'width'} = $num_col; + } + return; +} + +####################################################### +####################################################### =pod - ## - ## Debugging code - ## - foreach my $s (@Sequences) { - next if ($s->{'title'} ne 'Bioenergetics: Enzyme Regulation'); - &Apache::lonnet::logthis('-----------------------------------'); - &Apache::lonnet::logthis('title = '.$s->{'title'}); - &Apache::lonnet::logthis('symb = '.$s->{'symb'}); - &Apache::lonnet::logthis('num_assess = '.$s->{'num_assess'}); - foreach my $a (@{$s->{'contents'}}) { - &Apache::lonnet::logthis(' --------------------------------'); - &Apache::lonnet::logthis(' title = '.$a->{'title'}); - &Apache::lonnet::logthis(' symb = '.$a->{'symb'}); +=item &log_sequence($sequence,$recursive,$padding) + +Write data about the sequence to a logfile. If $recursive is not +undef the data is written recursively. $padding is used for recursive +calls. + +=cut + +####################################################### +####################################################### +sub log_sequence { + my ($seq,$recursive,$padding) = @_; + $padding = '' if (! defined($padding)); + if (ref($seq) ne 'HASH') { + &Apache::lonnet::logthis('log_sequence passed bad sequnce'); + return; + } + &Apache::lonnet::logthis($padding.'sequence '.$seq->{'title'}); + while (my($key,$value) = each(%$seq)) { + next if ($key eq 'contents'); + if (ref($value) eq 'ARRAY') { + for (my $i=0;$i< scalar(@$value);$i++) { + &Apache::lonnet::logthis($padding.$key.'['.$i.']='. + $value->[$i]); + } + } else { + &Apache::lonnet::logthis($padding.$key.'='.$value); } } + if (defined($recursive)) { + &Apache::lonnet::logthis($padding.'-'x20); + &Apache::lonnet::logthis($padding.'contains:'); + foreach my $item (@{$seq->{'contents'}}) { + if ($item->{'type'} eq 'container') { + &log_sequence($item,$recursive,$padding.' '); + } else { + &Apache::lonnet::logthis($padding.'title = '.$item->{'title'}); + while (my($key,$value) = each(%$item)) { + next if ($key eq 'title'); + if (ref($value) eq 'ARRAY') { + for (my $i=0;$i< scalar(@$value);$i++) { + &Apache::lonnet::logthis($padding.$key.'['.$i.']='. + $value->[$i]); + } + } else { + &Apache::lonnet::logthis($padding.$key.'='.$value); + } + } + } + } + &Apache::lonnet::logthis($padding.'end contents of '.$seq->{'title'}); + &Apache::lonnet::logthis($padding.'-'x20); + } + return; +} + +############################################## +############################################## + +=pod + +=item &StudentDataSelect($elementname,$status,$numvisible,$selected) + +Returns html for a selection box allowing the user to choose one (or more) +of the fields of student data available (fullname, username, id, section, etc) + +=over 4 + +=item $elementname The name of the HTML form element + +=item $status 'multiple' or 'single' selection box + +=item $numvisible The number of options to be visible + +=back =cut - return; +############################################## +############################################## +sub StudentDataSelect { + my ($elementname,$status,$numvisible)=@_; + if ($numvisible < 1) { + return; + } + # + # Build the form element + my $Str = "\n"; + $Str .= '\n"; + return $Str; } ############################################## @@ -354,7 +660,7 @@ sub PrepareCourseData { =pod -=item &MapSelect($elementname,$status,$numvisible,$selected,$restriction) +=item &MapSelect($elementname,$status,$numvisible,$restriction) Returns html for a selection box allowing the user to choose one (or more) of the sequences in the course. The values of the sequences are the symbs. @@ -368,10 +674,6 @@ If the top sequence is selected, the val =item $numvisible The number of options to be visible -=item $selected Array ref to the names of the already selected maps. -If undef, $ENV{'form.'.$elementname} is used. -If $ENV{'form.'.$elementname} is also empty, none will be selected. - =item $restriction Code reference to subroutine which returns true or false. The code must expect a reference to a sequence data structure. @@ -382,30 +684,13 @@ false. The code must expect a reference ############################################## ############################################## sub MapSelect { - my ($elementname,$status,$numvisible,$selected,$restriction)=@_; + my ($elementname,$status,$numvisible,$restriction)=@_; if ($numvisible < 1) { return; } # # Set up array of selected items - my @Selected; - if (! defined($selected)) { - if (exists($ENV{'form.'.$elementname})) { - if (ref($ENV{'form.'.$elementname})) { - @Selected = @$ENV{'form.'.$elementname}; - } else { - @Selected = ($ENV{'form.'.$elementname}); - } - } else { - @Selected = (); - } - } else { - if (ref($selected)) { - @Selected = @$selected; - } else { - @Selected = ($selected); - } - } + &SetSelectedMaps($elementname); # # Set up the restriction call if (! defined($restriction)) { @@ -420,23 +705,40 @@ sub MapSelect { } $Str .= 'size="'.$numvisible.'" >'."\n"; # + # Deal with 'all' + foreach (@SelectedMaps) { + if ($_ eq 'all') { + @SelectedMaps = ('all'); + last; + } + } + # + # Put in option for 'all' + $Str .= ' \n"; + $Str .= '>'.$seq->{'title'}."\n"; } $Str .= "\n"; return $Str; } - ############################################## ############################################## @@ -447,6 +749,7 @@ sub MapSelect { Returns html for a selection box allowing the user to choose one (or more) of the sections in the course. +Uses the package variables @Sections and @SelectedSections =over 4 =item $elementname The name of the HTML form element @@ -455,13 +758,6 @@ of the sections in the course. =item $numvisible The number of options to be visible -=item $selected Array ref to the names of the already selected sections. -If undef, $ENV{'form.'.$elementname} is used. -If $ENV{'form.'.$elementname} is also empty, none will be selected. - -=item $restriction Code reference to subroutine which returns true or -false. The code must expect a reference to a sequence data structure. - =back =cut @@ -474,6 +770,11 @@ sub SectionSelect { return; } # + # Make sure we have the data we need to continue + if (! @Sections) { + &PrepareClasslist() + } + # # Build the form element my $Str = "\n"; $Str .= '/; + foreach my $option (@OutputOptions) { + if (exists($option->{'special'}) && + $option->{'special'} =~ /do not show/) { + next; + } + $Str .= "\n".'