File:
[LON-CAPA] /
loncom /
interface /
lonstatistics.pm
Revision
1.49:
download - view:
text,
annotated -
select for diffs
Fri Aug 30 15:35:08 2002 UTC (22 years, 8 months ago) by
stredwic
Branches:
MAIN
CVS tags:
HEAD
Fixed an error for map selection in problem statistics. Added a new
module that will display a graph of percentage correctness for
a problem or series of problems based on input. It doesn't quite
look correct yet, because of graph.gif. I will have to edit graph.gif
in order to get ranges for the x axis etc. This was also an
experiment to see multiple dependent selections. Will probably do
that with student assessment next, but with students.
# The LearningOnline Network with CAPA
# (Publication Handler
#
# $Id: lonstatistics.pm,v 1.49 2002/08/30 15:35:08 stredwic Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#
# (Navigate problems for statistical reports
# YEAR=2001
# 5/5,7/9,7/25/1,8/11,9/13,9/26,10/5,10/9,10/22,10/26 Behrouz Minaei
# 11/1,11/4,11/16,12/14,12/16,12/18,12/20,12/31 Behrouz Minaei
# YEAR=2002
# 1/22,2/1,2/6,2/25,3/2,3/6,3/17,3/21,3/22,3/26,4/7,5/6 Behrouz Minaei
# 5/12,5/14,5/15,5/19,5/26,7/16,25/7,29/7 Behrouz Minaei
#
###
package Apache::lonstatistics;
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 HTML::TokeParser;
use GDBM_File;
sub CheckFormElement {
my ($cache, $ENVName, $cacheName, $default)=@_;
if(defined($ENV{'form.'.$ENVName})) {
$cache->{$cacheName} = $ENV{'form.'.$ENVName};
} elsif(!defined($cache->{$cacheName})) {
$cache->{$cacheName} = $default;
}
return;
}
sub ProcessFormData{
my ($cache)=@_;
$cache->{'reportKey'} = 'false';
&Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
['sort','download',
'reportSelected',
'StudentAssessmentStudent',
'ProblemStatisticsSort']);
&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'}) ||
defined($ENV{'form.NextStudent'}) ||
defined($ENV{'form.PreviousStudent'})) {
$cache->{'reportSelected'} = 'Student Assessment';
}
if(defined($ENV{'form.NextStudent'})) {
$cache->{'StudentAssessmentMove'} = 'next';
} elsif(defined($ENV{'form.PreviousStudent'})) {
$cache->{'StudentAssessmentMove'} = 'previous';
} else {
$cache->{'StudentAssessmentMove'} = 'selected';
}
&CheckFormElement($cache, 'StudentAssessmentStudent',
'StudentAssessmentStudent', 'All Students');
$cache->{'StudentAssessmentStudent'} =
&Apache::lonnet::unescape($cache->{'StudentAssessmentStudent'});
&CheckFormElement($cache, 'DefaultColumns', 'DefaultColumns', 'false');
# Problem analysis
&CheckFormElement($cache, 'Interval', 'Interval', '1');
# ProblemStatistcs
&CheckFormElement($cache, 'DisplayCSVFormat',
'DisplayFormat', 'Display Table Format');
&CheckFormElement($cache, 'ProblemStatisticsAscend',
'ProblemStatisticsAscend', 'Ascending');
&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=();
my @sequenceColumns=();
my $foundColumn = 0;
if(defined($ENV{'form.ReselectColumns'})) {
my @reselected = (ref($ENV{'form.ReselectColumns'}) ?
@{$ENV{'form.ReselectColumns'}}
: ($ENV{'form.ReselectColumns'}));
foreach (@reselected) {
if(/HeadingColumn/) {
push(@headingColumns, $_);
$foundColumn = 1;
} elsif(/SequenceColumn/) {
push(@sequenceColumns, $_);
$foundColumn = 1;
}
}
}
$cache->{'reportKey'} = 'false';
if($cache->{'reportSelected'} eq 'Analyze') {
$cache->{'reportKey'} = 'Analyze';
} elsif($cache->{'reportSelected'} eq 'DoDiffGraph') {
$cache->{'reportKey'} = 'DoDiffGraph';
} elsif($cache->{'reportSelected'} eq 'PercentWrongGraph') {
$cache->{'reportKey'} = 'PercentWrongGraph';
}
if(defined($ENV{'form.DoDiffGraph'})) {
$cache->{'reportSelected'} = 'DoDiffGraph';
$cache->{'reportKey'} = 'DoDiffGraph';
} elsif(defined($ENV{'form.PercentWrongGraph'})) {
$cache->{'reportSelected'} = 'PercentWrongGraph';
$cache->{'reportKey'} = 'PercentWrongGraph';
}
foreach (keys(%ENV)) {
if(/form\.Analyze/) {
$cache->{'reportSelected'} = 'Analyze';
$cache->{'reportKey'} = 'Analyze';
my $data;
(undef, $data)=split(':::', $_);
$cache->{'AnalyzeInfo'}=$data;
} elsif(/form\.HeadingColumn/) {
my $value = $_;
$value =~ s/form\.//;
push(@headingColumns, $value);
$foundColumn=1;
} elsif(/form\.SequenceColumn/) {
my $value = $_;
$value =~ s/form\.//;
push(@sequenceColumns, $value);
$foundColumn=1;
}
}
if($foundColumn) {
$cache->{'HeadingsFound'} = join(':', @headingColumns);
$cache->{'SequencesFound'} = join(':', @sequenceColumns);;
}
if(!defined($cache->{'HeadingsFound'}) ||
$cache->{'DefaultColumns'} ne 'false') {
$cache->{'HeadingsFound'}='HeadingColumnFull Name';
}
if(!defined($cache->{'SequencesFound'}) ||
$cache->{'DefaultColumns'} ne 'false') {
$cache->{'SequencesFound'}='All Sequences';
}
$cache->{'DefaultColumns'} = 'false';
return;
}
=pod
=item &SortStudents()
Determines which students to display and in which order. Which are
displayed are determined by their status(active/expired). The order
is determined by the sort button pressed (default to username). The
type of sorting is username, lastname, or section.
=over 4
Input: $students, $CacheData
$students: A array pointer to a list of students (username:domain)
$CacheData: A pointer to the hash tied to the cached data
Output: \@order
@order: An ordered list of students (username:domain)
=back
=cut
sub SortStudents {
my ($cache)=@_;
my @students = split(':::',$cache->{'NamesOfStudents'});
my @sorted1Students=();
foreach (@students) {
if($cache->{'Status'} eq 'Any' ||
$cache->{$_.':Status'} eq $cache->{'Status'}) {
push(@sorted1Students, $_);
}
}
my $sortBy = '';
if(defined($cache->{'sort'})) {
$sortBy = ':'.$cache->{'sort'};
}
my @order = sort { $cache->{$a.$sortBy} cmp $cache->{$b.$sortBy} ||
$cache->{$a.':fullname'} cmp $cache->{$b.':fullname'} }
@sorted1Students;
return \@order;
}
=pod
=item &SpaceColumns()
Determines the width of all the columns in the chart. It is based on
the max of the data for that column and its header.
=over 4
Input: $students, $studentInformation, $headings, $ChartDB
$students: An array pointer to a list of students (username:domain)
$studentInformatin: The type of data for the student information. It is
used as part of the key in $CacheData.
$headings: The name of the student information columns.
$ChartDB: The name of the cache database which is opened for read/write.
Output: None - All data stored in cache.
=back
=cut
sub SpaceColumns {
my ($students,$studentInformation,$headings,$cache)=@_;
# Initialize Lengths
for(my $index=0; $index<(scalar @$headings); $index++) {
my @titleLength=split(//,$headings->[$index]);
$cache->{$studentInformation->[$index].':columnWidth'}=
scalar @titleLength;
}
foreach my $name (@$students) {
foreach (@$studentInformation) {
my @dataLength=split(//,$cache->{$name.':'.$_});
my $length=(scalar @dataLength);
if($length > $cache->{$_.':columnWidth'}) {
$cache->{$_.':columnWidth'}=$length;
}
}
}
return;
}
sub PrepareData {
my ($c, $cacheDB, $studentInformation, $headings,$r)=@_;
# Test for access to the cache data
my $courseID=$ENV{'request.course.id'};
my $isRecalculate=0;
if(defined($ENV{'form.Recalculate'})) {
$isRecalculate=1;
}
my $isCached = &Apache::loncoursedata::TestCacheData($cacheDB,
$isRecalculate);
if($isCached < 0) {
return "Unable to tie hash to db file.";
}
# Download class list information if not using cached data
my %cache;
unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT(),0640)) {
return "Unable to tie hash to db file.";
}
if(!$isCached) {
my $processTopResourceMapReturn=
&Apache::loncoursedata::ProcessTopResourceMap(\%cache, $c, $r);
if($processTopResourceMapReturn ne 'OK') {
untie(%cache);
return $processTopResourceMapReturn;
}
}
if($c->aborted()) {
untie(%cache);
return 'aborted';
}
my $classlist=&Apache::loncoursedata::DownloadClasslist($courseID,
$cache{'ClasslistTimestamp'},
$c);
foreach (keys(%$classlist)) {
if(/^(con_lost|error|no_such_host)/i) {
untie(%cache);
return "Error getting student data.";
}
}
if($c->aborted()) {
untie(%cache);
return 'aborted';
}
# Active is a temporary solution, remember to change
Apache::loncoursedata::ProcessClasslist(\%cache,$classlist,$courseID,$c);
if($c->aborted()) {
untie(%cache);
return 'aborted';
}
&ProcessFormData(\%cache);
my $students = &SortStudents(\%cache);
&SpaceColumns($students, $studentInformation, $headings, \%cache);
$cache{'updateTime:columnWidth'}=24;
my $download = $cache{'download'};
my $downloadAll = $cache{'DownloadAll'};
my @allStudents=();
if($download ne 'false') {
$cache{'download'} = 'false';
} elsif($downloadAll ne 'false') {
$cache{'DownloadAll'} = 'false';
if($downloadAll eq 'sorted') {
@allStudents = @$students;
} else {
@allStudents = split(':::', $cache{'NamesOfStudents'});
}
}
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') {
return 'Stop at download all';
}
}
return ('OK', $students);
}
sub BuildClasslist {
my ($cacheDB,$students,$studentInformation,$headings,$r)=@_;
my %cache;
unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
return '<html><body>Unable to tie database.</body></html>';
}
my $Str='';
$Str .= '<table border="0"><tr><td bgcolor="#777777">'."\n";
$Str .= '<table border="0" cellpadding="3"><tr bgcolor="#e6ffff">'."\n";
my $displayString = '<td align="left"><a href="/adm/statistics?';
$displayString .= 'sort=LINKDATA">DISPLAYDATA </a></td>'."\n";
$Str .= &Apache::lonhtmlcommon::CreateHeadings(\%cache,
$studentInformation,
$headings, $displayString);
$Str .= '</tr>'."\n";
my $alternate=0;
foreach (@$students) {
my ($username, $domain) = split(':', $_);
if($alternate) {
$Str .= '<tr bgcolor="#ffffe6">';
} else {
$Str .= '<tr bgcolor="#ffffc6">';
}
$alternate = ($alternate + 1) % 2;
foreach my $data (@$studentInformation) {
$Str .= '<td>';
if($data eq 'fullname') {
$Str .= '<a href="/adm/statistics?reportSelected=';
$Str .= &Apache::lonnet::escape('Student Assessment');
$Str .= '&StudentAssessmentStudent=';
$Str .= &Apache::lonnet::escape($cache{$_.':'.$data}).'">';
$Str .= $cache{$_.':'.$data}.' ';
$Str .= '</a>';
} elsif($data eq 'updateTime') {
$Str .= '<a href="/adm/statistics?reportSelected=';
$Str .= &Apache::lonnet::escape('Class list');
$Str .= '&download='.$_.'">';
$Str .= $cache{$_.':'.$data}.' ';
$Str .= ' </a>';
} else {
$Str .= $cache{$_.':'.$data}.' ';
}
$Str .= '</td>'."\n";
}
}
$Str .= '</tr>'."\n";
$Str .= '</table></td></tr></table>'."\n";
$r->print($Str);
$r->rflush();
untie(%cache);
return;
}
sub CreateMainMenu {
my ($status, $reports)=@_;
my $Str = '';
$Str .= '<table border="0"><tbody><tr>'."\n";
$Str .= '<td></td><td></td>'."\n";
$Str .= '<td align="center"><b>Analysis Reports:</b></td>'."\n";
$Str .= '<td align="center"><b>Student Status:</b></td></tr>'."\n";
$Str .= '<tr>'."\n";
$Str .= '<td align="center"><input type="submit" name="Refresh" ';
$Str .= 'value="Refresh" /></td>'."\n";
$Str .= '<td align="center"><input type="submit" name="DownloadAll" ';
$Str .= 'value="Update All Student Data" /></td>'."\n";
$Str .= '<td align="center">';
$Str .= '<select name="reportSelected" onchange="document.';
$Str .= 'Statistics.submit()">'."\n";
foreach (sort(keys(%$reports))) {
next if($_ eq 'reportSelected');
$Str .= '<option name="'.$_.'"';
if($reports->{'reportSelected'} eq $reports->{$_}) {
$Str .= ' selected=""';
}
$Str .= '>'.$reports->{$_}.'</option>'."\n";
}
$Str .= '</select></td>'."\n";
$Str .= '<td align="center">';
$Str .= &Apache::lonhtmlcommon::StatusOptions($status, 'Statistics');
$Str .= '</td>'."\n";
$Str .= '</tr></tbody></table>'."\n";
$Str .= '<hr>'."\n";
return $Str;
}
sub BuildStatistics {
my ($r)=@_;
my $c = $r->connection;
my @studentInformation=('fullname','section','id','domain','username',
'updateTime');
my @headings=('Full Name', 'Section', 'PID', 'Domain', 'User Name',
'Last Updated');
my $spacing = ' ';
my %reports = ('classlist' => 'Class list',
'problem_statistics' => 'Problem Statistics',
'student_assessment' => 'Student Assessment',
'percentage' => 'Percentage Graphs',
# 'activitylog' => 'Activity Log',
'reportSelected' => 'Class list');
my %cache;
my $courseID=$ENV{'request.course.id'};
my $cacheDB = "/home/httpd/perl/tmp/$ENV{'user.name'}".
"_$ENV{'user.domain'}_$courseID\_statistics.db";
$r->print(&Apache::lonhtmlcommon::Title('Course Statistics and Charts'));
my ($returnValue, $students) = &PrepareData($c, $cacheDB,
\@studentInformation,
\@headings,$r);
if($returnValue ne 'OK') {
$r->print($returnValue."\n".'</body></html>');
return OK;
}
if(!$c->aborted()) {
&Apache::loncoursedata::CheckForResidualDownload($cacheDB,
'true', 'true',
$courseID,
$r, $c);
}
my $GoToPage;
if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
$GoToPage = $cache{'reportSelected'};
$reports{'reportSelected'} = $cache{'reportSelected'};
if(defined($cache{'reportKey'}) &&
!exists($reports{$cache{'reportKey'}}) &&
$cache{'reportKey'} ne 'false') {
$reports{$cache{'reportKey'}} = $cache{'reportSelected'};
}
if(defined($cache{'OptionResponses'})) {
$reports{'problem_analysis'} = 'Option Response Analysis';
}
$r->print('<form name="Statistics" ');
$r->print('method="post" action="/adm/statistics">');
$r->print(&CreateMainMenu($cache{'Status'}, \%reports));
$r->rflush();
untie(%cache);
} else {
$r->print('<html><body>Unable to tie database.</body></html>');
return OK;
}
if($GoToPage eq 'Activity Log') {
&Apache::lonproblemstatistics::Activity();
} elsif($GoToPage eq 'Problem Statistics') {
&Apache::lonproblemstatistics::BuildProblemStatisticsPage($cacheDB,
$students,
$courseID,
$c,$r);
} elsif($GoToPage eq 'Option Response Analysis') {
&Apache::lonproblemanalysis::BuildProblemAnalysisPage($cacheDB, $r);
} elsif($GoToPage eq 'Student Assessment') {
&Apache::lonstudentassessment::BuildStudentAssessmentPage($cacheDB,
$students,
$courseID,
'Statistics',
\@headings,
$spacing,
\@studentInformation,
$r, $c);
} elsif($GoToPage eq 'Analyze') {
&Apache::lonproblemanalysis::BuildAnalyzePage($cacheDB, $students,
$courseID, $r);
} elsif($GoToPage eq 'DoDiffGraph' || $GoToPage eq 'PercentWrongGraph') {
my $courseDescription = $ENV{'course.'.$courseID.'.description'};
$courseDescription =~ s/\ /"_"/eg;
&Apache::lonproblemstatistics::BuildGraphicChart($GoToPage, $cacheDB,
$courseDescription,
$students, $courseID,
$r, $c);
} elsif($GoToPage eq 'Class list') {
&BuildClasslist($cacheDB, $students, \@studentInformation,
\@headings, $r);
} elsif($GoToPage eq 'Percentage Graphs') {
&Apache::lonpercentage::BuildPercentageGraph($cacheDB, $students,
$courseID, $c, $r);
}
$r->print('</form>'."\n");
$r->print("\n".'</body>'."\n".'</html>');
$r->rflush();
return OK;
}
# ================================================================ Main Handler
sub handler {
my $r=shift;
# $jr = $r;
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;
}
# Set document type for header only
if($r->header_only) {
if ($ENV{'browser.mathml'}) {
$r->content_type('text/xml');
} else {
$r->content_type('text/html');
}
&Apache::loncommon::no_cache($r);
$r->send_http_header;
return OK;
}
unless($ENV{'request.course.fn'}) {
my $requrl=$r->uri;
$ENV{'user.error.msg'}="$requrl:bre:0:0:Course not initialized";
return HTTP_NOT_ACCEPTABLE;
}
$r->content_type('text/html');
$r->send_http_header;
&BuildStatistics($r);
return OK;
}
1;
__END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>