--- loncom/interface/lonstatistics.pm 2002/05/16 01:27:05 1.19
+++ loncom/interface/lonstatistics.pm 2010/12/05 17:27:30 1.148.2.7
@@ -1,7 +1,6 @@
# The LearningOnline Network with CAPA
-# (Publication Handler
#
-# $Id: lonstatistics.pm,v 1.19 2002/05/16 01:27:05 minaeibi Exp $
+# $Id: lonstatistics.pm,v 1.148.2.7 2010/12/05 17:27:30 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -26,1669 +25,1205 @@
# 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 Behrouz Minaei
#
###
-package Apache::lonstatistics;
-use strict;
+
+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 Apache::loncommon;
-use HTML::TokeParser;
-use GDBM_File;
+use Apache::loncoursedata;
+use Apache::lonhtmlcommon;
+use Apache::lonmysql;
+use Apache::lonlocal;
+use Apache::longroup;
+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();
+use LONCAPA;
-# -------------------------------------------------------------- Module Globals
-my %hash;
-my %CachData;
-my %GraphDat;
-my %OpResp;
-my %maps;
-my %mapsort;
-my %section;
-my %StuBox;
-my %DiscFac;
-my %DisUp;
-my %DisLow;
-my $UpCnt;
-my $CurMap;
-my $CurSec;
-my $CurStu;
-my @cols;
-my @list;
-my @students;
-my $p_count;
-my $Pos;
-my $r;
-my $OpSel1;
-my $OpSel2;
-my $OpSel3;
-my $OpSel4;
-my $GData;
-my $cid;
-my $firstres;
-my $lastres;
-my $DiscFlag;
-my $HWN;
-my $P_Order;
-my %foil_to_concept;
-my @Concepts;
-my %ConceptData;
-my %Header = (0,"Homework Sets Order",1,"#Stdnts",2,"Tries",3,"Mod",
- 4,"Mean",5,"#YES",6,"#yes",7,"%Wrng",8,"DoDiff",
- 9,"S.D.",10,"Skew.",11,"D.F.1st",12,"D.F.2nd");
-my %Answer = ();
-
-sub InitAnalysis {
- my ($rid, $student)=@_;
- my ($uname,$udom)=split(/\:/,$student);
- $rid=~/(\d+)\.(\d+)/;
- my $symb=&Apache::lonnet::declutter($hash{'map_id_'.$1}).'___'.$2.'___'.
- &Apache::lonnet::declutter($hash{'src_'.$rid});
- my $URI = $hash{'src_'.$rid};
- my $Answ=&Apache::lonnet::ssi($URI,('grade_target' => 'analyze',
- 'grade_username' => $uname,
- 'grade_domain' => $udom,
- 'grade_courseid' => $cid,
- 'grade_symb' => $symb));
-# my $Answ=&Apache::lonnet::ssi($URI,('grade_target' => 'analyze'));
-
- (my $garbage,$Answ)=split(/_HASH_REF__/,$Answ,2);
- %Answer=();
- %Answer=&Apache::lonnet::str2hash($Answ);
-
- my $parts='';
- foreach my $elm (@{$Answer{"parts"}}) {
- $parts.="$elm,";
- }
- chop($parts);
- my $conc='';
- foreach my $elm (@{$Answer{"$parts.concepts"}}) {
- $conc.="$elm@";
- }
- chop($conc);
-
- @Concepts=split(/\@/,$conc);
- foreach my $concept (@{$Answer{"$parts.concepts"}}) {
- foreach my $foil (@{$Answer{"$parts.concept.$concept"}}) {
- $foil_to_concept{$foil} = $concept;
- #$ConceptData{$foil} = $Answer{"$parts.foil.value.$foil"};
- }
+#
+# Classlist variables
+#
+my $curr_student;
+my $prev_student;
+my $next_student;
+
+
+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);
+}
+
+
+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 ($classgroups,$studentgroups);
+ my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
+ if (%curr_groups) {
+ ($classgroups,$studentgroups) =
+ &Apache::loncoursedata::get_group_memberships($classlist,
+ $field_names,
+ $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;
+ }
+ }
+ }
+ }
+ }
+ #
+ # Put the consolidated section data in the right place
+ if ($env{'request.course.sec'} !~ /^\s*$/) {
+ @Sections = ($env{'request.course.sec'});
+ } else {
+ @Sections = sort {
+ if ($a == $a && $b == $b ) { return $a <=> $b; }
+ return $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];
+ }
+ }
}
- return $symb;
+ #
+ 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;
}
-sub Interval {
- my ($rid,$part,$symb)=@_;
- my $Int=$ConceptData{"Interval"};
- my $due = &Apache::lonnet::EXT('resource.$part.duedate',$symb)+1;
- my $opn = &Apache::lonnet::EXT('resource.$part.opendate',$symb);
- my $add=int(($due-$opn)/$Int);
- $ConceptData{"Int.0"}=$opn;
- for (my $i=1;$i<$Int;$i++) {
- $ConceptData{"Int.$i"}=$opn+$i*$add;
- }
- $ConceptData{"Int.$Int"}=$due;
- for (my $i=0;$i<$Int;$i++) {
- for (my $n=0; $n<=$#Concepts; $n++ ) {
- my $tmp=$Concepts[$n];
- $ConceptData{"$tmp.$i.true"}=0;
- $ConceptData{"$tmp.$i.false"}=0;
- }
+sub get_selected_sections {
+ my @selected_sections =
+ &Apache::loncommon::get_env_multiple('form.Section');
+ @selected_sections = ('all') if (! @selected_sections);
+ foreach (@selected_sections) {
+ if ($_ eq 'all') {
+ @selected_sections = ('all');
+ }
}
+ #
+ # Deal with instructors with restricted section access
+ if ($env{'request.course.sec'} !~ /^\s*$/) {
+ @selected_sections = ($env{'request.course.sec'});
+ }
+ return @selected_sections;
}
-sub ShowOpGraph {
- my ($InpStr, $Int_No)=@_;
- my ($rid,$part)=split(/\:/,substr($InpStr,8));
- $ConceptData{"Interval"}=$Int_No;
- #Initialize the option response true answers
- my $symb=&InitAnalysis($rid,$students[0]);
- #compute the intervals
- &Interval($rid,$part,$symb);
- my $URI = $hash{'src_'.$rid};
- my $Src = $hash{'title_'.$rid};
- $Src =~ s/\ /"_"/eg;
- $r->print('
'.$URI.'');
- $r->rflush();
-
- #Java script Progress window
- &Create_PrgWin();
- &Update_PrgWin("Starting to analyze problem");
- for (my $index=0;$index<=$#students;$index++) {
- &Update_PrgWin($index);
- &OpStatus($rid,$students[$index]);
- }
- &Close_PrgWin();
-
- $r->print('
');
- for (my $k=0; $k<$Int_No; $k++ ) {
- &DrawGraph($k,$Src);
- }
-#$Apache::lonxml::debug=1;
-#&Apache::lonhomework::showhash(%ConceptData);
-#$Apache::lonxml::debug=0;
- my $Answ=&Apache::lonnet::ssi($URI);
- $r->print("
Here you can see the Problem:
$Answ");
+sub get_selected_groups {
+ my @selected_groups =
+ &Apache::loncommon::get_env_multiple('form.Group');
+ @selected_groups = ('all') if (! @selected_groups);
+ foreach my $grp (@selected_groups) {
+ if ($grp eq 'all') {
+ @selected_groups = ('all');
+ last;
+ }
+ }
+ return @selected_groups;
}
+
-sub DrawGraph {
- my ($k,$Src)=@_;
- my $Max=0;
- my @data1;
- my @data2;
- my $Correct=0;
- my $Wrong=0;
- # Adjust Data and find the Max
- for (my $n=0; $n<=$#Concepts; $n++ ) {
- my $tmp=$Concepts[$n];
- $data1[$n]=$ConceptData{"$tmp.$k.true"};
- $Correct+=$data1[$n];
- $data2[$n]=$ConceptData{"$tmp.$k.false"};
- $Wrong+=$data2[$n];
- my $Sum=$data1[$n]+$data2[$n];
- if ( $Max<$Sum ) {$Max=$Sum;}
- }
- for (my $n=0; $n<=$#Concepts; $n++ ) {
- if ($data1[$n]+$data2[$n]<$Max) {
- $data2[$n]+=$Max-($data1[$n]+$data2[$n]);
- }
+sub section_and_enrollment_description {
+ my ($mode) = @_;
+ if (! defined($mode)) { $mode = 'localized'; }
+ my @sections = &Apache::lonstatistics::get_selected_sections();
+ my @groups = &Apache::lonstatistics::get_selected_groups();
+ my $description;
+ if ($mode eq 'localized') {
+ $description = &mt('Unable to determine section, groups and access status');
+ } elsif ($mode eq 'plaintext') {
+ $description = 'Unable to determine section, groups and access status';
+ } else {
+ $description = 'Bad parameter passed to lonstatistics::section_and_enrollment_description';
+ &Apache::lonnet::logthis($description);
+ }
+ $description = §ion_or_group_text($mode,'section',@sections).
+ ' '.§ion_or_group_text($mode,'group',@groups);
+ if ($mode eq 'localized') {
+ $description .= ' '.&mt($env{'form.Status'}.' access status.');
+ } elsif ($mode eq 'plaintext') {
+ $description .= ' '.$env{'form.Status'}.' access status.';
+ }
+ return $description;
+}
+
+
+
+
+sub section_or_group_text {
+ my ($mode,$type,@items) = @_;
+ my $text;
+ my %phrases = ();
+ %{$phrases{'section'}} = (
+ single => 'Section',
+ all => 'All sections',
+ plural => 'Sections',
+ );
+ %{$phrases{'group'}} = (
+ single => 'Group',
+ all => 'All groups',
+ plural => 'Groups',
+ );
+ if (scalar(@items) == 1 && $items[0] ne 'all') {
+ if ($mode eq 'localized') {
+ $text = &mt($phrases{$type}{single}.' [_1].',$items[0]);
+ } elsif ($mode eq 'plaintext') {
+ $text = $phrases{$type}{single}.' '.$items[0].'.';
+
+ }
+ } elsif (scalar(@items) && $items[0] eq 'all') {
+ if ($mode eq 'localized') {
+ $text = &mt($phrases{$type}{all}.'.');
+ } elsif ($mode eq 'plaintext') {
+ $text = $phrases{$type}{all}.'.';
+ }
+ } elsif (scalar(@items)) {
+ my $lastitem = pop(@items);
+ if ($mode eq 'localized') {
+ $text = &mt($phrases{$type}{plural}.' [_1] and [_2].',
+ join(', ',@items),$lastitem);
+ } elsif ($mode eq 'plaintext') {
+ $text = $phrases{$type}{plural}.' '.join(', ',@items).' and '.
+ $lastitem.'.';
+ }
}
- my $P_No = $#data1+1;
-# $r->print('
From: ['.localtime($ConceptData{'Int.'.($k-1)}).
-# '] To: ['.localtime($ConceptData{"Int.$k"}).']');
- my $Str = "\n".'
# | '. - "\n".'Concept | '. - "\n".'Correct | '. - "\n".'Wrong | '. - "\n".'
---|---|---|---|
".($n+1)." | ". - "\n"."".$Concepts[$n]." | ". - "\n"."".$data1[$n]." | ". - "\n"."".$data2[$n]." | ". - "\n"."From:['.localtime($ConceptData{'Int.$k'}). -# '] To: ['.localtime($ConceptData{'Int.'.($k+1)}). -# "] | $Correct | $Wrong | ";# - - $Str .= "\n".'
# | '. - "\n".'Problem Title | '. - "\n".'Resouse | '. - "\n".'Address | '. - "\n".'
---|---|---|---|
$P_No | ". - "\n"."".$Temp." | ". - "\n"."".$hash{'src_'.$rid}." | ". - "\n"."".''.' | '. - "\n"."
# | '. - "\n".'Set Title | '. - "\n".'Results | '. - "\n".'Tries | '. - "\n".'
---|---|---|---|
$SetNo | ". - "\n"."$Set | ". - "\n"."$PtrCod | ". - "\n"."$PtrTry | ". - "\n"."
P# | '."\n"; - for ( my $nIdx=0; $nIdx < $ColNo; $nIdx++ ) { - $Result .= ''.''.' | '."\n"; + # + my @sequences_to_show; + foreach my $sequence (@sequences_with_assessments) { + if ($mode eq 'all') { + push (@sequences_to_show,$sequence); + } elsif ($mode eq 'selected') { + foreach my $map_symb (&get_selected_maps('Maps')) { + if ($sequence->symb eq $map_symb || $map_symb eq 'all'){ + push (@sequences_to_show,$sequence); + last; # Only put it in once + } + } + } + } - $Result .= "\n".'
---|
'. - ' #Stdnts: Total Number of Students opened the problem.'; - $r->print($Ptr); - - $r->print('Output CSV format: print(' checked'); } - $r->print('>'); - - $r->rflush(); - - if ((-e "$CacheDB")&&($ENV{'form.sort'} ne 'Recalculate Statistics')) { - if (tie(%CachData,'GDBM_File',"$CacheDB",&GDBM_READER,0640)) { - tie(%GraphDat,'GDBM_File',$GraphDB,&GDBM_WRCREAT,0640); - &Cache_Statistics(); - } - else { - $r->print("Unable to tie hash to db file"); - } - } - else { - if (tie(%CachData,'GDBM_File',$CacheDB,&GDBM_WRCREAT,0640)) { - tie(%GraphDat,'GDBM_File',$GraphDB,&GDBM_WRCREAT,0640); - foreach (keys %DiscFac) {delete $CachData{$_};} - foreach (keys %CachData) {delete $CachData{$_};} - $DiscFlag=0; - &Build_Statistics(); - } - else { - $r->print("Unable to tie hash to db file"); +sub SectionSelect { + my ($elementname,$status,$numvisible)=@_; + if ($numvisible < 1) { + return; + } + # + # Make sure we have the data we need to continue + if (! @Sections) { + &PrepareClasslist() + } + # + # Build the form element + my $Str = "\n"; + $Str .= '\n"; + return $Str; } -# ------------------------------------- Find the section of student in a course - -sub usection { - my ($udom,$unam,$courseid,$ActiveFlag)=@_; - $courseid=~s/\_/\//g; - $courseid=~s/^(\w)/\/$1/; - foreach (split(/\&/,&Apache::lonnet::reply('dump:'. - $udom.':'.$unam.':roles', - &Apache::lonnet::homeserver($unam,$udom)))){ - my ($key,$value)=split(/\=/,$_); - $key=&Apache::lonnet::unescape($key); - if ($key=~/^$courseid(?:\/)*(\w+)*\_st$/) { - my $section=$1; - if ($key eq $courseid.'_st') { $section=''; } - my ($dummy,$end,$start)=split(/\_/,&Apache::lonnet::unescape($value)); - if ( $ActiveFlag ne 'Any' ) { - my $now=time; - my $notactive=0; - if ($start) { - if ($now<$start) { $notactive=1; } - } - if ($end) { - if ($now>$end) { $notactive=1; } - } - if ((($ActiveFlag eq 'Expired') && $notactive == 1) || - (($ActiveFlag eq 'Active') && $notactive == 0 ) ) { - return $section; - } - else { return '-1'; } - } - return $section; +sub GroupSelect { + my ($elementname,$status,$numvisible)=@_; + if ($numvisible < 1) { + return; + } + # + # Make sure we have the data we need to continue + if (! @Groups) { + &PrepareClasslist(); + } + # + # Build the form element + my $Str = "\n"; + $Str .= '\n"; } -# ------ Dump the Student's DB file and handling the data for statistics table -sub ExtractStudentData { - my $student=shift; - my ($sname,$sdom) = split( /\:/, $student ); - my %result = &Apache::lonnet::dump($cid,$sdom,$sname); - my $ResId; - my $PrOrd; - my $Dis = ''; - my $Code; - my $Tries; - my $ParCr; - my $TotalTries = 0; - my $TotalOpend = 0; - my $ProbSolved = 0; - my $ProbTot = 0; - my $TimeTot = 0; - my $TotParCr = 0; - my $Wrongs; - my %TempHash; - my $Version; - my $LatestVersion; - my $SecLimit; - my $MapLimit; - my ($temp)=keys(%result); - unless ($temp=~/^error\:/) { - foreach my $CurCol(@cols) { - ($PrOrd,$ResId)=split(/\:/,$CurCol); - if ( !$CurCol ) { next; } - $ResId=~/(\d+)\.(\d+)/; - my $MapId=$1; - my $PrbId=$2; - my $MapOrg = $hash{'map_id_'.$MapId}; - my $Map = &Apache::lonnet::declutter($MapOrg); - if ( $CurMap ne 'All Maps' ) { - my ( $ResMap, $NameMap ) = split(/\=/,$CurMap); - if ( $Map ne $ResMap ) { next; } - } - my $meta=$hash{'src_'.$ResId}; - my $PartNo = 0; - $Dis .= ':'; - undef %TempHash; - - foreach (split(/\,/,&Apache::lonnet::metadata($meta,'keys'))) { - if ($_=~/^stores\_(\w+)\_tries$/) { - my $Part=&Apache::lonnet::metadata($meta,$_.'.part'); - if ( $TempHash{"$Part"} eq '' ) { - $TempHash{"$Part"} = $Part; - $TempHash{$PartNo}=$Part; - $TempHash{"$Part.Code"} = 'U'; - $TempHash{"$Part.PrOrd"} = $PrOrd+$PartNo; - $PartNo++; - } - #my $Part=&Apache::lonnet::metadata($meta,$_.'.part'); - } + + +sub DisplayClasslist { + my ($r)=@_; + &Apache::lonhtmlcommon::add_breadcrumb + ({text=>'Select One Student'}); + # + # Output some of the standard interface components + my $Str; + $Str .= &Apache::lonhtmlcommon::breadcrumbs('Select One Student'); + $Str .= '
'. - ' Tries : Total Number of Tries for solving the problem.
'. - ' Max : Maximunm Number of Tries for solving the problem.
'. - ' Avg. : Average Number of the tries. [ Tries / #Stdnts ]
'. - ' #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) ]
'. -# ' DoDiff : Degree of Difficulty of the problem. [ Tries/(#YES+#yes+0.1) ]
'. Kashy formula - ' DoDiff : Degree of Difficulty of the problem. [ 1 - ((#YES+#yes) / Tries) ]
'. #Gerd formula - ' S.D. : Standard Deviation of the tries.'. - '[ sqrt(sum((Xi - Avg.)^2)) / (#Stdnts-1)'. - ' where Xi denotes every student\'s tries ]
'. - ' Skew. : Skewness of the students tries.'. - ' [ (sqrt( sum((Xi - Avg.)^3) / #Stdnts)) / (S.D.^3) ]
'. - ' Dis.F. : Discrimination Factor: A Standard for '. - 'evaluating the problem according to a Criterion
'. - ' [Applied Criterion in %27 Upper Students - '. - 'Applied the same Criterion in %27 Lower Students]
'. - ' 1st Criterion for Sorting the Students: '. - 'Sum of Partial Credit Awarded / Total Number of Tries
'. - ' 2nd Criterion for Sorting the Students: '. - 'Total number of Correct Answers / Total Number of Tries'. - '
'.&mt('Sections').' | '; + $Str .= ''.&mt('Groups').' | '; + $Str .= ''.&mt('Access Status').' | '; + $Str .= '
---|---|---|
'. + &Apache::lonstatistics::SectionSelect('Section','multiple',5). + ' | '; + $Str .= ''. + &Apache::lonstatistics::GroupSelect('Group','multiple',5). + ' | '; + $Str .= ''. + &Apache::lonhtmlcommon::StatusOptions(undef,undef,5). + ' | '; + + $Str .= '
'. + &mt('There are no currently enrolled students in the course.'). + '
'; + } elsif (lc($env{'form.Status'}) eq 'expired') { + $Str .= ''. + &mt('There are no previously enrolled students in the course.'). + '
'; + } elsif (lc($env{'form.Status'}) eq 'future') { + $Str .= ''. + &mt('There are no students with future access in the course.'). + '
'; + } else { # 'any' and any others + $Str .= ''. + &mt('There are no students in the course.'). + '
'; } - &Apache::lonnet::declutter( $hash{'src_'.$ResId} ); - my $URI = $hash{'src_'.$ResId}; - my $Prob = $Map.'___'.$PrbId.'___'. - &Apache::lonnet::declutter($URI); - $Code='U'; - $Tries = 0; - $ParCr = 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"}; - $ParCr = $result{"$Version:$Prob:resource.$Part.awarded"}; - my $Time = $result{"$Version:$Prob:timestamp"}; - $TempHash{"$Part.Time"} = ($Time) ? $Time : 0; - $TempHash{"$Part.Tries"} = ($Tries) ? $Tries : 0; - $TempHash{"$Part.ParCr"} = ($ParCr) ? $ParCr : 0; - $TotalTries += $TempHash{"$Part.Tries"}; - $TotParCr += $TempHash{"$Part.ParCr"}; - my $Val = $result{"$Version:$Prob:resource.$Part.solved"}; - if ( $Val eq 'correct_by_student' ) - { $Wrongs = $Tries - 1; $Code = 'C'; } - elsif ( $Val eq 'correct_by_override' ) - { $Wrongs = $Tries - 1; $Code = 'O'; } - elsif ( $Val eq 'incorrect_attempted' || - $Val eq 'incorrect_by_override' ) - { $Wrongs = $Tries; $Code = 'I'; } - $TempHash{"$Part.Code"} = $Code; - $TempHash{"$Part.Wrongs"} = $Wrongs; - } - } - } - for ( my $n = 0; $n < $PartNo; $n++ ) { - my $part = $TempHash{$n}; - my $Yes = 0; - if ( $TempHash{$part.'.Code'} eq 'C' || - $TempHash{$part.'.Code'} eq 'O' ) - {$ProbSolved++;$Yes=1;} - - # my $ptr = "$hash{'title_'.$ResId}"; - my $ptr = $TempHash{$part.'.PrOrd'}.'&'.$ResId; - - if ( $PartNo > 1 ) { - $ptr .= "*(part $part)"; - $Dis .= '&'; - } - my $Fac = ($TempHash{"$part.Tries"}) ? - ($TempHash{"$part.ParCr"}/$TempHash{"$part.Tries"}) : 0; - my $DisF; - if ( $Fac > 0 && $Fac < 1 ) { - $DisF = sprintf( "%.4f", $Fac ); - } - else {$DisF = $Fac;} -# $DisF .= '+'.$TempHash{"$part.Time"};33333333 - $TimeTot += $TempHash{"$part.Time"}; - $Dis .= $TempHash{$part.'.PrOrd'}.'='.$DisF.'+'.$Yes; - $ptr .= "&$TempHash{$part.'.Tries'}". - "&$TempHash{$part.'.Wrongs'}". - "&$TempHash{$part.'.Code'}"; - push (@list, $ptr); - $TotalOpend++; - $ProbTot++; - } + } else { + if (lc($env{'form.Status'}) eq 'active') { + $Str .= ''. + &mt('There are no currently enrolled students in the selected sections.'). + '
'; + } elsif (lc($env{'form.Status'}) eq 'expired') { + $Str .= ''. + &mt('There are no previously enrolled students in the selected sections.'). + '
'; + } elsif (lc($env{'form.Status'}) eq 'future') { + $Str .= ''. + &mt('There are no students with future access in the selected sections.'). + '
'; + } else { # 'any' and any others + $Str .= ''. + &mt('There are no students in the selected sections.'). + '
'; } - #else { - #for(my $n=0; $n<$PartNo; $n++) { - # push (@list, "$TempHash{'0'.'.PrOrd'}.':'.$ResId:0:0:U"); - # $ProbTot++; - #} - #} - } - if ( $TotalTries ) { - my $DisFac = ( $TotalTries ) ? ($TotParCr/$TotalTries) : 0; - my $DisFactor = sprintf( "%.4f", $DisFac ); - $DiscFac{$DisFactor}=$Dis; - #my $time; - #if ($ProbSolved){ - #$time = int(($TimeTot/$ProbSolved)-10000000); - #} - #$DiscFac{($DisFactor.':'.$sname.':'.$ProbTot.':'.$TotalOpend.':'. - # $TotalTries.':'.$ProbSolved.':'.$time)}=$Dis; - } + } + $Str.= '' + .'' + .&mt('Return to the chart').'' + .'
'; + $r->print($Str); + $r->rflush(); + return; } - #$r->print($sname.' PrCr= '.$TotParCr.' Slvd= '.$ProbSolved.' Tries='.$TotalTries.''.&mt("Click on a student's name or username to view their chart").'
' + .&Apache::loncommon::start_data_table() + .&Apache::loncommon::start_data_table_header_row(); + foreach my $field (@Fields) { + $Str .= ''.&mt('Aggregate test performance data will be available after the Concept Test end date: [_1].', + &Apache::lonlocal::locallocaltime($duedate)).'
'); + } else { + $r->print(''. + &mt('Aggregate test performance data unavailable without definition of an end date for the Concept Test.').'
'); + } + $r->print(&Apache::loncommon::end_page()); + return OK; + } } -} - -sub Create_PrgWin { -#----------- Create progress - $r->print(<' + .&mt('Please notify the server administrator [_1]', + ,''.$serveradmin.'') + .'
'); + $r->print(''. + &mt('Course Statistics and Charts cannot be '. + 'retrieved until the database is restarted. '. + 'Your data is intact but cannot be displayed '. + 'at this time.').'
'); + $r->print(&Apache::loncommon::end_page()); + return; + } + # + # Clean out the caches + if (exists($env{'form.ClearCache'})) { + &Apache::loncoursedata::delete_caches($env{'requres.course.id'}); + } + # + # Begin form output + $r->print('\n"); + } + $r->print(&Apache::loncommon::end_page()); $r->rflush(); + # + return OK; } +1; -sub Update_PrgWin { -#----------- update progress - my $index = shift; - $r->print(''); - $r->rflush(); -} +__END__ -sub Close_PrgWin { -#--------------------- close Progress Line - $r->print(''); - $r->rflush(); -} +=pod -sub Build_Statistics { - &Create_PrgWin(); -# ---------------------------- Gathering the Data of students' tries - for (my $index=0;$index<=$#students;$index++) { - &Update_PrgWin($index); - &ExtractStudentData($students[$index]); - } - -# -------------------- sorting the Data - $r->print(''); - - @list = sort (@list); - - &Discriminant(); - - $OpSel2=''; - $OpSel1='selected'; - - $p_count = 0; - my $nIdx = 0; - my $dummy; - my $p_val; - my $ResId; - my $NoElements = $#list + 1; -#-------------------------------- loop for data representation - foreach (sort keys %mapsort) { - my ($Hid,$pr)=split(/\:/,$mapsort{$_}); - my @lpr=split(/\&/,$pr); - &CreateTable(1,$Hid); - for (my $i=1; $i<=$#lpr; $i++) { - my %storestats=(); - my ($PrOrd,$Prob,$Tries,$Wrongs,$Code)=split(/\&/,$list[$nIdx]); - my $Temp = $Prob; - my $MxTries = 0; - my $TotalTries = 0; - my $YES = 0; - my $Incorrect = 0; - my $Override = 0; - my $StdNo = 0; - my @StdLst; - while ( $PrOrd == $lpr[$i] ) - { - $nIdx++; - $StdNo++; - $StdLst[ $StdNo ] = $Tries; - $TotalTries += $Tries; - if ( $MxTries < $Tries ) { $MxTries = $Tries; } - if ( $Code eq 'C' ){ $YES++; } - elsif( $Code eq 'I' ) { $Incorrect++; } - elsif( $Code eq 'O' ) { $Override++; } - elsif( $Code eq 'U' ) { $StdNo--; } - ($PrOrd,$Prob,$Tries,$Wrongs,$Code)=split(/\&/,$list[$nIdx]); - } - - $p_count++; - my $Dummy; - ($ResId,$Dummy)=split(/\*/,$Temp); - - $Temp = ''.$hash{'title_'.$ResId}.$Dummy.''; - - my $res = &Apache::lonnet::declutter($hash{'src_'.$ResId}); - my $urlres=$res; - - $ResId=~/(\d+)\.(\d+)/; - my $Map = &Apache::lonnet::declutter( $hash{'map_id_'.$1} ); - $urlres=$Map; - - $res = ''.$res.''; - #$Map = ''.$res.''; +=head1 NAME -#------------------------ Compute the Average of Tries about one problem - my $Average = ($StdNo) ? $TotalTries/$StdNo : 0; +lonstatistics - $storestats{$ENV{'request.course.id'}.'___'.$urlres.'___timestamp'}=time; - $storestats{$ENV{'request.course.id'}.'___'.$urlres.'___stdno'}=$StdNo; - $storestats{$ENV{'request.course.id'}.'___'.$urlres.'___avetries'}=$Average; - -#-------------------------------- Compute percentage of Wrong tries - my $Wrong = ( $StdNo ) ? 100 * ( $Incorrect / $StdNo ) : 0; - -#-------------------------------- Compute Standard Deviation - my $StdDev = 0; - if ( $StdNo > 1 ) { - for ( my $n = 0; $n < $StdNo; $n++ ) { - my $Dif = $StdLst[ $n ]-$Average; - $StdDev += $Dif*$Dif; - } - $StdDev /= ( $StdNo - 1 ); - $StdDev = sqrt( $StdDev ); - } - -#-------------------------------- Compute Degree of Difficulty - my $DoDiff = 0; - if( $TotalTries > 0 ) { - $DoDiff = 1 - ( ( $YES + $Override ) / $TotalTries ); -# $DoDiff = ($TotalTries)/($YES + $Override+ 0.1); - } - - $storestats{$ENV{'request.course.id'}.'___'.$urlres.'___difficulty'}=$DoDiff; +=head1 SYNOPSIS -#-------------------------------- Compute the Skewness - my $Skewness = 0; - my $Sum = 0; - if ( $StdNo > 0 && $StdDev > 0 ) { - for ( my $n = 0; $n < $StdNo; $n++ ) { - my $Dif = $StdLst[ $n ]-$Average; - $Skewness += $Dif*$Dif*$Dif; - } - $Skewness /= $StdNo; - $Skewness /= $StdDev*$StdDev*$StdDev; - } - -#--------------------- Compute the Discrimination Factors - my ($Up1,$Up2)=split(/\:/,$DisUp{$lpr[$i]}); - my ($Lw1,$Lw2)=split(/\:/,$DisLow{$lpr[$i]}); - my $Dis1 = $Up1 - $Lw1; - my $Dis2 = $Up2 - $Lw2; - my $_D1 = sprintf("%.2f", $Dis1); - my $_D2 = sprintf("%.2f", $Dis2); - -#----------------- Some restition in presenting the float numbers - my $Avg = sprintf( "%.2f", $Average ); - my $Wrng = sprintf( "%.1f", $Wrong ); - my $SD = sprintf( "%.1f", $StdDev ); - my $DoD = sprintf( "%.2f", $DoDiff ); - my $Sk = sprintf( "%.1f", $Skewness ); - my $join = $lpr[$i].'&'.$Temp.'&'.$StdNo.'&'. - $TotalTries.'&'.$MxTries.'&'.$Avg.'&'. - $YES.'&'.$Override.'&'.$Wrng.'&'.$DoD.'&'. - $SD.'&'.$Sk.'&'.$_D1.'&'.$_D2.'&'. - $Prob; - $CachData{($p_count-1)}=$join; - - $urlres=~/^(\w+)\/(\w+)/; - if ($StdNo) { - &Apache::lonnet::put('resevaldata',\%storestats,$1,$2); - } -#-------------------------------- Row of statistical table - if ( $DiscFlag == 0 ) { - &TableRow($join,$i,($p_count-1)); - } - } - &CloseTable(); - } - &Close_PrgWin(); -} +Main handler for statistics and chart. -sub Cache_Statistics { - my @list = (); - my $Useful; - my $UnUseful; - my %myHeader = reverse( %Header ); - #&Apache::lonnet::delenv('form_'); - #&Apache::lonnet::delenv('test'); - $Pos = $myHeader{$ENV{'form.sort'}}; - if ($Pos > 0) {$Pos++;} - $p_count = 0; - foreach my $key( keys %CachData) { - my @Temp=split(/\&/,$CachData{$key}); - if ( $Pos == 0 ) { - ($UnUseful,$Useful)=split(/\>/,$Temp[$Pos]); - } - else { - $Useful = $Temp[$Pos]; - } - $list[$p_count]=$Useful.'@'.$CachData{$key}; - $p_count++; - } - - @list = sort MySort (@list); - - my $nIdx=0; - - if ( $Pos == 0 ) { - foreach (sort keys %mapsort) { - my ($Hid,$pr)=split(/\:/,$mapsort{$_}); - &CreateTable(1,$Hid); - my @lpr=split(/\&/,$pr); - for (my $i=1; $i<=$#lpr; $i++) { - my($Pre, $Post) = split(/\@/,$list[$nIdx]); - #$r->print('