File:
[LON-CAPA] /
loncom /
interface /
lonstatistics.pm
Revision
1.33:
download - view:
text,
annotated -
select for diffs
Fri Jul 26 16:22:09 2002 UTC (22 years, 9 months ago) by
stredwic
Branches:
MAIN
CVS tags:
HEAD
Added section selection. I added a new multiselect box for sections. All
students without a section number or a space or undefined will fall under
the none category. A list of possible sections for a course will be displayed.
Only students with a section number matching one of the selected sections
will be selectable in the students menu and/or display their report. If
the currently view student doesn't have the correct section when the section
selection changes, the student selection will revert to no student selected.
Note: To refresh the reports after changing which sections to display, press
the refresh button or any of the other onchange interface controls.
# The LearningOnline Network with CAPA
# (Publication Handler
#
# $Id: lonstatistics.pm,v 1.33 2002/07/26 16:22:09 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 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::lonchart;
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']);
&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');
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'});
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'};
}
foreach (keys(%ENV)) {
if(/form\.Analyze:::/) {
# $cache->{'reportSelected'} = 'Analyze';
# $cache->{'reportKey'} = 'Problem Analysis';
my ($uri, $title, $part, $problem);
(undef, $uri, $title, $part, $problem)=split(':::', $_);
$cache->{'AnalyzeURI'} = $uri;
$cache->{'AnalyzeTitle'} = $title;
$cache->{'AnalyzePart'} = $part;
$cache->{'AnalyzeProblem'} = $problem;
&CheckFormElement($cache, 'Interval', 'Interval', '1');
}
}
return;
# Select page to display
if(defined($ENV{'form.ProblemStatistics'}) ||
defined($ENV{'form.ProblemStatisticsRecalculate'}) ||
defined($ENV{'form.DisplayCSVFormat'})) {
$cache->{'GoToPage'} = 'ProblemStatistics';
&CheckFormElement($cache, 'DisplayCSVFormat',
'DisplayFormat', 'Display Table Format');
&CheckFormElement($cache, 'Ascend','ProblemStatisticsAscend',
'Ascending');
&CheckFormElement($cache, 'Maps', 'ProblemStatisticsMap',
'All Maps');
} elsif(defined($ENV{'form.ProblemAnalysis'})) {
$cache->{'GoToPage'} = 'ProblemAnalysis';
&CheckFormElement($cache, 'Interval', 'Interval', '1');
} elsif(defined($ENV{'form.DoDiffGraph'})) {
$cache->{'GoToPage'} = 'DoDiffGraph';
} elsif(defined($ENV{'form.PercentWrongGraph'})) {
$cache->{'GoToPage'} = 'PercentWrongGraph';
} elsif(defined($ENV{'form.ActivityLog'})) {
$cache->{'GoToPage'} = 'ActivityLog';
} else {
$cache->{'GoToPage'} = 'Menu';
}
&CheckFormElement($cache, 'Status', 'Status', 'Active');
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)=@_;
# 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);
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;
if($cache{'download'} ne 'false') {
my $who = $cache{'download'};
my $courseData =
&Apache::loncoursedata::DownloadCourseInformation(
$who, $courseID,
$cache{$who.':lastDownloadTime'});
&Apache::loncoursedata::ProcessStudentData(\%cache, $courseData, $who);
$cache{'download'} = 'false';
} elsif($cache{'DownloadAll'} ne 'false') {
my @allStudents;
if($cache{'DownloadAll'} eq 'sorted') {
@allStudents = @$students;
} else {
@allStudents = split(':::', $cache{'NamesOfStudents'});
}
foreach (@allStudents) {
my $courseData =
&Apache::loncoursedata::DownloadCourseInformation(
$_, $courseID,
$cache{$_.':lastDownloadTime'});
&Apache::loncoursedata::ProcessStudentData(\%cache, $courseData,
$_);
if($c->aborted()) {
untie(%cache);
return 'aborted';
}
}
$cache{'DownloadAll'} = 'false';
}
if($c->aborted()) {
untie(%cache);
return 'aborted';
}
untie(%cache);
return ('OK', $students);
}
# Create progress
sub Create_PrgWin {
my ($r)=@_;
$r->print(<<ENDPOP);
<script>
popwin=open('','popwin','width=400,height=100');
popwin.document.writeln('<html><body bgcolor="#88DDFF">'+
'<title>LON-CAPA Statistics</title>'+
'<h4>Computation Progress</h4>'+
'<form name=popremain>'+
'<input type=text size=35 name=remaining value=Starting></form>'+
'</body></html>');
popwin.document.close();
</script>
ENDPOP
$r->rflush();
}
# update progress
sub Update_PrgWin {
my ($totalStudents,$index,$name,$r)=@_;
$r->print('<script>popwin.document.popremain.remaining.value="'.
'Computing '.$index.'/'.$totalStudents.': '.
$name.'";</script>');
$r->rflush();
}
# close Progress Line
sub Close_PrgWin {
my ($r)=@_;
$r->print('<script>popwin.close()</script>');
$r->rflush();
}
# For loading the colored table for display or un-colored for print
sub setbgcolor {
my $PrintTable=shift;
my %color;
if ($PrintTable){
$color{"gb"}="#FFFFFF";
$color{"red"}="#FFFFFF";
$color{"yellow"}="#FFFFFF";
$color{"green"}="#FFFFFF";
$color{"purple"}="#FFFFFF";
} else {
$color{"gb"}="#DDFFFF";
$color{"red"}="#FFDDDD";
$color{"yellow"}="#EEFFCC";
$color{"green"}="#DDFFDD";
$color{"purple"}="#FFDDFF";
}
return \%color;
}
sub BuildClasslist {
my ($cacheDB,$students,$studentInformation,$headings)=@_;
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";
untie(%cache);
return $Str;
}
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',
'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";
my %color=&setbgcolor(0);
my ($returnValue, $students) = &PrepareData($c, $cacheDB,
\@studentInformation,
\@headings);
if($returnValue ne 'OK') {
$r->print('<html><body>'.$returnValue."\n".'</body></html>');
return OK;
}
my $GoToPage;
if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {
$GoToPage = $cache{'reportSelected'};
$reports{'reportSelected'} = $cache{'reportSelected'};
# if(defined($cache{'reportKey'}) && $cache{'reportKey'} ne 'false') {
# $reports{$cache{'reportKey'}} = $cache{'reportSelected'};
# }
if(defined($cache{'OptionResponses'})) {
$reports{'problem_analysis'} = 'Problem Analysis';
}
$r->print(&Apache::lonhtmlcommon::Title('LON-CAPA Statistics'));
$r->print('<form name="Statistics" ');
$r->print('method="post" action="/adm/statistics">');
$r->print(&CreateMainMenu($cache{'Status'}, \%reports));
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') {
$r->print(
&Apache::lonproblemstatistics::BuildProblemStatisticsPage($cacheDB,
$students,
$courseID,
$c,$r,
\%color));
} elsif($GoToPage eq 'Problem Analysis') {
$r->print(
&Apache::lonproblemanalysis::BuildProblemAnalysisPage($cacheDB));
} elsif($GoToPage eq 'Student Assessment') {
$r->print(
&Apache::lonstudentassessment::BuildStudentAssessmentPage($cacheDB,
$students,
$courseID,
'Statistics',
\@headings,
$spacing,
\@studentInformation,
$r, $c));
} elsif($GoToPage eq 'Analyze') {
$r->print(&Apache::lonproblemanalysis::BuildAnalyzePage($cacheDB,
$students,
$courseID));
} elsif($GoToPage eq 'DoDiffGraph') {
&Apache::lonproblemstatistics::BuildDiffGraph($r);
} elsif($GoToPage eq 'PercentWrongGraph') {
&Apache::lonproblemstatistics::BuildWrongGraph($r);
} elsif($GoToPage eq 'Class list') {
$r->print(&BuildClasslist($cacheDB, $students, \@studentInformation,
\@headings));
}
$r->print('</form>'."\n");
$r->print("\n".'</body>'."\n".'</html>');
$r->rflush();
return OK;
}
# ================================================================ Main Handler
sub handler {
my $r=shift;
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>