--- loncom/interface/lonstatistics.pm 2002/08/13 00:37:18 1.41 +++ loncom/interface/lonstatistics.pm 2003/02/18 20:27:33 1.59 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # (Publication Handler # -# $Id: lonstatistics.pm,v 1.41 2002/08/13 00:37:18 stredwic Exp $ +# $Id: lonstatistics.pm,v 1.59 2003/02/18 20:27:33 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -35,7 +35,36 @@ # ### -package Apache::lonstatistics; +=pod + +=head1 NAME + +lonstatistics + +=head1 SYNOPSIS + +Main handler for statistics and chart. + +=head1 PACKAGES USED + +use strict; +use Apache::Constants qw(:common :http); +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::lonpercentage; +use GDBM_File; + +=over 4 + +=cut + +package Apache::lonstatistics; use strict; use Apache::Constants qw(:common :http); @@ -47,9 +76,163 @@ use Apache::lonhtmlcommon; use Apache::lonproblemanalysis; use Apache::lonproblemstatistics; use Apache::lonstudentassessment; -use HTML::TokeParser; +use Apache::lonpercentage; use GDBM_File; +my @FullClasslist; +my @Students; +my @Sections; +my $curr_student; +my $prev_student; +my $next_student; + +####################################################### +####################################################### + +=pod + +=item &clear_classlist_variables() + +undef the following package variables: + +=over + +=item @FullClasslist The full classlist + +=item @Students The students we are concerned with for this invocation + +=item @Sections The sections available in this class + +=item $curr_student The student currently being examined + +=item $prev_student The student previous in the classlist + +=item $next_student The student next in the classlist + +=back + +=cut + +####################################################### +####################################################### +sub clear_classlist_variables { + undef(@FullClasslist); + undef(@Students); + undef(@Sections); + undef($curr_student); + undef($prev_student); + undef($next_student); +} + +####################################################### +####################################################### + +=pod + +=item &PrepareClasslist() + +Build up the classlist information. The classlist information is kept in +the following package variables: + +=over + +=item @FullClasslist The full classlist + +=item @Students The students we are concerned with for this invocation + +=item @Sections The sections available in this class + +=item $curr_student The student currently being examined + +=item $prev_student The student previous in the classlist + +=item $next_student The student next in the classlist + +=back + +$curr_student, $prev_student, and $next_student may not be defined, depending +upon the calling context. + +=cut + +####################################################### +####################################################### +sub PrepareClasslist { + my $r = shift; + my %Sections; + &clear_classlist_variables(); + # + # Retrieve the classlist + my $cid = $ENV{'request.course.id'}; + my $cdom = $ENV{'course.'.$cid.'.domain'}; + my $cnum = $ENV{'course.'.$cid.'.num'}; + my ($classlist,$field_names) = &Apache::loncoursedata::get_classlist($cid, + $cdom,$cnum); + my %valid_section; + if (exists($ENV{'form.Section'}) && $ENV{'form.Section'} !~ /(all|any)/) { + if (ref($ENV{'form.Section'})) { + foreach (@$ENV{'form.section'}) { + $valid_section{$_}++; + } + } else { + $valid_section{$_}++; + } + } + # + # 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]; + } + push (@FullClasslist,$studenthash); + # + # Build up a list of sections + my $section = $studenthash->{'section'}; + $section = 'no section' if (! defined($section) || $section =~/^\s*/ ); + $Sections{$section}++; + # + # Only put in the list those students we are interested in + if (defined($ENV{'form.Section'}) && + $ENV{'form.Section'} !~ /(all|any)/ && + ! exists($valid_section{$section})) { + next; + } + push (@Students,$studenthash); + } + # + # Put the consolidated section data in the right place + @Sections = sort {$a<=>$b} keys(%Sections); + # + # Sort the Students + my $sortby = 'fullname'; + @Students = sort {$a->{$sortby} <=> $b->{$sortby}} @Students; + # + # Now deal with that current student thing.... + if (exists($ENV{'form.StudentAssessmentStudent'})) { + my ($current_uname,$current_dom) = + split(':',$ENV{'form.StudentAssessmentStudent'}); + my $i; + for ($i = 0; $i<=$#Students; $i++) { + next if (($Students[$i]->{'username'} ne $current_uname) || + ($Students[$i]->{'domain'} ne $current_dom)); + last; # If we get here, we have our student. + } + if ($i == 0) { + $prev_student = 'none'; + } else { + $prev_student = $Students[$i-1]; + } + if ($i == $#Students) { + $next_student = 'none'; + } else { + $next_student = $Students[$i+1]; + } + } +} + +####################################################### +####################################################### sub CheckFormElement { my ($cache, $ENVName, $cacheName, $default)=@_; @@ -73,15 +256,41 @@ sub ProcessFormData{ 'reportSelected', 'StudentAssessmentStudent', 'ProblemStatisticsSort']); + &CheckFormElement($cache, 'DownloadAll', 'DownloadAll', 'false'); + if ($cache->{'DownloadAll'} ne 'false') { + # Clean the hell out of that cache! + # We cannot untie the hash at this scope (stupid libgd :( ) + # So, remove every single key. What a waste of time.... + # Of course, if you are doing this you are probably resigned + # to waiting a while. + &Apache::lonnet::logthis("Cleaning out the cache file"); + while (my ($key,undef)=each(%$cache)) { + next if ($key eq 'DownloadAll'); + delete($cache->{$key}); + } + } &CheckFormElement($cache, 'Status', 'Status', 'Active'); &CheckFormElement($cache, 'postdata', 'reportSelected', 'Class list'); &CheckFormElement($cache, 'reportSelected', 'reportSelected', 'Class list'); $cache->{'reportSelected'} = &Apache::lonnet::unescape($cache->{'reportSelected'}); - &CheckFormElement($cache, 'DownloadAll', 'DownloadAll', 'false'); &CheckFormElement($cache, 'sort', 'sort', 'fullname'); &CheckFormElement($cache, 'download', 'download', 'false'); + &CheckFormElement($cache, 'StatisticsMaps', + 'StatisticsMaps', 'All Maps'); + &CheckFormElement($cache, 'StatisticsProblemSelect', + 'StatisticsProblemSelect', 'All Problems'); + &CheckFormElement($cache, 'StatisticsPartSelect', + 'StatisticsPartSelect', 'All Parts'); + if(defined($ENV{'form.Section'})) { + my @sectionsSelected = (ref($ENV{'form.Section'}) ? + @{$ENV{'form.Section'}} : + ($ENV{'form.Section'})); + $cache->{'sectionsSelected'} = join(':', @sectionsSelected); + } elsif(!defined($cache->{'sectionsSelected'})) { + $cache->{'sectionsSelected'} = $cache->{'sectionList'}; + } # student assessment if(defined($ENV{'form.CreateStudentAssessment'}) || @@ -102,15 +311,6 @@ sub ProcessFormData{ &Apache::lonnet::unescape($cache->{'StudentAssessmentStudent'}); &CheckFormElement($cache, 'DefaultColumns', 'DefaultColumns', 'false'); - if(defined($ENV{'form.Section'})) { - my @sectionsSelected = (ref($ENV{'form.Section'}) ? - @{$ENV{'form.Section'}} : - ($ENV{'form.Section'})); - $cache->{'sectionsSelected'} = join(':', @sectionsSelected); - } elsif(!defined($cache->{'sectionsSelected'})) { - $cache->{'sectionsSelected'} = $cache->{'sectionList'}; - } - # Problem analysis &CheckFormElement($cache, 'Interval', 'Interval', '1'); @@ -119,10 +319,12 @@ sub ProcessFormData{ 'DisplayFormat', 'Display Table Format'); &CheckFormElement($cache, 'ProblemStatisticsAscend', 'ProblemStatisticsAscend', 'Ascending'); - &CheckFormElement($cache, 'ProblemStatisticsMaps', - 'ProblemStatisticsMaps', 'All Maps'); &CheckFormElement($cache, 'ProblemStatisticsSort', 'ProblemStatisticsSort', 'Homework Sets Order'); + &CheckFormElement($cache, 'DisplayLegend', 'DisplayLegend', + 'Hide Legend'); + &CheckFormElement($cache, 'SortProblems', 'SortProblems', + 'Sort Within Sequence'); # Search only form elements my @headingColumns=(); @@ -237,9 +439,11 @@ sub SortStudents { my $sortBy = ''; if(defined($cache->{'sort'})) { $sortBy = ':'.$cache->{'sort'}; + } else { + $sortBy = ':fullname'; } - my @order = sort { $cache->{$a.$sortBy} cmp $cache->{$b.$sortBy} || - $cache->{$a.':fullname'} cmp $cache->{$b.':fullname'} } + my @order = sort { lc($cache->{$a.$sortBy}) cmp lc($cache->{$b.$sortBy}) || + lc($cache->{$a.':fullname'}) cmp lc($cache->{$b.':fullname'}) } @sorted1Students; return \@order; @@ -304,7 +508,7 @@ sub PrepareData { $isRecalculate=1; } - my $isCached = &Apache::loncoursedata::TestCacheData($cacheDB, + my $isCached = &Apache::loncoursedata::TestCacheData($cacheDB, $isRecalculate); if($isCached < 0) { return "Unable to tie hash to db file."; @@ -316,14 +520,14 @@ sub PrepareData { return "Unable to tie hash to db file."; } - if(!$isCached) { +# if(!$isCached) { my $processTopResourceMapReturn= - &Apache::loncoursedata::ProcessTopResourceMap(\%cache, $c, $r); + &Apache::loncoursedata::ProcessTopResourceMap(\%cache, $c); if($processTopResourceMapReturn ne 'OK') { untie(%cache); return $processTopResourceMapReturn; } - } + # } if($c->aborted()) { untie(%cache); @@ -357,41 +561,40 @@ sub PrepareData { &SpaceColumns($students, $studentInformation, $headings, \%cache); $cache{'updateTime:columnWidth'}=24; - if($cache{'download'} ne 'false') { - my @who = ($cache{'download'}); + my $download = $cache{'download'}; + my $downloadAll = $cache{'DownloadAll'}; + my @allStudents=(); + if($download ne 'false') { $cache{'download'} = 'false'; - if(&Apache::loncoursedata::DownloadStudentCourseData(\@who, 'false', - $cacheDB, 'true', - 'false', $courseID, - $r, $c) ne 'OK') { - untie(%cache); - return 'Stop at download individual'; - } - } elsif($cache{'DownloadAll'} ne 'false') { + } elsif($downloadAll ne 'false') { $cache{'DownloadAll'} = 'false'; - my @allStudents; - if($cache{'DownloadAll'} eq 'sorted') { + if($downloadAll eq 'sorted') { @allStudents = @$students; } else { @allStudents = split(':::', $cache{'NamesOfStudents'}); } - if(&Apache::loncoursedata::DownloadStudentCourseData(\@allStudents, - 'false', - $cacheDB, 'true', + } + + untie(%cache); + + if($download ne 'false') { + my @who = ($download); + if(&Apache::loncoursedata::DownloadStudentCourseData(\@who, 'false', + $cacheDB, 'true', + 'false', $courseID, + $r, $c) ne 'OK') { + return 'Stop at download individual'; + } + } elsif($downloadAll ne 'false') { + if(&Apache::loncoursedata::DownloadStudentCourseData(\@allStudents, + 'false', + $cacheDB, 'true', 'true', $courseID, $r, $c) ne 'OK') { - untie(%cache); return 'Stop at download all'; } } - if($c->aborted()) { - untie(%cache); - return 'aborted'; - } - - untie(%cache); - return ('OK', $students); } @@ -403,19 +606,37 @@ sub BuildClasslist { return 'Unable to tie database.'; } +# my $Ptr = ''; +# $Ptr .= ''; +# $Ptr .= ''."\n"; +# $Ptr .= '
Select Sections'; +# $Ptr .= ''."\n"; +# my @sectionsSelected = split(':',$cache{'sectionsSelected'}); +# my @sections = split(':',$cache{'sectionList'}); +# $Ptr .= &Apache::lonhtmlcommon::MultipleSectionSelect(\@sections, +# \@sectionsSelected, +# 'Statistics'); +# $Ptr .= '

