--- loncom/interface/lonstatistics.pm 2002/02/06 16:38:04 1.3
+++ loncom/interface/lonstatistics.pm 2006/05/01 05:39:44 1.130
@@ -1,7 +1,6 @@
# The LearningOnline Network with CAPA
-# (Publication Handler
#
-# $Id: lonstatistics.pm,v 1.3 2002/02/06 16:38:04 minaeibi Exp $
+# $Id: lonstatistics.pm,v 1.130 2006/05/01 05:39:44 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -26,1037 +25,1249 @@
# http://www.lon-capa.org/
#
# (Navigate problems for statistical reports
-# YEAR=2001
-# 5/05/01, 7/09/01, 7/25/01, 8/11/01,9/13/01, 9/26/01 Behrouz Minaei
-# 10/5/01, 10/9/01, 10/22/01, 10/26/01 Behrouz Minaei
-# 11/1/01, 11/4/01, 11/16/01 Behrouz Minaei
-# 12/14/01, 12/16/01, 12/18/01,12/20/01,12/31/01 Behrouz Minaei
-# YEAR=2002
-# 1/22/02,2/1/02 Behrouz Minaei
+#
###
-package Apache::lonstatistics;
+=pod
+
+=head1 NAME
+
+lonstatistics
+
+=head1 SYNOPSIS
+
+Main handler for statistics and chart.
+
+=over 4
+
+=cut
+
+package Apache::lonstatistics;
use strict;
use Apache::Constants qw(:common :http);
-use Apache::lonnet();
+use vars qw(
+ @FullClasslist
+ @Students
+ @Sections
+ @Groups
+ %StudentData
+ @StudentDataOrder
+ @SelectedStudentData
+ $enrollment_status);
+
+use Apache::lonnet;
use Apache::lonhomework;
-use HTML::TokeParser;
-use GDBM_File;
-#use Benchmark;
-
-# -------------------------------------------------------------- Module Globals
-my %hash;
-my %CachData;
-my %GraphDat;
-my %maps;
-my @mapsort;
-my %section;
-my %StuBox;
-my %DiscFac;
-my $CurMap;
-my $CurSec;
-my $CurStu;
-my @cols;
-my @list;
-my @students;
-my $p_count;
-my $Pos;
-my $r;
-my $OpSel1;
-my $OpSel2;
-my $OpSelDis1;
-my $OpSelDis2;
-my $CurDis=0;
-my $OpSel3;
-my $OpSel4;
-my $GData;
-my $cid;
-my $firstres;
-my $lastres;
-my $DiscFlag=0;
-my $HWN=0;
-
-my %Header = (0,"Problem Title",1,"#Stdnts",2,"Tries",3,"Mod",
- 4,"Mean",5,"#YES",6,"#yes",7,"%Wrng",8,"S.D.",
- 9,"Skew.",10,"DoDiff",11,"Map");
-# 9,"Skew.",10,"DoDiff",11,"Dis.F.",12,"Resourse URL");
+use Apache::loncommon;
+use Apache::loncoursedata;
+use Apache::lonhtmlcommon;
+use Apache::lonmysql;
+use Apache::lonlocal;
+use Time::HiRes;
+#
+# Statistics Packages
+use Apache::lonproblemanalysis();
+use Apache::lonsubmissiontimeanalysis();
+use Apache::loncorrectproblemplot();
+use Apache::lonproblemstatistics();
+use Apache::lonstudentassessment();
+use Apache::lonpercentage;
+use Apache::lonstudentsubmissions();
+use Apache::lonsurveyreports();
+use Apache::longradinganalysis();
+
+#######################################################
+#######################################################
+
+=pod
-sub NumericSort {
- $a <=> $b;
+=item Package Variables
+
+=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 @Groups The groups available in the 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
+
+=over
+
+=cut
+
+#######################################################
+#######################################################
+#
+# Classlist variables
+#
+my $curr_student;
+my $prev_student;
+my $next_student;
+
+#######################################################
+#######################################################
+
+=pod
+
+=item &clear_classlist_variables()
+
+undef the following package variables:
+
+=over
+
+=item @FullClasslist
+
+=item @Students
+
+=item @Sections
+
+=item @Groups
+
+=item %StudentData
+
+=item @StudentDataOrder
+
+=item @SelectedStudentData
+
+=item $curr_student
+
+=item $prev_student
+
+=item $next_student
+
+=back
+
+=cut
+
+#######################################################
+#######################################################
+sub clear_classlist_variables {
+ undef(@FullClasslist);
+ undef(@Students);
+ undef(@Sections);
+ undef(@Groups);
+ undef(%StudentData);
+ undef(@SelectedStudentData);
+ undef($curr_student);
+ undef($prev_student);
+ undef($next_student);
}
-# ------ Create different Student Report
-sub StudentReport {
- my ($sname,$sdom)=@_;
- if ( $sname eq 'All Students' ) {
- $r->print( '
WARNING:
- Please select a student
' );
- return;
- }
- my $shome=&Apache::lonnet::homeserver( $sname,$sdom );
- my $reply=&Apache::lonnet::reply('dump:'.$sdom.':'.$sname.':'.$cid,$shome );
- my %result = ();
- my $ResId;
- my $Code;
- my $Tries;
- my $TotalTries = 0;
- my $ParCr = 0;
- my $Wrongs;
- my %TempHash;
- my $Version;
- my $LatestVersion;
- my $PtrTry='';
- my $PtrCod='';
- my $SetNo=0;
- my $Str = "\n".'
";
- }
- $PtrTry='';
- $PtrCod='';
- next;
- }
- $ResId=~/(\d+)\.(\d+)/;
- my $Map = &Apache::lonnet::declutter( $hash{'map_id_'.$1} );
- if ( $CurMap ne 'All Maps' ) {
- my ( $ResMap, $NameMap ) = split(/\=/,$CurMap);
- if ( $Map ne $ResMap ) { next; }
- }
- my $meta=$hash{'src_'.$ResId};
- my $PartNo = 0;
- undef %TempHash;
- map {
- if ($_=~/^stores\_(\d+)\_tries$/) {
- my $Part=&Apache::lonnet::metadata($meta,$_.'.part');
- if ( $TempHash{"$Part"} eq '' ) {
- $TempHash{"$Part"} = $Part;
- $TempHash{$PartNo}=$Part;
- $TempHash{"$Part.Code"} = '-';
- $PartNo++;
- }
- }
- } split(/\,/,&Apache::lonnet::metadata($meta,'keys'));
-
- my $Prob = $Map.'___'.$2.'___'.
- &Apache::lonnet::declutter( $hash{'src_'.$ResId} );
- $Code='U';
- $Tries = 0;
- $Wrongs = 0;
- $LatestVersion = $result{"version:$Prob"};
-
- if ( $LatestVersion ) {
- for ( my $Version=1; $Version<=$LatestVersion; $Version++ ) {
- my $vkeys = $result{"$Version:keys:$Prob"};
- my @keys = split(/\:/,$vkeys);
-
- foreach my $Key (@keys) {
- if (($Key=~/\.(\w+)\.solved$/) && ($Key!~/^\d+\:/)) {
- my $Part = $1;
- $Tries = $result{"$Version:$Prob:resource.$Part.tries"};
- $TempHash{"$Part.Tries"} = ($Tries) ? $Tries : 0;
- $TotalTries += $Tries;
- my $Val = $result{"$Version:$Prob:resource.$Part.solved"};
- if ( $Val eq 'correct_by_student' )
- { $Wrongs = $Tries - 1; $Code = 'Y'; }
- elsif ( $Val eq 'correct_by_override' )
- { $Wrongs = $Tries - 1; $Code = 'y'; }
- elsif ( $Val eq 'incorrect_attempted' ||
- $Val eq 'incorrect_by_override' )
- { $Wrongs = $Tries; $Code = 'N'; }
- $TempHash{"$Part.Code"} = $Code;
- $TempHash{"$Part.Wrongs"} = $Wrongs;
- }
- }
- }
- for ( my $n = 0; $n < $PartNo; $n++ ) {
- my $part = $TempHash{$n};
- if ($PtrTry ne '') {$PtrTry .= ',';}
- $PtrTry .= "$TempHash{$part.'.Tries'}";
- $PtrCod .= "$TempHash{$part.'.Code'}";
- }
+#######################################################
+#######################################################
+
+=pod
+
+=item &PrepareClasslist()
+
+Build up the classlist information. The classlist information is kept in
+the following package variables:
+
+=over
+
+=item @FullClasslist
+
+=item @Students
+
+=item @Sections
+
+=item @Groups
+
+=item %StudentData
+
+=item @SelectedStudentData
+
+=item $curr_student
+
+=item $prev_student
+
+=item $next_student
+
+=back
+
+$curr_student, $prev_student, and $next_student may not be defined, depending
+upon the calling context.
+
+=cut
+
+#######################################################
+#######################################################
+sub PrepareClasslist {
+ 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($cdom,
+ $cnum);
+ my @selected_sections = &get_selected_sections();
+ my @selected_groups = &get_selected_groups();
+ #
+ # Deal with instructors with restricted section access
+ if ($env{'request.course.sec'} !~ /^\s*$/) {
+ @selected_sections = ($env{'request.course.sec'});
+ }
+ #
+ # Set up %StudentData
+ @StudentDataOrder = qw/fullname username domain id section status groups comments/;
+ foreach my $field (@StudentDataOrder) {
+ $StudentData{$field}->{'title'} = &mt($field);
+ $StudentData{$field}->{'base_width'} = length(&mt($field));
+ $StudentData{$field}->{'width'} =
+ $StudentData{$field}->{'base_width'};
+ }
+ #
+ # get the status requested
+ $enrollment_status = 'Active';
+ $enrollment_status = $env{'form.Status'} if (exists($env{'form.Status'}));
+ #
+ # Get groupmembership
+ my (%curr_groups,$classgroups,$studentgroups);
+ my $numgroups = &Apache::loncommon::coursegroups(\%curr_groups,$cdom,$cnum);
+ if ($numgroups) {
+ ($classgroups,$studentgroups) =
+ &Apache::loncoursedata::get_group_memberships($classlist,
+ $cdom,$cnum);
+ }
+ my $now = time;
+
+ # Process the classlist
+ while (my ($student,$student_data) = each (%$classlist)) {
+ my $studenthash = ();
+ for (my $i=0; $i< scalar(@$field_names);$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;
+ }
+ }
+ my @studentsgroups = &Apache::loncoursedata::get_students_groups
+ ($student,$enrollment_status,
+ $classgroups);
+ if (@studentsgroups) {
+ $studenthash->{'groups'} = join(', ',@studentsgroups);
+ $studenthash->{'groupref'} = \@studentsgroups;
+ } else {
+ $studenthash->{'groups'} = 'none';
+ $studenthash->{'groupref'} = [];
+ }
+ push (@FullClasslist,$studenthash);
+ #
+ # Build up a list of sections
+ my $section = $studenthash->{'section'};
+ if (! defined($section) || $section =~/^\s*$/ || $section == -1) {
+ $studenthash->{'section'} = 'none';
+ $section = $studenthash->{'section'};
+ }
+ $Sections{$section}++;
+ #
+ # Only put in the list those students we are interested in
+ foreach my $sect (@selected_sections) {
+ if ( (($sect eq 'all') ||
+ ($section eq $sect)) &&
+ (($studenthash->{'status'} eq $enrollment_status) ||
+ ($enrollment_status eq 'Any'))
+ ){
+ my $groupcheck = 0;
+ if (grep/^all$/,@selected_groups) {
+ push (@Students,$studenthash);
+ last;
+ } elsif (grep/^none$/,@selected_groups) {
+ if ($studenthash->{'groups'} eq 'none') {
+ push (@Students,$studenthash);
+ last;
+ }
+ } else {
+ foreach my $group (@selected_groups) {
+ if (grep/^$group$/,@studentsgroups) {
+ push (@Students,$studenthash);
+ $groupcheck = 1;
+ last;
+ }
+ }
+ if ($groupcheck) {
+ last;
+ }
+ }
}
- else {
- for(my $n=0; $n<$PartNo; $n++) {
- if ($PtrTry ne '') {$PtrTry .= ',';}
- $PtrTry .= "0";
- $PtrCod .= "-";
- }
- }
}
}
- $Str .= "\n".'
';
- $r->print($Str);
- $r->rflush();
+ #
+ # Put the consolidated section data in the right place
+ 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 groups
+ @Groups = sort {$a cmp $b} keys(%{$studentgroups});
+ unshift(@Groups,'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 { lc($a->{$sortby}) cmp lc($b->{$sortby}) ||
+ lc($a->{'fullname'}) cmp lc($b->{'fullname'}) ||
+ lc($a->{'username'}) cmp lc($b->{'username'}) } @Students;
+ @Students = @TmpStudents;
+ #
+ # Now deal with that current student thing....
+ $curr_student = undef;
+ if (exists($env{'form.SelectedStudent'})) {
+ my ($current_uname,$current_dom) =
+ split(':',$env{'form.SelectedStudent'});
+ my $i;
+ for ($i = 0; $i<=$#Students; $i++) {
+ next if (($Students[$i]->{'username'} ne $current_uname) ||
+ ($Students[$i]->{'domain'} ne $current_dom));
+ $curr_student = $Students[$i];
+ last; # If we get here, we have our student.
+ }
+ 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 (exists($env{'form.StudentData'})) {
+ @SelectedStudentData =
+ &Apache::loncommon::get_env_multiple('form.StudentData');
+ } else {
+ @SelectedStudentData = ('username');
+ }
+ foreach (@SelectedStudentData) {
+ if ($_ eq 'all') {
+ @SelectedStudentData = ('all');
+ last;
+ }
+ }
+ #
+ return;
}
+#######################################################
+#######################################################
-# ------------------------------------------- Prepare Statistics Table
-sub PreStatTable {
- my $CacheDB = "/home/httpd/perl/tmp/$ENV{'user.name'}".
- "_$ENV{'user.domain'}_$cid\_statistics.db";
- my $GraphDB = "/home/httpd/perl/tmp/$ENV{'user.name'}".
- "_$ENV{'user.domain'}_$cid\_graph.db";
- my $CachDisFac = "/home/httpd/perl/tmp/$ENV{'user.name'}".
- "_$ENV{'user.domain'}_$cid\_DiscFactor.db";
- $r->print(' ');
-
- my $Ptr = '';
-
- $Ptr .= ' Sorting Type: '."\n".
- ' '."\n";
- $Ptr .= ' ';
- $Ptr .= ''."\n";
- $Ptr .= ' ';
- $Ptr .= ''."\n";
-
- $Ptr .= '
'.
- ' #Stdnts: Total Number of Students opened the problem. '.
- ' Tries : Total Number of Tries for solving the problem. '.
- ' Mod : Maximunm Number of Tries for solving the problem. '.
- ' Mean : Average Number of the tries. [ Tries / #Stdns ] '.
- ' #YES : Number of students solved the problem correctly. '.
- ' #yes : Number of students solved the problem by override. '.
- ' %Wrng : Percentage of students tried to solve the problem but'.
- ' still incorrect. [ 100*((#Stdnts-(#YES+#yes))/#Stdnts) ] '.
- ' S.D. : Standard Deviation of the tries.'.
- '[ sqrt(sum((Xi - Mean)^2)) / (#Stdnts-1)'.
- ' where Xi is every student\'s tries ] '.
- ' Skew. : Skewness of the students tries.'.
- ' [ (sqrt( sum((Xi - Mean)^3) / #Stdnts)) / (S.D.^3) ] '.
-# ' DoDiff : Degree of Difficulty of the problem. [ Tries/(#YES+#yes+0.1) ] '.
- ' DoDiff : Degree of Difficulty of the problem. [ 1 - ((#YES+#yes) / Tries) ] '.
-# ' Dis.F. : Discrimination Factor. [ Sum of Partial Credits Awarded / Total Number of Tries in %27 upper and lower students]'.
- '