'; +# $r->print($Ptr); +# $r->rflush(); +# my %mySections = (); +# foreach (@sections) { $mySections{$_} = 'True'; } +# $r->print("
$cache{'sectionsSelected'}
"); + my $Str=''; $Str .= '
'."\n"; $Str .= ''."\n"; my $displayString = ''."\n"; - $Str .= &Apache::lonhtmlcommon::CreateHeadings(\%cache, + $Str .= &Apache::lonhtmlcommon::CreateHeadings(\%cache, $studentInformation, $headings, $displayString); $Str .= ''."\n"; my $alternate=0; foreach (@$students) { +# if ($mySections{$cache{$_.':'.'section'}} ne 'True') {next;} my ($username, $domain) = split(':', $_); if($alternate) { $Str .= ''; @@ -463,8 +684,8 @@ sub CreateMainMenu { $Str .= '
DISPLAYDATA 
'."\n"; $Str .= ''."\n"; - $Str .= ''."\n"; - $Str .= ''."\n"; + $Str .= ''."\n"; + $Str .= ''."\n"; $Str .= ''."\n"; $Str .= '
Analysis Reports:Student Status:
Select a ReportStudent Status
'Class list', 'problem_statistics' => 'Problem Statistics', 'student_assessment' => 'Student Assessment', + 'percentage' => 'Correct-problems Plot', # 'activitylog' => 'Activity Log', 'reportSelected' => 'Class list'); @@ -514,17 +737,17 @@ sub BuildStatistics { my $cacheDB = "/home/httpd/perl/tmp/$ENV{'user.name'}". "_$ENV{'user.domain'}_$courseID\_statistics.db"; - $r->print(&Apache::lonhtmlcommon::Title('LON-CAPA Statistics')); + $r->print(&Apache::lonhtmlcommon::Title('Course Statistics and Charts')); - my ($returnValue, $students) = &PrepareData($c, $cacheDB, - \@studentInformation, + my ($returnValue, $students) = &PrepareData($c, $cacheDB, + \@studentInformation, \@headings,$r); if($returnValue ne 'OK') { $r->print($returnValue."\n".''); return OK; } if(!$c->aborted()) { - &Apache::loncoursedata::CheckForResidualDownload($cacheDB, + &Apache::loncoursedata::CheckForResidualDownload($cacheDB, 'true', 'true', $courseID, $r, $c); @@ -534,14 +757,14 @@ sub BuildStatistics { if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) { $GoToPage = $cache{'reportSelected'}; $reports{'reportSelected'} = $cache{'reportSelected'}; - if(defined($cache{'reportKey'}) && - !exists($reports{$cache{'reportKey'}}) && + if(defined($cache{'reportKey'}) && + !exists($reports{$cache{'reportKey'}}) && $cache{'reportKey'} ne 'false') { $reports{$cache{'reportKey'}} = $cache{'reportSelected'}; } if(defined($cache{'OptionResponses'})) { - $reports{'problem_analysis'} = 'Problem Analysis'; + $reports{'problem_analysis'} = 'Option Response Analysis'; } $r->print('
print('
'."\n"); @@ -596,10 +827,17 @@ sub handler { # $jr = $r; + my $loaderror=&Apache::lonnet::overloaderror($r); + if ($loaderror) { return $loaderror; } + $loaderror= + &Apache::lonnet::overloaderror($r, + $ENV{'course.'.$ENV{'request.course.id'}.'.home'}); + if ($loaderror) { return $loaderror; } + unless(&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'})) { $ENV{'user.error.msg'}= $r->uri.":vgr:0:0:Cannot view grades for complete course"; - return HTTP_NOT_ACCEPTABLE; + return HTTP_NOT_ACCEPTABLE; } # Set document type for header only @@ -617,16 +855,25 @@ sub handler { unless($ENV{'request.course.fn'}) { my $requrl=$r->uri; $ENV{'user.error.msg'}="$requrl:bre:0:0:Course not initialized"; - return HTTP_NOT_ACCEPTABLE; + return HTTP_NOT_ACCEPTABLE; } $r->content_type('text/html'); $r->send_http_header; + &PrepareClasslist($r); + &BuildStatistics($r); return OK; } 1; + +=pod + +=back + +=cut + __END__