Diff for /loncom/interface/lonstatistics.pm between versions 1.29 and 1.129

version 1.29, 2002/07/22 20:35:05 version 1.129, 2006/03/15 19:41:26
Line 1 Line 1
 # The LearningOnline Network with CAPA  # The LearningOnline Network with CAPA
 # (Publication Handler  
 #  #
 # $Id$  # $Id$
 #  #
Line 26 Line 25
 # http://www.lon-capa.org/  # http://www.lon-capa.org/
 #  #
 # (Navigate problems for statistical reports  # (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  Behrouz Minaei  
 #  #
 ###  ###
   
 package Apache::lonstatistics;   =pod
   
 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::lonchart;  
 use HTML::TokeParser;  
 use GDBM_File;  
   
 # -------------------------------------------------------------- Module Globals  
 my %hash;  
 my %CachData;  
 my %GraphDat;  
 my $r;  
 my $GData;  
 my %color;  
 my %foil_to_concept;  
 my @Concepts;  
 my %ConceptData;  
 my %Answer=();  
 my %mapsort;  
   
 my %Activity=();  
 my %Grade=();  
 my %DoDiff=();  
 my %Discuss=();  
 my $TotalDiscuss=0;  
 my $TotalDiscuss_=0;  
   
   
 sub LoadDiscussion {  
 #    my $symb=shift;  
 #    $r->print('<br>$cid ... '.$symb);  
 #    my %contrib=&Apache::lonnet::dump('msu_2964385f9033c63msul1','msu','2964385f9033c63msul1');  
     my $cid=$ENV{'request.course.id'};  
     my %contrib=&Apache::lonnet::dump(  
                 $ENV{'request.course.id'},  
                 $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},  
                 $ENV{'course.'.$ENV{'request.course.id'}.'.num'});  
                               
     foreach my $temp(keys %contrib) {  
  if ($temp=~/^version/) {  
     my $ver=$contrib{$temp};  
     my ($dummy,$prb)=split(':',$temp);  
     for (my $idx=1; $idx<=$ver; $idx++ ) {  
  my $name=$contrib{"$idx:$prb:sendername"};  
  $Discuss{"$name:$prb"}=$idx;  
     }  
  }  
     }         
 #   $r->print('<br>cid='.$cid);  
 #    my %contrib=&Apache::lonnet::restore($symb,$cid,  
 # $ENV{$cid.'.domain'},  
 # $ENV{'course.'.$cid.'.num'});  
   
 #    $Apache::lonxml::debug=1;  
 #    &Apache::lonhomework::showhash(%Discuss);  
 #    $Apache::lonxml::debug=0;  
 }  
   
 sub LoadDoDiffFile {  
     my $file="/home/minaeibi/183d.txt";  
     open(FILEID, "<$file");  
     my $line=<FILEID>;  
     %DoDiff=();  
     my @Act=split('&',$line);  
       
 #    $r->print('<br>'.$#Act);  
     for(my $n=0;$n<=$#Act;$n++){  
        my ($res,$Degree)=split('@',$Act[$n]);  
       $DoDiff{$res}=$Degree;  
     }  
 }  
   
   
 sub LoadClassFile {  =head1 NAME
     my $file="/home/minaeibi/class.txt";  
     open(FILEID, "<$file");  
     my $line;  
     %Grade=();  
     while ($line=<FILEID>) {  
         my ($id,$ex1,$ex2,$ex3,$ex4,$hw,$final,$grade)=split(' ',$line);  
         $Grade{$id}=$grade;  
     }  
 }  
   
   lonstatistics
   
 #------- Classification    =head1 SYNOPSIS
 sub Classify {  
     my ($DiscFac, $students)=@_;  
     &LoadClassFile();  
     my $Count=0;  
     my @List=();  
     my @LS=();  
     my @LF=();  
     my @LM=();  
     my $cf=0;  
     my $cs=0;  
     my $cm=0;  
     foreach (keys(%$DiscFac)){    
  my @l=split(/\:/,$_);  
  if (!($students->{$l[1]})) {next;}  
  my $Grade=$Grade{$students->{$l[1]}};  
  if( $Grade > 3 ) {  
     $cs++;  
     push(@LS,("$l[6],$l[5],$l[4],$l[7],$l[8],$l[9],Successful"));  
  } elsif ( $Grade > 2 ) {  
     $cm++;  
     push(@LM,("$l[6],$l[5],$l[4],$l[7],$l[8],$l[9],Average"));  
  } else {  
     $cf++;  
     push(@LF,("$l[6],$l[5],$l[4],$l[7],$l[8],$l[9],Failed"));  
  }  
     }      
     for(my $n=0;$n<$cs;$n++){$r->print('<br>'.$LS[$n]);}  
     for(my $n=0;$n<$cm;$n++){$r->print('<br>'.$LM[$n]);}    
     for(my $n=0;$n<$cf;$n++){$r->print('<br>'.$LF[$n]);}  
 }   
   
   Main handler for statistics and chart.
   
 sub ProcAct {  =over 4
     # return;  
     my ($Act,$Submit)=@_;  
     my @Act=split(/\@/,$Act);  
     @Act = sort(@Act);  
   
     ##$r->print('<br>'.$#Act);  
     ##for(my $n=0;$n<=$#Act;$n++){  
 ## $r->print('<br>n='.$n.')'.$Act[$n]);  
 ##    }  
   
 #    my $Beg=$Act[0];  =cut
     my $Dif=$Submit-$Act[0];  
     $Dif = ($Dif>0) ? ($Dif/3600) : 0;   
   
 #    $r->print('<br>Access Number = '.$#Act.'<br>Submit Time='.$Submit.'<br>First Access='.$Act[0].'<br>Last Access='.$Act[$#Act].'<br> Submit - First = <b>'.$Dif.'</b>');  package Apache::lonstatistics;
   
   use strict;
   use Apache::Constants qw(:common :http);
   use vars qw(
       @FullClasslist 
       @Students
       @Sections 
       %StudentData
       @StudentDataOrder
       @SelectedStudentData
       $enrollment_status);
   
 #time spent for solving the problem             use Apache::lonnet;
 #    $r->print('<br>Def'.($Act[$#Act-1]-$Act[0]));  use Apache::lonhomework;
   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();
   
     return $Dif;  #######################################################
 }  #######################################################
   
   =pod
   
   =item Package Variables
   
 sub LoadActivityLog {  =item @FullClasslist The full classlist
 #    my $CacheDB = "/home/minaeibi/act183.log.cache";  
     my $CacheDB = "/home/httpd/perl/tmp/act183.log.cache";  
       
     if (-e "$CacheDB") {  
  if (tie(%Activity,'GDBM_File',"$CacheDB",&GDBM_READER,0640)) {  
     return;  
         }  
         else {  
     $r->print("Unable to tie log Cache hash to db file");  
         }  
     }  
     else {  
  if (tie(%Activity,'GDBM_File',$CacheDB,&GDBM_WRCREAT,0640)) {  
     foreach (keys %Activity) {delete $Activity{$_};}  
     &Build_log();   
  }  
         else {  
     $r->print("Unable to tie log Build hash to db file");  
         }  
     }  
 }  
   
 sub Build_log {  =item @Students The students we are concerned with for this invocation
     my $file="/home/minaeibi/act183.log";  
     open(FILEID, "<$file");  
     my $line;  
     my $count=0;  
     while ($line=<FILEID>) {  
  my ($time,$machine,$what)=split(':',$line);  
  $what=&Apache::lonnet::unescape($what);  
  my @accesses=split('&',$what);  
              
  foreach my $access (@accesses) {  
   
     $count++;  
   
     my ($date,$resource,$who,$domain,$post,@posts)=split(':',$access);  
     if (!$resource) { next; }  
     my $res=&Apache::lonnet::unescape($resource);  
     if (($res =~ /\.problem/)) {  
  $Activity{$who.':'.$res}.=$date.'@';  
                 #$r->print('<br>'.$time.':'.$who.'---'.$res);  
  &Update_PrgInit($count);  
   
     }  =item @Sections The sections available in this class
  }  
     }  
   
 # my $c=1;  =item $curr_student The student currently being examined
 # foreach (sort keys %Activity) {  
 #     $r->print('<br>'.$c.')'.$_.' ... '.$Activity{$_});  
 #     $c++;  
 # }  
   
 }  =item $prev_student The student previous in the classlist
   
 sub Activity {  =item $next_student The student next in the classlist
 #    $rid=~/(\d+)\.(\d+)/;  
 #    my $MapId=$1;  
 #    my $PrbId=$2;  
 #    my $MapOrg = $hash{'map_id_'.$MapId};  
 #    my $Map = &Apache::lonnet::declutter($MapOrg);  
 #    my $URI = $hash{'src_'.$rid};  
 #    my $Symb = $Map.'___'.$PrbId.'___'.&Apache::lonnet::declutter($URI);  
     my $file="/home/minaeibi/activity.log";  
     my $userid='adamsde1';  
     $r->print("<br>Using $file");  
     $r->rflush();  
     open(FILEID, "<$file");  
     my $line;  
     my @allaccess;  
     my $Count=0;  
     while ($line=<FILEID>) {  
  my ($time,$machine,$what)=split(':',$line);  
  $what=&Apache::lonnet::unescape($what);  
  my @accesses=split('&',$what);  
  foreach my $access (@accesses) {  
     my ($date,$resource,$who,$domain,$post,@posts)=split(':',$access);  
     #if ($who ne $userid) { next; }  
     if (!$resource) { next; }  
     my $res=&Apache::lonnet::unescape($resource);  
     if (($res =~ /\.(sequence|problem|htm|html|page)/)) {  
      $Count++;  
  $r->print("<br>$Count) ".localtime($date).": $who --> $res");  
 #        if ($post) {   
 #    $Count++;  
 #    $r->print("<br><b>$Count) Sent data ".join(':',  
 #                              &Apache::lonnet::unescape(@posts)).'</b>');  
 # }  
  $r->rflush();  
     }  
     #push (@allaccess,unescape($access));  
     #print $machine;  
  }  
     }  
 #    @allaccess=sort(@allaccess);  
 #    $Count=0;  
 #    foreach my $access (@allaccess) {  
 # my ($date,$resource,$who,$domain,$post,@posts)=split(':',$access);  
 # $Count++;  
 # $r->print("<br>$Count) $date: $who --> $resource");  
 # $r->rflush();  
 # if ($post) {   
 #    $r->print("<br><b>Sent data ".join(':',unescape(@posts)).'</b>');  
 # }  
 #    }  
 }  
   
   =over
   
 sub InitAnalysis {  =cut 
     my ($uri,$part,$problem,$student,$courseID)=@_;  
     my ($uname,$udom)=split(/\:/,$student);  
   
   
     # Render the student's view of the problem.  $Answ is the problem   
     # Stringafied  
     my $Answ=&Apache::lonnet::ssi($uri,('grade_target' => 'analyze',  
                                   'grade_username' => $uname,  
                                   'grade_domain' => $udom,  
                                   'grade_courseid' => $courseID,  
                                   'grade_symb' => $problem));  
 #    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"};  
  }  
     }  
   
     return;  #######################################################
 }  #######################################################
   #
   # Classlist variables
   #
   my $curr_student;
   my $prev_student;
   my $next_student;
   
   #######################################################
   #######################################################
   
 sub Interval {  =pod
     my ($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;  
  }  
     }  
 }  
   
   =item &clear_classlist_variables()
   
 sub ShowOpGraph {  undef the following package variables:
     my ($cache, $students, $courseID)=@_;  
     my $uri      = $cache->{'AnalyzeURI'};  
     my $part     = $cache->{'AnalyzePart'};  
     my $problem  = $cache->{'AnalyzeProblem'};  
     my $title    = $cache->{'AnalyzeTitle'};  
     my $interval = $cache->{'Interval'};  
     $ConceptData{"Interval"} = $interval;  
   
     #Initialize the option response true answers  =over
     &InitAnalysis($uri, $part, $problem, $students->[0],$courseID);  
   
     #compute the intervals  =item @FullClasslist
     &Interval($part,$problem);  
   
     $title =~ s/\ /"_"/eg;  =item @Students
     $r->print('<br><b>'.$uri.'</b>');  
     $r->rflush();  
            
     #Java script Progress window  
     &Create_PrgWin();  
     &Update_PrgWin("Starting-to-analyze-problem");  
     for (my $index=0;$index<(scalar @$students);$index++) {  
  &Update_PrgWin($index);  
  &OpStatus($problem,$students->[$index],$courseID);  
     }  
     &Close_PrgWin();  
   
     $r->print('<br>');  
     for (my $k=0; $k<$interval; $k++ ) {  
  &DrawGraph($k,$title);  
     }  
     for (my $k=0; $k<$interval; $k++ ) {  
  &DrawTable($k);  
     }  
 #$Apache::lonxml::debug=1;  
 #&Apache::lonhomework::showhash(%ConceptData);  
 #$Apache::lonxml::debug=0;  
     my $Answ=&Apache::lonnet::ssi($uri);  
     $r->print("<br><b>Here you can see the Problem:</b><br>$Answ");  
 }  
   
   =item @Sections
   
 sub DrawTable {  =item %StudentData
     my $k=shift;  
     my $Max=0;  
     my @data1;  
     my @data2;  
     my $Correct=0;  
     my $Wrong=0;  
     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]);  
  }  
     }  
     my $P_No = $#data1+1;  
 #    $r->print('<br><b>From: ['.localtime($ConceptData{'Int.'.($k-1)}).  
 #              '] To: ['.localtime($ConceptData{"Int.$k"}).']</b>');   
     my $Str = "\n".'<table border=2>'.  
               "\n".'<tr>'.  
               "\n".'<th> # </th>'.  
       "\n".'<th> Concept </th>'.  
       "\n".'<th> Correct </th>'.  
       "\n".'<th> Wrong </th>'.  
       "\n".'</tr>';  
   
     for (my $n=0; $n<=$#Concepts; $n++ ) {  
  $Str .= "\n"."<tr>".  
         "\n"."<td>".($n+1)."</td>".  
                 "\n".'<td bgcolor='.$color{"yellow"}.'> '.$Concepts[$n]." </td>".  
                 "\n".'<td bgcolor='.$color{"green"}.'> '.$data1[$n]." </td>".  
                 "\n".'<td bgcolor='.$color{"red"}.'> '.$data2[$n]." </td>".  
                 "\n"."</tr>";  
     }  
     $Str.='<td></td><td><b>From:['.localtime($ConceptData{'Int.'.$k}).  
           '] To: ['.localtime($ConceptData{'Int.'.($k+1)}-1).  
           "]</b></td><td>$Correct</td><td>$Wrong</td>";  
   
     $Str .= "\n".'</table>';  =item @StudentDataOrder
   
     $r->print($Str);  =item @SelectedStudentData
 #$Apache::lonxml::debug=1;  
 #&Apache::lonhomework::showhash(%ConceptData);  
 #$Apache::lonxml::debug=0;  
 }  
   
   =item $curr_student
   
 sub DrawGraph {  =item $prev_student
     my ($k,$Src)=@_;  
     my $Max=0;  
     my @data1;  
     my @data2;  
   
     # Adjust Data and find the Max   
     for (my $n=0; $n<=$#Concepts; $n++ ) {  
  my $tmp=$Concepts[$n];  
  $data1[$n]=$ConceptData{"$tmp.$k.true"};  
  $data2[$n]=$ConceptData{"$tmp.$k.false"};  
  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]);  
  }  
     }  
     my $P_No = $#data1+1;  
   
     if ( $Max > 1 ) {   =item $next_student
  $Max += (10 - $Max % 10);  
  $Max = int($Max);  
     } else { $Max = 1; }  
   
     my $Titr=($ConceptData{'Interval'}>1) ? $Src.'_interval_'.($k+1) : $Src;  
 #    $GData=$Titr.'&Concepts'.'&'.'Answers'.'&'.$Max.'&'.$P_No.'&'.$data1.'&'.$data2;  
     $GData="$Titr&Concepts&Answers&$Max&$P_No&".  
            (join(',',@data1)).'&'.(join(',',@data2));  
   
     $r->print('<IMG src="/cgi-bin/graph.gif?'.$GData.'" border=1/>');  =back
 }  
   
   =cut
   
 sub Decide {  #######################################################
     #deciding the true or false answer belongs to each interval  #######################################################
     my ($type,$foil,$time)=@_;   sub clear_classlist_variables {
     my $k=0;      undef(@FullClasslist);
     while ($time>$ConceptData{'Int.'.($k+1)} &&       undef(@Students);
            $k<$ConceptData{'Interval'}) {$k++;}      undef(@Sections);
     $ConceptData{"$foil_to_concept{$foil}.$k.$type"}++;      undef(%StudentData);
       undef(@SelectedStudentData);
       undef($curr_student);
       undef($prev_student);
       undef($next_student);
 }  }
   
 #restore the student submissions and finding the result  #######################################################
 sub OpStatus {  #######################################################
     my ($problem, $student, $courseID)=@_;  
     my ($username,$userdomain)=split(/':'/,$student);  
     my $code='U';  
     my %reshash=&Apache::lonnet::restore($problem, $courseID, $userdomain,   
                                          $username);  
     my @True = ();  
     my @False = ();  
     my $flag=0;  
     if ($reshash{'version'}) {  
         my $tries=0;  
  &Apache::lonhomework::showhash(%Answer);  
  for (my $version=1;$version<=$reshash{'version'};$version++) {  
     my $time=$reshash{"$version:timestamp"};  
      
     foreach my $key (sort(split(/\:/,$reshash{$version.':keys'}))) {  
  if (($key=~/\.(\w+)\.(\w+)\.submission$/)) {  
     my $Id1 = $1; my $Id2 = $2;  
     #check if this is a repeat submission, if so skip it  
              if ($reshash{"$version:resource.$Id1.previous"}) { next; }  
     #if no solved this wasn't a real submission, ignore it  
     if (!defined($reshash{"$version:resource.$Id1.solved"})) {  
  &Apache::lonxml::debug("skipping ");  
  next;  
     }  
     my $Resp = $reshash{"$version:$key"};  
     my %submission=&Apache::lonnet::str2hash($Resp);  
     foreach (keys %submission) {  
  my $Ansr = $Answer{"$Id1.$Id2.foil.value.$_"};  
  if ($submission{$_}) {  
     if ($submission{$_} eq $Ansr) {  
  &Decide("true",$_,$time );  
     }  
     else {&Decide("false",$_,$time );}  
  }  
     }  
         }    
     }  
         }  
     }  
 }  
   
   =pod
   
 #---- END Analyze Web Page ----------------------------------------------  =item &PrepareClasslist()
   
 #---- Problem Statistics Web Page ---------------------------------------  Build up the classlist information.  The classlist information is kept in
   the following package variables:
   
 #------- Processing upperlist and lowerlist according to each problem  =over
 sub ProcessDiscriminant {  
     my ($List) = @_;  
     my @sortedList = sort (@$List);  
     my $Count = scalar @sortedList;  
     my $Problem;  
     my @Dis;  
     my $Slvd=0;  
     my $tmp;  
     my $Sum1=0;  
     my $Sum2=0;  
     my $nIndex=0;  
     my $nStudent=0;  
     my %Proc=undef;  
     while ($nIndex<$Count) {  
  ($Problem,$tmp)=split(/\=/,$sortedList[$nIndex]);  
  @Dis=split(/\+/,$tmp);  
  my $Temp = $Problem;  
  do {  
     $nIndex++;  
     $nStudent++;  
     $Sum1 += $Dis[0];  
     $Sum2 += $Dis[1];  
     ($Problem,$tmp)=split(/\=/,$sortedList[$nIndex]);  
     @Dis=split(/\+/,$tmp);  
  } while ( $Problem eq $Temp && $nIndex < $Count );  
 # $Proc{$Temp}=($Sum1/$nStudent).':'.$nStudent;  
  $Proc{$Temp}=($Sum1/$nStudent).':'.($Sum2/$nStudent);  
 #       $r->print("$nIndex) $Temp --> ($nStudent) $Proc{$Temp} <br>");  
  $Sum1=0;  
  $Sum2=0;  
  $nStudent=0;  
     }  
   
     return %Proc;  =item @FullClasslist
 }  
   
   =item @Students
   
   =item @Sections
   
 #------- Creating Discimination factor     =item %StudentData
 sub Discriminant {  
     my ($discriminantFactor)=@_;  
     my @discriminantKeys=keys(%$discriminantFactor);  
     my $Count = scalar @discriminantKeys;  
   
     my $UpCnt = int(0.27*$Count);  
     my $low=0;  
     my $up=$Count-$UpCnt;  
     my @UpList=();  
     my @LowList=();  
   
     $Count=0;  
     foreach my $key (sort(@discriminantKeys)) {   
  $Count++;      
  if($low < $UpCnt || $Count > $up) {  
             $low++;  
             my $str=$discriminantFactor->{$key};  
             foreach(split(/\:/,$str)){  
                 if($_) {  
                     if($low<$UpCnt) { push(@LowList,$_); }  
                     else            { push(@UpList,$_);  }  
                 }  
             }  
         }  
     }  
     my %DisUp =  &ProcessDiscriminant(\@UpList);  
     my %DisLow = &ProcessDiscriminant(\@LowList);  
   
     return (\%DisUp, \%DisLow);  =item @SelectedStudentData
 }  
   
      =item $curr_student
 sub NumericSort {            
     $a <=> $b;  
 }  
   
   =item $prev_student
   
 sub CreateProblemStatisticsTableHeading {  =item $next_student
     my ($displayFormat,$sequenceSource,$sequenceTitle,$headings)=@_;  
     if($displayFormat eq 'Display CSV Format') {  
         $r->print('<br>"'.$sequenceTitle.'","');  
         $r->print($sequenceSource.'"');  
  return;  
     }  
   
     $r->print('<br><a href="'.$sequenceSource.  
               '" target="_blank">'.$sequenceTitle.'</a>');  
   
     my $Result = "\n".'<table border=2><tr><th>P#</th>'."\n";  
     for(my $nIndex=0; $nIndex < (scalar (keys %$headings)); $nIndex++) {   
  $Result .= '<th>'.'<input type="submit" name="';  
         $Result .= 'ProblemStatisticsHeading" value="';  
         $Result .= $headings->{$nIndex}.'" />'.'</th>'."\n";  
     }  
     $Result .= "\n".'</tr>'."\n";      
     $r->print($Result);  
     $r->rflush();  
 }  
   
 sub CloseTable {  =back
     my ($cache)=@_;  
     if($cache->{'DisplayFormat'} eq 'Display CSV Format') {  
  return;  
     }      
     $r->print("\n".'</table>'."\n");  
     $r->rflush();  
 }  
   
   $curr_student, $prev_student, and $next_student may not be defined, depending
   upon the calling context.
   
    =cut
 # ------ Dump the Student's DB file and handling the data for statistics table   
 sub ExtractStudentData {  
     my ($cache,$name,$list)=@_;  
     my %discriminantFactor;  
   
     my $totalTries = 0;  
     my $totalAwarded = 0;  
     my $tempProblemOrder=0;  
     my $spent=0;  
     my $spent_yes=0;  
     my $TotDiscuss=0;  
     my $TotalOpend = 0;  
     my $ProbSolved = 0;  
     my $ProbTot = 0;  
     my $TotFirst = 0;  
     my $TimeTot = 0;  
     my $Discussed=0;  
   
     foreach my $sequence (split(':', $cache->{'orderedSequences'})) {  
         if($cache->{'ProblemStatisticsMap'} ne 'All Maps'  &&  
            $cache->{'ProblemStatisticsMap'} ne $cache->{$sequence.':title'}) {  
             next;  
         }  
   
         my $Dis = '';  
         foreach my $problemID (split(':', $cache->{$sequence.':problems'})) {  
             my $problem = $cache->{$problemID.':problem'};  
             my $LatestVersion = $cache->{$name.':version:'.$problem};  
   
             # Output dashes for all the parts of this problem if there  
             # is no version information about the current problem.  
             #if(!$LatestVersion) {  
             #    foreach my $part (split(/\:/,$cache->{$sequence.':'.  
             #                                          $problemID.  
             #                                          ':parts'})) {  
             #        $codes    .= "-,";  
             #        $attempts .= "0,";   
             #    }  
             #    next;  
             #}  
   
             my %partData=undef;  
             # Initialize part data, display skips correctly  
             # Skip refers to when a student made no submissions on that  
             # part/problem.  
             foreach my $part (split(/\:/,$cache->{$sequence.':'.  
                                                   $problemID.  
                                                   ':parts'})) {  
                 $partData{$part.':tries'}=0;  
                 $partData{$part.':code'}='-';  
             }  
   
             # Looping through all the versions of each part, starting with the  #######################################################
             # oldest version.  Basically, it gets the most recent   #######################################################
             # set of grade data for each part.  sub PrepareClasslist {
     for(my $Version=1; $Version<=$LatestVersion; $Version++) {      my %Sections;
                 foreach my $part (split(/\:/,$cache->{$sequence.':'.      &clear_classlist_variables();
                                                       $problemID.      #
                                                       ':parts'})) {      # Retrieve the classlist
       my $cid  = $env{'request.course.id'};
                     if(!defined($cache->{$name.":$Version:$problem".      my $cdom = $env{'course.'.$cid.'.domain'};
                                                ":resource.$part.solved"})) {      my $cnum = $env{'course.'.$cid.'.num'};
                         # No grade for this submission, so skip      my ($classlist,$field_names) = &Apache::loncoursedata::get_classlist($cdom,
                         next;   $cnum);
                     }      my @selected_sections = &get_selected_sections();
       #
                     my $tries=0;      # Deal with instructors with restricted section access
                     my $time=0;      if ($env{'request.course.sec'} !~ /^\s*$/) {
                     my $awarded=0;          @selected_sections = ($env{'request.course.sec'});
     $Discussed=0;      }
                     my $code='U';      #
       # Set up %StudentData
                     $awarded = $cache->{$name.      @StudentDataOrder = qw/fullname username domain id section status comments/;
                                         "$Version:$problem:resource.".      foreach my $field (@StudentDataOrder) {
                                         "$part.awarded"};          $StudentData{$field}->{'title'} = &mt($field);
                     $partData{$part.':awarded'} = ($awarded) ? $awarded : 0;          $StudentData{$field}->{'base_width'} = length(&mt($field));
                     $totalAwarded += $awarded;          $StudentData{$field}->{'width'} = 
                                  $StudentData{$field}->{'base_width'};
                     $tries = $cache->{$name.":$Version:$problem".      }
                                       ":resource.$part.tries"};      #
                     $partData{$part.':tries'} = ($tries) ? $tries : 0;      # get the status requested
                     $partData{$part.':wrong'} = $partData{$part.':tries'};      $enrollment_status = 'Active';
                     $totalTries += $tries;      $enrollment_status = $env{'form.Status'} if (exists($env{'form.Status'}));
       #
                     my $val = $cache->{$name.":$Version:$problem".      # Process the classlist
                                        ":resource.$part.solved"};      while (my ($student,$student_data) = each (%$classlist)) {
                     if    ($val eq 'correct_by_student')   {$code = 'C';}           my $studenthash = ();
                     elsif ($val eq 'correct_by_override')  {$code = 'O';}          for (my $i=0; $i< scalar(@$field_names);$i++) {
                     elsif ($val eq 'incorrect_attempted')  {$code = 'I';}               my $field = $field_names->[$i];
                     elsif ($val eq 'incorrect_by_override'){$code = 'I';}              # Store the data
                     elsif ($val eq 'excused')              {$code = 'x';}              $studenthash->{$field}=$student_data->[$i];
                     $partData{$part.':code'}=$code;              # Keep track of the width of the fields
               next if (! exists($StudentData{$field}));
                     if($partData{$part.':wrong'} ne 0 &&               my $length = length($student_data->[$i]);
                        ($code eq 'C' || $code eq 'O')) {              if ($StudentData{$field}->{'width'} < $length) {
                         $partData{$part.':wrong'}--;                  $StudentData{$field}->{'width'} = $length; 
                     }  
                 }  
             }              }
           }
             # Loop through all the parts for the current problem in the           push (@FullClasslist,$studenthash);
             # correct order and prepare the output          #
             foreach (split(/\:/,$cache->{$sequence.':'.$problemID.          # Build up a list of sections
                                          ':parts'})) {          my $section = $studenthash->{'section'};
                 my $Yes = 0;          if (! defined($section) || $section =~/^\s*$/ || $section == -1) {
                 if($partData{$_.':code'} eq 'C' ||               $studenthash->{'section'} = 'none';
                    $partData{$_.':code'} eq 'O') {              $section = $studenthash->{'section'};
                     $Yes=1;          }
                 }          $Sections{$section}++;
                 #my $ptr = "$hash{'title_'.$ResId}";          #
                 my $ptr = $tempProblemOrder.'&'.$problemID;          # Only put in the list those students we are interested in
           foreach my $sect (@selected_sections) {
                 if($_ > 1) {              if ( (($sect eq 'all') || 
                     $ptr .= "*(part $_)";                    ($section eq $sect)) &&
                     $Dis .= '&';                   (($studenthash->{'status'} eq $enrollment_status) || 
                 }                    ($enrollment_status eq 'Any')) 
                    ){
  my ($pr_no,$dod)=split('&',$ptr);                  push (@Students,$studenthash);
  my $DoDiff=$DoDiff{$dod};                  last;
 #               $r->print('<br>'.$name.'---'.$ptr.'==='.$DoDiff);  
   
                 my $Fac = ($partData{$_.':Tries'}) ?   
                     ($partData{$_.':awarded'}/$partData{$_.':tries'}) : 0;  
                 my $DisF;  
                 if($Fac > 0 &&  $Fac < 1) {   
                     $DisF = sprintf( "%.4f", $Fac );  
                 } else {  
                     $DisF = $Fac;  
                 }  
   
                 if ($Discuss{"$name:$problem"}) {  
     $TotDiscuss++;  
                     $Discussed=1;  
                 }  
                 my $time = $cache->{"$name:$LatestVersion:$problem:timestamp"};  
                 $Dis .= $tempProblemOrder.'='.$DisF.'+'.$Yes;  
                 $ptr .= '&'.$partData{$_.'.Tries'}.  
                         '&'.$partData{$_.'.Wrongs'}.  
                         '&'.$partData{$_.'.Code'};  
                 push (@$list, $ptr."&$Discussed");  
   
 #### if ($DoDiff>0.85) {  
   
                 $TimeTot += $time;  
   
                 if ($Yes==1 && $partData{$_.'.Tries'}==1) {  
     $TotFirst++;  
                 }  
  my $Acts= $Activity{$name.':'.$problem};  
  if ($Acts) {  
     my $Pt=&ProcAct( $Acts, $time );  
     #my ($spe,$beg) = split(/\+/,$Pt);  
                     my $spe= $Pt;  
     if ($Yes==1) {$spent_yes += $spe;}  
     $spent += $spe;  
     #$Beg += $beg;  
 #                   $r->print('<br>'.$name.'---'.$problem.'---'.$spe);  
  }  
  $TotalOpend++;  
  $ProbTot++;  
   
                 $tempProblemOrder++;  
             }              }
         }          }
  my $pstr;  
         if($totalTries) {  
     my $DisFac = ($totalAwarded/$totalTries);  
     my $DisFactor = sprintf( "%.4f", $DisFac );  
             my $TS = sprintf( "%.2f", $spent );  
             my $TS_yes = sprintf( "%.2f", $spent_yes );  
    # $DiscFac{$DisFactor}=$Dis;  
     $pstr=$DisFactor.':'.$name.':'.$ProbTot.':'.$TotalOpend.':'.  
                   $totalTries.':'.$ProbSolved.':'.$TotFirst.':'.  
                   $TS_yes.':'.$TS.':'.$TotDiscuss;  
     $discriminantFactor{$pstr}=$Dis;  
  }  
     }      }
       #
     return (\%discriminantFactor);      # Put the consolidated section data in the right place
 }      if ($env{'request.course.sec'} !~ /^\s*$/) {
           @Sections = ($env{'request.course.sec'});
       } else {
 =pod          @Sections = sort {$a cmp $b} keys(%Sections);
 sub MySort {                    unshift(@Sections,'all'); # Put 'all' at the front of the list
     if ( $Pos > 0 ) {  
  if ($ENV{'form.order'} eq 'Descending') {$b <=> $a;}  
  else { $a <=> $b; }  
     }  
     else {  
  if ($ENV{'form.order'} eq 'Descending') {$b cmp $a;}  
  else { $a cmp $b; }  
     }      }
 }      #
 =cut      # Sort the Students
       my $sortby = 'fullname';
 sub BuildStatisticsTable {      $sortby = $env{'form.sort'} if (exists($env{'form.sort'}));
     my ($cache,$discriminantFactor,$list,$headings,$students)=@_;      my @TmpStudents = sort { lc($a->{$sortby}) cmp lc($b->{$sortby}) ||
                                lc($a->{'fullname'}) cmp lc($b->{'fullname'}) ||
 #6666666       lc($a->{'username'}) cmp lc($b->{'username'}) } @Students;
 #    my $file="/home/httpd/perl/tmp/183d.txt";      @Students = @TmpStudents;
 #    open(OUT, ">$file");      # 
 #6666666      # Now deal with that current student thing....
     &Create_PrgWin();      $curr_student = undef;
 ##777777      if (exists($env{'form.SelectedStudent'})) {
 ##    &LoadActivityLog();          my ($current_uname,$current_dom) = 
 ##    $r->print('<script>popwin.document.popremain.remaining.value="'.              split(':',$env{'form.SelectedStudent'});
 ##              'Loading Discussion...";</script>');          my $i;
 ##    &LoadDoDiffFile();          for ($i = 0; $i<=$#Students; $i++) {
     &LoadDiscussion();              next if (($Students[$i]->{'username'} ne $current_uname) || 
                        ($Students[$i]->{'domain'}   ne $current_dom));
     my $p_count = 0;              $curr_student = $Students[$i];
     my $nIndex = 0;              last; # If we get here, we have our student.
     my $dummy;          }
     my $p_val;          if (defined($curr_student)) {
     my $ResId;              if ($i == 0) {
     my $NoElements = scalar @$list;                  $prev_student = undef;
               } else {
     foreach my $sequence (split(':', $cache->{'orderedSequences'})) {                  $prev_student = $Students[$i-1];
         if($cache->{'ProblemStatisticsMap'} ne 'All Maps'  &&              }
            $cache->{'ProblemStatisticsMap'} ne $cache->{$sequence.':title'}) {              if ($i == $#Students) {
             next;                  $next_student = undef;
         }              } else {
                   $next_student = $Students[$i+1];
  &CreateProblemStatisticsTableHeading($cache->{'DisplayFormat'},              }
                                              $cache->{$sequence.':source'},           }
                                              $cache->{$sequence.':title'},  
                                              $headings);  
   
 ##777777  
 ##    &Classify($discriminantFactor, $students);  
   
   
  my ($Hid,$pr)=split(/\:/,$mapsort{$_});  
  my @lpr=split(/\&/,$pr);  
  for (my $i=1; $i<=$#lpr; $i++) {  
     my %storestats=();  
     my ($PrOrd,$Prob,$Tries,$Wrongs,$Code,$Disc)=split(/\&/,$list->[$nIndex]);  
     my $Temp = $Prob;  
     my $MxTries = 0;  
     my $TotalTries = 0;  
     my $YES = 0;  
     my $Incorrect = 0;  
     my $Override = 0;  
     my $StdNo = 0;  
     my $DiscNo=0;  
     my @StdLst;  
     while ( $PrOrd == $lpr[$i] )   
     {  
  $nIndex++;  
  $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,$Disc)=split(/\&/,$list->[$nIndex]);  
     }  
   
     $p_count++;  
     my $Dummy;  
     ($ResId,$Dummy)=split(/\*/,$Temp);  
   
     $Temp = '<a href="'.$hash{'src_'.$ResId}.  
                 '" target="_blank">'.$hash{'title_'.$ResId}.$Dummy.'</a>';  
   
     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 = '<a href="'.$hash{'src_'.$ResId}.'">'.$res.'</a>';  
     #$Map = '<a href="'.$Map.'">'.$res.'</a>';  
   
 #------------------------ Compute the Average of Tries about one problem  
     my $Average = ($StdNo) ? $TotalTries/$StdNo : 0;  
   
     $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;  
   
 #-------------------------------- 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(/\:/,':');#jason$DisUp->{$lpr[$i]});  
     my ($Lw1,$Lw2)=split(/\:/,':');#jason$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.'&'.  
                        $DiscNo.'&'.$Prob;  
     $CachData{($p_count-1)}=$join;  
   
 #6666666  
 #    $r->print('<br>'.$out.'&'.$DoD);  
 #            print (OUT $out.'@'.$DoD.'&');  
 #6666666  
   
     $urlres=~/^(\w+)\/(\w+)/;  
     if ($StdNo) {   
  &Apache::lonnet::put('resevaldata',\%storestats,$1,$2);   
     }  
 #-------------------------------- Row of statistical table  
             &TableRow($cache,$join,$i,($p_count-1));  
  }  
  &CloseTable($cache);  
     }      }
     &Close_PrgWin();      #
 #666666      if (exists($env{'form.StudentData'})) {
 #    close( OUT );   @SelectedStudentData = 
 #666666      &Apache::loncommon::get_env_multiple('form.StudentData');
 }      } else {
           @SelectedStudentData = ('username');
 =pod  
 sub Cache_Statistics {  
     my ($cache)=@_;  
     my @list = ();  
     my $Useful;  
     my $UnUseful;  
 #    $r->print('<input type="hidden" name="show" value="excel" />'."\n");   
     my %myHeader = reverse( %Header );  
     $Pos = $myHeader{$ENV{'form.sort'}};  
     if ($Pos > 0) {$Pos++;}  
     my $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 $nIndex=0;  
   
     if ( $Pos == 0 ) {  
  foreach (sort keys %mapsort) {  
     my ($Hid,$pr)=split(/\:/,$mapsort{$_});  
     &CreateProblemStatisticsTableHeading($cache,1,$Hid);  
     my @lpr=split(/\&/,$pr);  
     for (my $i=1; $i<=$#lpr; $i++) {  
  my($Pre, $Post) = split(/\@/,$list[$nIndex]);   
  #$r->print('<br>'.$Pre.'---'.$Post);  
  &TableRow($cache,$Post,$i,$nIndex);  
  $nIndex++;  
     }  
     &CloseTable($cache);  
  }  
     }      }
     else {      foreach (@SelectedStudentData) {
  &CreateProblemStatisticsTableHeading($cache,0);          if ($_ eq 'all') {
  for ( my $nIndex = 0; $nIndex < $p_count; $nIndex++ ) {              @SelectedStudentData = ('all');
     my($Pre, $Post) = split(/\@/,$list[$nIndex]);               last;
     &TableRow($cache,$Post,$nIndex,$nIndex);          }
  }   
  &CloseTable($cache);  
     }      }
       #
       return;
 }  }
 =cut   
   
   #######################################################
   #######################################################
   
   =pod
   
 sub TableRow {  =item get_selected_sections
     my ($Str,$Idx,$RealIdx)=@_;  
     my($PrOrd,$Temp,$StdNo,$TotalTries,$MxTries,$Avg,$YES,$Override,  
        $Wrng,$DoD,$SD,$Sk,$_D1,$_D2,$DiscNo,$Prob)=split(/\&/,$Str);  
     if ($ENV{'form.showcsv'}) {  
         my ($ResId,$Dummy)=split(/\*/,$Prob);  
         my $Ptr =  "\n".'<br>'.  
                "\n".'"'.($RealIdx+1).'",'.  
                "\n".'"'.$hash{'title_'.$ResId}.$Dummy.'",'.  
                "\n".'"'.$hash{'src_'.$ResId}.'",'.  
                "\n".'"'.$StdNo.'",'.  
                "\n".'"'.$TotalTries.'",'.  
                "\n".'"'.$MxTries.'",'.  
                "\n".'"'.$Avg.'",'.  
                "\n".'"'.$YES.'",'.  
                "\n".'"'.$Override.'",'.  
                "\n".'"'.$Wrng.'",'.  
                "\n".'"'.$DoD.'",'.  
                "\n".'"'.$SD.'",'.  
                "\n".'"'.$Sk.'",'.  
                "\n".'"'.$_D1.'",'.  
        "\n".'"'.$_D2.'"'.  
        "\n".'"'.$DiscNo.'"';  
   
         $r->print("\n".$Ptr);  
     }  
     else{  
         my $Ptr =  "\n".'<tr>'.  
                "\n".'<td>'.($RealIdx+1).'</td>'.  
           #     "\n".'<td>'.$PrOrd.$Temp.'</td>'.  
                "\n".'<td>'.$Temp.'</td>'.  
                "\n".'<td bgcolor='.$color{"yellow"}.'> '.$StdNo.'</td>'.  
                "\n".'<td bgcolor='.$color{"yellow"}.'>'.$TotalTries.'</td>'.  
                "\n".'<td bgcolor='.$color{"yellow"}.'>'.$MxTries.'</td>'.  
                "\n".'<td bgcolor='.$color{"gb"}.'>'.$Avg.'</td>'.  
                "\n".'<td bgcolor='.$color{"gb"}.'> '.$YES.'</td>'.  
                "\n".'<td bgcolor='.$color{"gb"}.'> '.$Override.'</td>'.  
                "\n".'<td bgcolor='.$color{"red"}.'> '.$Wrng.'</td>'.  
                "\n".'<td bgcolor='.$color{"red"}.'> '.$DoD.'</td>'.  
                "\n".'<td bgcolor='.$color{"green"}.'> '.$SD.'</td>'.  
                "\n".'<td bgcolor='.$color{"green"}.'> '.$Sk.'</td>'.  
                "\n".'<td bgcolor='.$color{"purple"}.'> '.$_D1.'</td>'.  
        "\n".'<td bgcolor='.$color{"purple"}.'> '.$_D2.'</td>'.  
                "\n".'<td bgcolor='.$color{"yellow"}.'> '.$DiscNo.'</td>';  
         $r->print("\n".$Ptr.'</tr>' );  
     }  
     $GraphDat{$RealIdx}=$DoD.':'.$Wrng;  
 }  
   
 sub StatusOptions {  Returns an array of the selected sections
     my ($cache)=@_;  
   
     my $Status = $cache->{'Status'};  =cut
     my $OpSel1 = '';  
     my $OpSel2 = '';  
     my $OpSel3 = '';  
   
     if($Status eq 'Any')         { $OpSel3 = ' selected'; }  
     elsif($Status eq 'Expired' ) { $OpSel2 = ' selected'; }  
     else                         { $OpSel1 = ' selected'; }  
   
     my $Ptr = '';  
     $Ptr .= '<tr><td align="right"><b>Student Status:</b></td>'."\n";  
     $Ptr .= '<td align="left"><select name="Status">';  
     $Ptr .= '<option'.$OpSel1.'>Active</option>'."\n";  
     $Ptr .= '<option'.$OpSel2.'>Expired</option>'."\n";  
     $Ptr .= '<option'.$OpSel3.'>Any</option>'."\n";  
     $Ptr .= '</select></td></tr>'."\n";  
   
     return $Ptr;  #######################################################
   #######################################################
   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 AscendOrderOptions {  #######################################################
     my ($cache)=@_;  #######################################################
   
     my $order = $cache->{'Ascend'};  =pod
     my $OpSel1 = '';  
     my $OpSel2 = '';  
   
     if($order eq 'Ascending') {  =item &section_and_enrollment_description
         $OpSel1 = ' selected';  
     } else {  
         $OpSel2 = ' selected';  
     }  
   
     my $Ptr = '';  Returns a string describing the currenly selected section(s) and 
     $Ptr .= '<tr><td align="right"><b>Sorting Type:</b></td>'."\n";  enrollment status.  
     $Ptr .= '<td align="left"><select name="Ascend">'."\n";  
     $Ptr .= '<option'.$OpSel1.'>Ascending</option>'."\n".  
     '<option'.$OpSel2.'>Descending</option>'."\n";  
     $Ptr .= '</select></td></tr>'."\n";  
   
     return $Ptr;  Inputs: mode = 'plaintext' or 'localized'  (defaults to 'localized')
 }      'plaintext' is used for example in Excel spreadsheets.
   Returns: scalar description string.
   
 sub ProblemStatisticsButtons {  =cut
     my ($cache)=@_;  
   
     my $Ptr = '<tr><td></td><td align="left">';  #######################################################
     $Ptr .= '<input type=submit name="ProblemStatisticsRecalculate" ';  #######################################################
     $Ptr .= 'value="Recalculate Statistics"/>'."\n";  sub section_and_enrollment_description {
     $Ptr .= '&nbsp;&nbsp;&nbsp;';      my ($mode) = @_;
     $Ptr .= '<input type="submit" name="DoDiffGraph" ';      if (! defined($mode)) { $mode = 'localized'; }
     $Ptr .= 'value="DoDiff Graph" />'."\n";      my @sections = &Apache::lonstatistics::get_selected_sections();
     $Ptr .= '&nbsp;&nbsp;&nbsp;';      my $description;
     $Ptr .= '<input type="submit" name="PercentWrongGraph" ';      if ($mode eq 'localized') {
     $Ptr .= 'value="%Wrong Graph" />'."\n";          $description = &mt('Unable to determine section and enrollment');
     $Ptr .= '&nbsp;&nbsp;&nbsp;';      } elsif ($mode eq 'plaintext') {
     $Ptr .= '<input type="submit" name="DisplayCSVFormat" ';          $description = 'Unable to determine section and enrollment';
     if($cache->{'DisplayFormat'} eq 'Display CSV Format') {  
         $Ptr .= 'value="Display CSV Format" />'."\n";  
     } else {      } else {
         $Ptr .= 'value="Display Table Format" />'."\n";          $description = 'Bad parameter passed to lonstatistics::section_and_enrollment_description';
           &Apache::lonnet::logthis($description);
     }      }
     $Ptr .= '</td></tr>';      if (scalar(@sections) == 1 && $sections[0] ne 'all') {
           if ($mode eq 'localized') {
     return $Ptr;              $description = &mt('Section [_1]. [_2] enrollment status.',
                                  $sections[0],$env{'form.Status'});
           } elsif ($mode eq 'plaintext') {
               $description = 'Section '.$sections[0].'. '.
                   $env{'form.Status'}.' enrollment status.';
           }
       } elsif (scalar(@sections) && $sections[0] eq 'all') {
           if ($mode eq 'localized') {
               $description = &mt('All sections. [_1] enrollment status.',
                                  $env{'form.Status'});
           } elsif ($mode eq 'plaintext') {
               $description = 'All sections. '.
                   $env{'form.Status'}.' enrollment status.';
           }
       } elsif (scalar(@sections)) {
           my $lastsection = pop(@sections);
           if ($mode eq 'localized') {
               $description = &mt('Sections [_1] and [_2]. [_3] enrollment status.',
                                  join(', ',@sections),$lastsection,
                                  $env{'form.Status'});
           } elsif ($mode eq 'plaintext') {
               $description = 
                   'Sections '.join(', ',@sections).' and '.$lastsection.'. '.
                   $env{'form.Status'}.' enrollment status.';
           }
       }
       return $description;
 }  }
   
 sub ProblemStatisticsLegend {  #######################################################
     my $Ptr = '';  #######################################################
     $Ptr = '<table border="0">';  
     $Ptr .= '<tr><td>';  
     $Ptr .= '<b>#Stdnts</b>:</td>';  
     $Ptr .= '<td>Total Number of Students opened the problem.';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>Tries</b>:</td>';  
     $Ptr .= '<td>Total Number of Tries for solving the problem.';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>Mod</b>:</td>';  
     $Ptr .= '<td>Maximunm Number of Tries for solving the problem.';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>Mean</b>:</td>';  
     $Ptr .= '<td>Average Number of the tries. [ Tries / #Stdnts ]';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>#YES</b>:</td>';  
     $Ptr .= '<td>Number of students solved the problem correctly.';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>#yes</b>:</td>';  
     $Ptr .= '<td>Number of students solved the problem by override.';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>%Wrng</b>:</td>';  
     $Ptr .= '<td>Percentage of students tried to solve the problem ';  
     $Ptr .= 'but still incorrect. [ 100*((#Stdnts-(#YES+#yes))/#Stdnts) ]';  
     $Ptr .= '</td></tr><tr><td>';  
 #    Kashy formula  
 #    '<b>  DoDiff </b>: Degree of Difficulty of the problem.<br>'.  
 #    '[ Tries/(#YES+#yes+0.1) ]<br>'.  
     #Gerd formula  
     $Ptr .= '<b>DoDiff</b>:</td>';  
     $Ptr .= '<td>Degree of Difficulty of the problem.  ';  
     $Ptr .= '[ 1 - ((#YES+#yes) / Tries) ]';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>S.D.</b>:</td>';  
     $Ptr .= '<td>Standard Deviation of the tries.  ';  
     $Ptr .= '[ sqrt(sum((Xi - Mean)^2)) / (#Stdnts-1) ';  
     $Ptr .= 'where Xi denotes every student\'s tries ]';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>Skew.</b>:</td>';  
     $Ptr .= '<td>Skewness of the students tries.';  
     $Ptr .= '[(sqrt( sum((Xi - Mean)^3) / #Stdnts)) / (S.D.^3)]';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>Dis.F.</b>:</td>';  
     $Ptr .= '<td>Discrimination Factor: A Standard for evaluating the ';  
     $Ptr .= 'problem according to a Criterion<br>';  
     $Ptr .= '<b>[Applied Criterion in %27 Upper Students - ';  
     $Ptr .= 'Applied the same Criterion in %27 Lower Students]</b><br>';  
     $Ptr .= '<b>1st Criterion</b> for Sorting the Students: ';  
     $Ptr .= '<b>Sum of Partial Credit Awarded / Total Number of Tries</b><br>';  
     $Ptr .= '<b>2nd Criterion</b> for Sorting the Students: ';  
     $Ptr .= '<b>Total number of Correct Answers / Total Number of Tries</b>';  
     $Ptr .= '</td></tr>';  
     $Ptr .= '<tr><td><b>Disc.</b></td>';  
     $Ptr .= '<td>Number of Students had at least one discussion.';  
     $Ptr .= '</td></tr></table>';  
   
     return $Ptr;  =pod
 }  
   
 #---- END Problem Statistics Web Page ----------------------------------------  =item get_students
   
 #---- Problem Statistics Graph Web Page --------------------------------------  Returns a list of the selected students
   
 # ------------------------------------------- Prepare data for Graphical chart  =cut
   
 sub GetGraphData {  #######################################################
     my $ylab = shift;  #######################################################
     my $Col;  sub get_students {
     my $data='';      if (! @Students) {
     my $count = 0;          &PrepareClasslist()
     my $Max = 0;  
     my $cid=$ENV{'request.course.id'};  
     my $GraphDB = "/home/httpd/perl/tmp/$ENV{'user.name'}".  
                   "_$ENV{'user.domain'}_$cid\_graph.db";  
     foreach (keys %GraphDat) {delete $GraphDat{$_};}  
     if (-e "$GraphDB") {  
  if (tie(%GraphDat,'GDBM_File',"$GraphDB",&GDBM_READER,0640)) {  
     if ( $ylab eq 'DoDiff Graph' ) {  
  $ylab = 'Degree-of-Difficulty';  
  $Col = 0;  
     }  
     else {  
  $ylab = 'Wrong-Percentage';  
  $Col = 1;  
     }  
     foreach (sort NumericSort keys %GraphDat) {   
  my @Temp=split(/\:/,$GraphDat{$_});  
                 my $inf = $Temp[$Col];   
  if ( $Max < $inf ) {$Max = $inf;}  
  $data .= $inf.',';  
  $count++;  
     }  
     if ( $Max > 1 ) {   
  $Max += (10 - $Max % 10);  
  $Max = int($Max);  
     }  
     else { $Max = 1; }  
             untie(%GraphDat);  
     my $Course = $ENV{'course.'.$cid.'.description'};  
     $Course =~ s/\ /"_"/eg;  
     $GData=$Course.'&'.'Problems'.'&'.$ylab.'&'.$Max.'&'.$count.'&'.$data;  
  }  
  else {  
     $r->print("Unable to tie hash to db file");  
  }  
     }      }
       return @Students;
 }  }
 #---- Problem Analysis Web Page ----------------------------------------------  
   
 sub IntervalOptions {  #######################################################
     my ($cache)=@_;  #######################################################
   
     my $interval = 1;  =pod
     for(my $n=1; $n<=7; $n++) {  
         if($cache->{'Interval'} == $n) {  
             $interval = $n;  
         }  
     }  
   
     my $Ptr = '<br><b>Select number of intervals</b>'."\n".  =item &current_student()
              '<select name="Interval">'."\n";  
     for(my $n=1; $n<=7;$ n++) {  
  $Ptr .= '<option';  
         if($interval == $n) {  
             $Ptr .= ' selected';  
         }  
  $Ptr .= '>'.$n."</option>"."\n";  
     }  
     $Ptr .= '</select>'."\n";  
   
     return $Ptr;  Returns a pointer to a hash containing data about the currently
 }  selected student.
   
 sub OptionResponseTable {  =cut
     my ($cache)=@_;  
     my $Str = '';  
     $Str .= '<br><b> Option Response Problems in this course:</b>'."\n";  
     $Str .= '<br><br>'."\n";  
     $Str .= "<table border=2><tr><th> \# </th><th> Problem Title </th>";  
     $Str .= '<th> Resource </th><th> Analysis  </th></tr>'."\n";  
   
     my $number=1;  
     foreach (split(':::',$cache->{'OptionResponses'})) {  
         my ($uri,$title,$part,$problem)=split('::',$_);  
         my $Temp = '<a href="'.$uri.'" target="_blank">'.$title.'</a>';  
         $Str .= '<tr>';  
         $Str .= '<td> '.$number.' </td>';  
         $Str .= '<td bgcolor="'.$color{"green"}.'"> '.$Temp.' </td>';  
         $Str .= '<td bgcolor="'.$color{"yellow"}.'"> '.$uri.' </td>';  
         $Str .= '<td><input type="submit" name="Analyze:::'.$uri.':::';  
         $Str .= $title.':::'.$part.':::'.$problem.'" value="';  
         $Str .= 'Analyze" /></td></tr>'."\n";  
         $number++;  
     }  
     $Str .= '</table>'."\n";  
   
     return $Str;  #######################################################
   #######################################################
   sub current_student { 
       return $curr_student;
 }  }
   
 #---- END Problem Analysis Web Page ------------------------------------------  #######################################################
   #######################################################
   
 #---- Student Assessment Web Page --------------------------------------------  =pod
   
 # ------ Create different Student Report   =item &previous_student()
 sub StudentReport {  
     my ($cache, $name)=@_;  
   
     my $Str = '';  
     if($cache->{$name.':error'} =~ /course/) {  
         my ($username)=split(':',$name);  
         $Str .= '<b><font color="blue">No course data for student </font>';  
         $Str .= '<font color="red">'.$username.'.</font></b><br>';  
         return $Str;  
     }  
   
     $Str .= "<table border=2><tr><th> \# </th><th> Set Title </th>";  
     $Str .= '<th> Results </th><th> Tries </th></tr>'."\n";  
   
     my $codes;  
     my $attempts;  
     foreach my $sequence (split(':', $cache->{'orderedSequences'})) {  
         if($cache->{'StudentAssessmentMap'} ne 'All Maps'  &&  
            $cache->{'StudentAssessmentMap'} ne $cache->{$sequence.':title'}) {  
             next;  
         }  
   
         $Str .= '<tr><td>'.$sequence.'</td>';  
         $Str .= '<td>'.$cache->{$sequence.':title'}.'</td>';  
   
         $codes = '';  
         $attempts = '';  
         foreach my $problemID (split(':', $cache->{$sequence.':problems'})) {  
             my $problem = $cache->{$problemID.':problem'};  
             my $LatestVersion = $cache->{$name.':version:'.$problem};  
   
             # Output dashes for all the parts of this problem if there  
             # is no version information about the current problem.  
             if(!$LatestVersion) {  
                 foreach my $part (split(/\:/,$cache->{$sequence.':'.  
                                                       $problemID.  
                                                       ':parts'})) {  
     $codes    .= "-,";  
                     $attempts .= "0,";   
                 }  
                 next;  
             }  
   
             my %partData=undef;  Returns a pointer to a hash containing data about the student prior
             # Initialize part data, display skips correctly  in the list of students.  Or something.  
             # Skip refers to when a student made no submissions on that  
             # part/problem.  
             foreach my $part (split(/\:/,$cache->{$sequence.':'.  
                                                   $problemID.  
                                                   ':parts'})) {  
                 $partData{$part.':tries'}=0;  
                 $partData{$part.':code'}='-';  
             }  
   
             # Looping through all the versions of each part, starting with the  =cut
             # oldest version.  Basically, it gets the most recent   
             # set of grade data for each part.  
     for(my $Version=1; $Version<=$LatestVersion; $Version++) {  
                 foreach my $part (split(/\:/,$cache->{$sequence.':'.  
                                                       $problemID.  
                                                       ':parts'})) {  
   
                     if(!defined($cache->{$name.":$Version:$problem".  
                                                ":resource.$part.solved"})) {  
                         # No grade for this submission, so skip  
                         next;  
                     }  
   
                     my $tries=0;  
                     my $code='U';  
   
                     $tries = $cache->{$name.":$Version:$problem".  
                                       ":resource.$part.tries"};  
                     $partData{$part.':tries'}=($tries) ? $tries : 0;  
   
                     my $val = $cache->{$name.":$Version:$problem".  
                                        ":resource.$part.solved"};  
                     if    ($val eq 'correct_by_student')   {$code = 'Y';}   
                     elsif ($val eq 'correct_by_override')  {$code = 'y';}  
                     elsif ($val eq 'incorrect_attempted')  {$code = 'N';}   
                     elsif ($val eq 'incorrect_by_override'){$code = 'N';}  
                     elsif ($val eq 'excused')              {$code = 'x';}  
                     $partData{$part.':code'}=$code;  
                 }  
             }  
   
             # Loop through all the parts for the current problem in the   
             # correct order and prepare the output  
             foreach (split(/\:/,$cache->{$sequence.':'.$problemID.  
                                          ':parts'})) {  
                 $codes    .= $partData{$_.':code'}.',';  
                 $attempts .= $partData{$_.':tries'}.',';   
             }  
         }  
         $codes    =~ s/,$//;  
         $attempts =~ s/,$//;  
         $Str .= '<td>'.$codes.'</td>';  
         $Str .= '<td>'.$attempts.'</td>';  
         $Str .= '</tr>'."\n";  
     }  
   
     $Str .= '</table>'."\n";  
   
     return $Str;  #######################################################
   #######################################################
   sub previous_student { 
       return $prev_student;
 }  }
   
 #---- END Student Assessment Web Page ----------------------------------------  #######################################################
   #######################################################
 #---- Menu Web Page ----------------------------------------------------------  
   
 #---- END Menu Web Page ------------------------------------------------------  =pod
   
 #---- HELPER FUNCTIONS -------------------------------------------------------  =item &next_student()
   
 sub CheckFormElement {  Returns a pointer to a hash containing data about the next student
     my ($cache, $ENVName, $cacheName, $default)=@_;  to be viewed.
   
     if(defined($ENV{'form.'.$ENVName})) {  =cut
         $cache->{$cacheName} = $ENV{'form.'.$ENVName};  
     } elsif(!defined($cache->{$cacheName})) {  
         $cache->{$cacheName} = $default;  
     }  
   
     return;  #######################################################
   #######################################################
   sub next_student { 
       return $next_student;
 }  }
   
 sub ProcessFormData{  ##############################################
     my ($cache)=@_;  ##############################################
   
     $cache->{'reportKey'} = 'false';  =pod 
   
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},  =item &StudentDataSelect($elementname,$status,$numvisible,$selected)
                                             ['sort','download']);  
     &CheckFormElement($cache, 'Status', 'Status', 'Active');  
     &CheckFormElement($cache, 'postdata', 'reportSelected', 'Class list');  
     &CheckFormElement($cache, 'reportSelected', 'reportSelected',   
                       'Class list');  
     &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, 'StudentAssessmentMap', 'StudentAssessmentMap',   
                       'All Maps');  
     &CheckFormElement($cache, 'StudentAssessmentStudent',   
                       'StudentAssessmentStudent', 'No Student Selected');  
   
     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   Returns html for a selection box allowing the user to choose one (or more) 
 displayed are determined by their status(active/expired).  The order  of the fields of student data available (fullname, username, id, section, etc)
 is determined by the sort button pressed (default to username).  The  
 type of sorting is username, lastname, or section.  
   
 =over 4  =over 4
   
 Input: $students, $CacheData  =item $elementname The name of the HTML form element
   
 $students: A array pointer to a list of students (username:domain)  
   
 $CacheData: A pointer to the hash tied to the cached data  =item $status 'multiple' or 'single' selection box
   
 Output: \@order  =item $numvisible The number of options to be visible
   
 @order: An ordered list of students (username:domain)  
   
 =back  =back
   
 =cut  =cut
   
 sub SortStudents {  ##############################################
     my ($cache)=@_;  ##############################################
   sub StudentDataSelect {
     my @students = split(':::',$cache->{'NamesOfStudents'});      my ($elementname,$status,$numvisible)=@_;
     my @sorted1Students=();      if ($numvisible < 1) {
     foreach (@students) {          return;
         if($cache->{'Status'} eq 'Any' ||       }
            $cache->{$_.':Status'} eq $cache->{'Status'}) {      #
             push(@sorted1Students, $_);      # Build the form element
       my $Str = "\n";
       $Str .= '<select name="'.$elementname.'" ';
       if ($status ne 'single') {
           $Str .= 'multiple="true" ';
       }
       $Str .= 'size="'.$numvisible.'" >'."\n";
       #
       # Deal with 'all'
       $Str .= '    <option value="all" ';
       foreach (@SelectedStudentData) {
           if ($_ eq 'all') {
               $Str .= 'selected ';
               last;
         }          }
     }      }
       $Str .= ">all</option>\n";
     my $sortBy = '';      #
     if(defined($cache->{'sort'})) {      # Loop through the student data fields
         $sortBy = ':'.$cache->{'sort'};      foreach my $item (@StudentDataOrder) {
           $Str .= '    <option value="'.$item.'" ';
           foreach (@SelectedStudentData) {
               if ($item eq $_ ) {
                   $Str .= 'selected ';
                   last;
               }
           }
           $Str .= '>'.$item."</option>\n";
     }      }
     my @order = sort { $cache->{$a.$sortBy} cmp $cache->{$b.$sortBy} ||      $Str .= "</select>\n";
                        $cache->{$a.':fullname'} cmp $cache->{$b.':fullname'} }       return $Str;
                 @sorted1Students;  
   
     return \@order;  
 }  }
   
 sub PrepareData {  #######################################################
     my ($c, $cacheDB)=@_;  #######################################################
   
     # 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) {  =pod
         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,  =item &get_selected_maps($elementname)
                                                 $cache{'ClasslistTimestamp'},  
                                                 $c);  
     foreach (keys(%$classlist)) {  
         if(/^(con_lost|error|no_such_host)/i) {  
             untie(%cache);  
             return "Error getting student data.";  
         }  
     }  
   
     if($c->aborted()) {  Input: Name of the <select> form element used to specify the maps.
         untie(%cache);  
         return 'aborted';   
     }  
   
     # Active is a temporary solution, remember to change  Returns: Array of symbs of selected maps or the description 'all'.
     Apache::loncoursedata::ProcessClasslist(\%cache,$classlist,$courseID,$c);     If form.$elementname does not exist, 'all' is returned.
     if($c->aborted()) {  
         untie(%cache);  
         return 'aborted';   
     }  
   
     &ProcessFormData(\%cache);  =cut
     my $students = &SortStudents(\%cache);  
   
     if($cache{'download'} ne 'false') {  #######################################################
         my $who = $cache{'download'};  #######################################################
         my $courseData =   sub get_selected_maps {
             &Apache::loncoursedata::DownloadStudentCourseInformation(      my ($elementname) = @_;
                                              $who, $courseID,       my @selected_maps = 
                                              $cache{$who.':lastDownloadTime'});   &Apache::loncommon::get_env_multiple('form.'.$elementname);
         &Apache::loncoursedata::ProcessStudentData(\%cache, $courseData, $who);      @selected_maps = ('all') if (! @selected_maps);
         $cache{'download'} = 'false';      foreach my $map (@selected_maps) {
     } elsif($cache{'DownloadAll'} ne 'false') {          if ($map eq 'all') {
         my @allStudents;              @selected_maps = ('all');
         if($cache{'DownloadAll'} eq 'sorted') {              last;
             @allStudents = @$students;  
         } else {  
             @allStudents = split(':::', $cache{'NamesOfStudents'});  
         }  
         foreach (@allStudents) {  
             my $courseData =   
                 &Apache::loncoursedata::DownloadStudentCourseInformation(  
                                              $_, $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';   
     }      }
       return @selected_maps;
     if($c->aborted()) {  
         untie(%cache);  
         return 'aborted';   
     }  
   
     untie(%cache);  
   
     return ('OK', $students);  
 }  }
   
 # Create progress  
 sub Create_PrgWin {  
     $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->print('<script>popwin.document.popremain.remaining.value="'.  
               'Computing '.$index.'/'.$totalStudents.': '.  
               $name.'";</script>');  
     $r->rflush();  
 }  
   
 # close Progress Line  =pod
 sub Close_PrgWin {  
     $r->print('<script>popwin.close()</script>');  
     $r->rflush();   
 }  
   
 # For loading the colored table for display or un-colored for print  =item &selected_sequences_with_assessments
 sub setbgcolor {  
     my $PrintTable=shift;  
     undef %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;  Retrieve the sequences which were selected by the user to show.  
 }  
   
 sub initial {  Input: $mode: scalar.  Either 'selected' or 'all'.  If not specified,
     undef %hash;      'selected' is used.
     undef %CachData;  
     undef %GraphDat;  
     undef %ConceptData;  
     undef $GData;  
 }  
   
 #---- END HELPER FUNCTIONS ---------------------------------------------------  Returns: an array containing a navmap object and navmap resources, 
       or an array containing a scalar with an error message.
   
 sub BuildProblemStatisticsPage {  =cut
     my ($cacheDB, $students, $courseID, $c)=@_;  
   
     my %cache;  
     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {  
         $r->print('<html><body>Unable to tie database.</body></html>');  
         return;  
     }  
   
     my $Ptr = '';  #######################################################
     $Ptr .= '<table border="0"><tbody>';  #######################################################
     $Ptr .= '<tr><td align="right"><b>Select Map</b></td>'."\n";  sub selected_sequences_with_assessments {
     $Ptr .= '<td align="left">';      my ($mode) = @_;
     $Ptr .= &Apache::lonhtmlcommon::MapOptions(\%cache, 'ProblemStatistics');      $mode = 'selected' if (! defined($mode));
     $Ptr .= '</td></tr>'."\n";      my $navmap = Apache::lonnavmaps::navmap->new();
     $r->print($Ptr);      if (!defined($navmap)) {
     $r->print(&AscendOrderOptions());          return ('Can not open Coursemap');
     $r->print(&ProblemStatisticsButtons(\%cache));      }
     $r->print('</table>');      #
       my @sequences = $navmap->retrieveResources(undef,
     $r->print(&ProblemStatisticsLegend());                                                 sub { shift->is_map(); },1,0,1);
       my @sequences_with_assessments;
     untie(%cache);      for my $sequence ($navmap->getById('0.0'), @sequences) {
     foreach (@$students) {   if ($navmap->hasResource($sequence,sub { shift->is_problem(); },0,1)){
         my $courseData =               push(@sequences_with_assessments,$sequence);
             &Apache::loncoursedata::DownloadStudentCourseInformation($_,           }
                                                                     $courseID);      }
         last if ($c->aborted());      #
         if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT,0640)) {      my @sequences_to_show;
             &Apache::loncoursedata::ProcessStudentData(\%cache,       foreach my $sequence (@sequences_with_assessments) {
                                                        $courseData, $_);          if ($mode eq 'all') {
             untie(%cache);              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
                   }
               }
         }          }
     }  
     if($c->aborted()) { return; }  
   
     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {  
         $r->print('<html><body>Unable to tie database.</body></html>');  
         return;  
     }  
     my $discriminantFactor;  
     my @list=();  
     foreach (@$students) {  
         $discriminantFactor = &ExtractStudentData(\%cache, $_, \@list);  
     }      }
       return $navmap,@sequences_to_show;
   }
   
     my ($upper, $lower) = &Discriminant($discriminantFactor);  ##############################################
     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", 13, "Disc.");  
     &BuildStatisticsTable(\%cache, $discriminantFactor, \@list, \%Header,   
                            $students);  
   
     untie(%cache);  =pod 
   
     return;  =item &map_select($elementname,$status,$numvisible,$restriction) 
 }  
   
 sub BuildDiffGraph {  Returns html for a selection box allowing the user to choose one (or more) 
     my ($courseID)=@_;  of the sequences in the course.  The values of the sequences are the symbs.
   If the top sequence is selected, the value 'top' will result.
   
     my $graphData = &GetGraphData('DiffGraph', $courseID);  =over 4
     $r->print('<IMG src="/cgi-bin/graph.gif?'.$graphData.'" />');  
   
     return;  =item $elementname The name of the HTML form element
 }  
   
 sub BuildWrongGraph {  =item $status 'multiple' or 'single' selection box
     my ($courseID)=@_;  
   
     my $graphData = &GetGraphData('WrongGraph', $courseID);  =item $numvisible The number of options to be visible
     $r->print('<IMG src="/cgi-bin/graph.gif?'.$graphData.'" />');  
   
     return;  =back
 }  
   
 sub BuildAnalyzePage {  =cut
     my ($cacheDB, $students, $courseID)=@_;  
   
     my %cache;  ##############################################
     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {  ##############################################
         $r->print('<html><body>Unable to tie database.</body></html>');  sub map_select {
       my ($elementname,$status,$numvisible)=@_;
       if ($numvisible < 1) {
         return;          return;
     }      }
       #
       # Set up array of selected items
       my @selected_maps = &get_selected_maps($elementname);
       #
       # Build the form element
       my $form = "\n";
       $form .= '<select name="'.$elementname.'" ';
       if ($status ne 'single') {
           $form .= 'multiple="true" ';
       }
       $form .= 'size="'.$numvisible.'" >'."\n";
       #
       # Put in option for 'all'
       $form .= '    <option value="all" ';
       if ($selected_maps[0] eq 'all') {
           $form .= 'selected ';
       }
       $form .= ">all</option>\n";
       #
       # Loop through the sequences
       my @sequences = &selected_sequences_with_assessments('all');
       my $navmap;
       if (!ref($sequences[0])) {
           return $sequences[0];
       } else {
           $navmap = shift(@sequences);
       }
       foreach my $seq (@sequences){
           $form .= '    <option value="'.$seq->symb.'" ';
           foreach (@selected_maps) {
               if ($seq->symb eq $_) {
                   $form .= 'selected ';
                   last;
               }
           }
           $form .= '>'.$seq->compTitle."</option>\n";
       }
       $form .= "</select>\n";
       return $form;
   }
   
     &ShowOpGraph(\%cache, $students, $courseID);  ##############################################
   ##############################################
     untie(%cache);  
   
     return;  =pod 
 }  
   
 sub BuildProblemAnalysisPage {  =item &SectionSelect($elementname,$status,$numvisible) 
     my ($cacheDB)=@_;  
   
     my %cache;  Returns html for a selection box allowing the user to choose one (or more) 
     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {  of the sections in the course.  
         $r->print('<html><body>Unable to tie database.</body></html>');  
         return;  
     }  
   
     $r->print(&IntervalOptions());  Uses the package variables @Sections
     $r->print(&OptionResponseTable(\%cache));  =over 4
   
     untie(%cache);  =item $elementname The name of the HTML form element
   
     return;  =item $status 'multiple' or 'single' selection box
 }  
   
 sub BuildStudentAssessmentPage {  =item $numvisible The number of options to be visible
     my ($cacheDB, $students, $courseID, $c)=@_;  
   
     my %cache;  =back
   
     my $Ptr = '';  =cut
     $Ptr .= '<table border="0"><tbody>';  
     $r->print($Ptr);  
   
     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {  ##############################################
         $r->print('<html><body>Unable to tie database.</body></html>');  ##############################################
   sub SectionSelect {
       my ($elementname,$status,$numvisible)=@_;
       if ($numvisible < 1) {
         return;          return;
     }      }
       #
     my $selectedName = $cache{'StudentAssessmentStudent'};      # Make sure we have the data we need to continue
     for(my $index=0;       if (! @Sections) {
         ($selectedName ne 'All Students') && ($index<(scalar @$students));           &PrepareClasslist()
         $index++) {      }
         my $fullname = $cache{$students->[$index].':fullname'};      #
         if($fullname eq $selectedName) {      # Build the form element
             if($cache{'StudentAssessmentMove'} eq 'next') {      my $Str = "\n";
                 if($index == ((scalar @$students) - 1)) {      $Str .= '<select name="'.$elementname.'" ';
                     $selectedName = $students->[0];      if ($status ne 'single') {
                 } else {          $Str .= 'multiple="true" ';
                     $selectedName = $students->[$index+1];      }
                 }      $Str .= 'size="'.$numvisible.'" >'."\n";
             } elsif($cache{'StudentAssessmentMove'} eq 'previous') {      #
                 if($index == 0) {      # Loop through the sequences
                     $selectedName = $students->[-1];      foreach my $s (@Sections) {
                 } else {          $Str .= '    <option value="'.$s.'" ';
                     $selectedName = $students->[$index-1];          foreach (&get_selected_sections()) {
                 }              if ($s eq $_) {
             } else {                  $Str .= 'selected ';
                 $selectedName = $students->[$index];                  last;
             }              }
             last;  
         }          }
           $Str .= '>'.$s."</option>\n";
     }      }
       $Str .= "</select>\n";
     $Ptr .= '<tr><td align="right"><b>Select Map</b></td>'."\n";      return $Str;
     $Ptr .= '<td align="left">';  
     $Ptr .= &Apache::lonhtmlcommon::MapOptions(\%cache, 'StudentAssessment');  
     $Ptr .= '</td></tr>'."\n";  
     $Ptr .= '<tr><td align="right"><b>Select Student</b></td>'."\n";  
     $Ptr .= '<td align="left">'."\n";  
     $Ptr .= &Apache::lonhtmlcommon::StudentOptions(\%cache, $students,   
                                                    $selectedName,   
                                                    'StudentAssessment');  
     $Ptr .= '</td></tr>'."\n";  
     untie(%cache);  
   
     $Ptr .= '<tr><td></td><td align="left">';  
     $Ptr .= '<input type="submit" name="CreateStudentAssessment" ';  
     $Ptr .= 'value="Create Student Report" />';  
     $Ptr .= '&nbsp&nbsp&nbsp';  
     $Ptr .= '<input type="submit" name="PreviousStudent" ';  
     $Ptr .= 'value="Previous Student" />';  
     $Ptr .= '&nbsp&nbsp&nbsp';  
     $Ptr .= '<input type="submit" name="NextStudent" ';  
     $Ptr .= 'value="Next Student" />';  
     $Ptr .= '&nbsp&nbsp&nbsp';  
     $Ptr .= '</td></tr></tbody></table>'."\n";  
     $r->print($Ptr);  
   
     if($selectedName eq 'No Student Selected') {  
  $r->print('<h3><font color=blue>WARNING: ');  
         $r->print('Please select a student</font></h3>');  
         return;  
     }  
   
     my $selected=0;  
     foreach (@$students) {  
         next if ($_ ne $selectedName &&   
                  $selectedName ne 'All Students');  
         $selected = 1;  
         my $courseData =   
             &Apache::loncoursedata::DownloadStudentCourseInformation($_,   
                                                                     $courseID);  
         last if ($c->aborted());  
         if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT,0640)) {  
             &Apache::loncoursedata::ProcessStudentData(\%cache,   
                                                        $courseData, $_);  
             if(!$c->aborted()) { $r->print(&StudentReport(\%cache, $_)); }  
             untie(%cache);  
         }  
     }  
     if($selected == 0) {  
  $r->print('<h3><font color=blue>WARNING: ');  
         $r->print('Please select a student</font></h3>');  
         return;  
     }  
   
     return;  
 }  }
   
 sub BuildClasslist {  ##################################################
     my ($cacheDB,$students,$studentInformation,$headings,$spacePadding)=@_;  ##################################################
   sub DisplayClasslist {
     my %cache;      my ($r)=@_;
     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {      &Apache::lonhtmlcommon::add_breadcrumb
         return '<html><body>Unable to tie database.</body></html>';          ({text=>'Select One Student'});
       #
       # Output some of the standard interface components
       my $Str;
       $Str .= &Apache::lonhtmlcommon::breadcrumbs(undef,'Select One Student');
       $Str .= '<p><table cellspacing="5">'."\n";
       $Str .= '<tr>';
       $Str .= '<th align="center"><b>'.&mt('Sections').'</b></th>';
       $Str .= '<th align="center"><b>'.&mt('Enrollment Status').'</b></th>';
       $Str .= '</tr>'.$/;
       $Str .= '<tr>';
       $Str .= '<td>'.
           &Apache::lonstatistics::SectionSelect('Section','multiple',5).
           '</td>';
       $Str .= '<td>'.
           &Apache::lonhtmlcommon::StatusOptions(undef,undef,5).
           '</td>';
       
       $Str .= '</tr>'.$/;
       $Str .= '</table></p>';
       $Str .= '<input type="submit" name="selectstudent" value="'.
           &mt('Update Display').'" />';
       $r->print($Str);
       $r->rflush();
       #
       my @Fields = ('fullname','username','domain','id','section','status');
       #
       $Str = '';
       my @selected_sections = &get_selected_sections();
       if (! @Students) {
           if ($selected_sections[0] eq 'all') { 
               if (lc($env{'form.Status'}) eq 'any') {
                   $Str .= '<h2>'.
                       &mt('There are no students in the course.').
                       '</h2>';
               } elsif (lc($env{'form.Status'}) eq 'active') {
                   $Str .= '<h2>'.
                   &mt('There are no currently enrolled students in the course.').
                       '</h2>';
               } elsif (lc($env{'form.Status'}) eq 'expired') {
                   $Str .= '<h2>'.
                       &mt('There are no previously enrolled students in the course.').
                           '</h2>';
               }
           } else { 
               my $sections;
               if (lc($env{'form.Status'}) eq 'any') {
                   $Str .= '<h2>'.
                       &mt('There are no students in the selected sections.').
                       '</h2>';
               } elsif (lc($env{'form.Status'}) eq 'active') {
                   $Str .= '<h2>'.
                       &mt('There are no currently enrolled students in the selected sections.').
                       '</h2>';
               } elsif (lc($env{'form.Status'}) eq 'expired') {
                   $Str .= '<h2>'.
                       &mt('There are no previously enrolled students in the selected sections.').
                       '</h2>';
               }
           }
           $Str.= '<a href="/adm/statistics?reportSelected=student_assessment">'.
               &mt('Click here to return to the chart').'</a>';
           $r->print($Str);
           $r->rflush();
           return;
     }      }
   
     my $Str='';      # "Click" is asinine but it is probably not my place to change the world.
       $Str .= '<h2>Click on a students name or username to view their chart</h2>';
     $Str .= '<table border="0"><tr><td bgcolor="#777777">'."\n";      $Str .= '<table border="0"><tr><td bgcolor="#777777">'."\n";
     $Str .= '<table border="0" cellpadding="3"><tr bgcolor="#e6ffff">'."\n";      $Str .= '<table border="0" cellpadding="3"><tr bgcolor="#e6ffff">'."\n";
       foreach my $field (@Fields) {
     my $displayString = '<td align="left"><a href="/adm/statistics?';          $Str .= '<th><a href="/adm/statistics?'.
     $displayString .= 'sort=LINKDATA">DISPLAYDATA&nbsp</a></td>'."\n";              'reportSelected=student_assessment&'.
     $Str .= &Apache::lonhtmlcommon::CreateStudentInformationHeadings(\%cache,              'selectstudent=1&'.
                                                            $studentInformation,              'sort='.$field.'">'.&mt($field).
                                                            $headings,              '</a></th>';
                                                            $displayString);      }
     $Str .= '<td align="left">';  
     $Str .= '<a href="/adm/statistics?sort=lastDownloadTime">';  
     $Str .= 'Last Updated&nbsp&nbsp</a></td>'."\n";  
     $Str .= '</tr>'."\n";      $Str .= '</tr>'."\n";
     my $alternate=0;      #
     foreach (@$students) {      my $alternate = 0;
         my ($username, $domain) = split(':', $_);      foreach my $student (@Students) { # @Students is a package variable
           my $sname = $student->{'username'}.':'.$student->{'domain'};
         if($alternate) {          if($alternate) {
             $Str .= '<tr bgcolor="#ffffe6"><td>';              $Str .= '<tr bgcolor="#ffffe6">';
         } else {          } else {
             $Str .= '<tr bgcolor="#ffffc6"><td>';              $Str .= '<tr bgcolor="#ffffc6">';
         }          }
         $alternate = ($alternate + 1) % 2;          $alternate = ($alternate + 1) % 2;
         foreach my $data (@$studentInformation) {          #
             if($data eq 'fullname') {          foreach my $field (@Fields) {
               $Str .= '<td>';
               if ($field eq 'fullname' || $field eq 'username') {
                 $Str .= '<a href="/adm/statistics?reportSelected=';                  $Str .= '<a href="/adm/statistics?reportSelected=';
                 $Str .= &Apache::lonnet::escape('Student Assessment').'">';                  $Str .= &Apache::lonnet::escape('student_assessment');
             }                  $Str .= '&sort='.&Apache::lonnet::escape($env{'form.sort'});
                   $Str .= '&SelectedStudent=';
             $Str .= $cache{$_.':'.$data}.'&nbsp';                  $Str .= &Apache::lonnet::escape($sname).'">';
                   $Str .= $student->{$field}.'&nbsp';
             if($data eq 'fullname') {  
                 $Str .= '</a>';                  $Str .= '</a>';
               } elsif ($field eq 'status') {
                   $Str .= &mt($student->{$field});
               } else {
                   $Str .= $student->{$field};
             }              }
               $Str .= '</td>';
             $Str .= '</td><td>';  
         }  
   
         $Str .= '<a href="/adm/statistics?download='.$_.'">';  
         my $downloadTime = $cache{$_.':lastDownloadTime'};  
         if($downloadTime ne 'Not downloaded') {  
             $downloadTime = localtime($downloadTime);  
         }          }
         $Str .= $downloadTime;          $Str .= "</tr>\n";
   
         $Str .= '&nbsp</a></td></tr>'."\n";  
     }      }
   
     $Str .= '</table></td></tr></table>'."\n";      $Str .= '</table></td></tr></table>'."\n";
       #
       $r->print($Str);
       $r->rflush();
       #
       return;
   }
   
     untie(%cache);  ##############################################
   ##############################################
   sub CreateMainMenu {
       #
       # Define menu data
       my @reports = ({ internal_name => 'problem_statistics',
                        name => &mt('Overall Problem Statistics'),
                        short_description => 
       &mt('Student performance statistics on all problems.'),
                    },
                      { internal_name => 'problem_analysis',
                        name => &mt('Detailed Problem Analysis'),
                        short_description => 
       &mt('Detailed statistics and graphs of student performance on problems.'),
                    },
                      { internal_name => 'submissiontime_analysis',
                        name => &mt('Submission Time Plots'),
                        short_description => 
       &mt('Display and analysis of submission times on assessments.'),
                    },
                      { internal_name => 'student_submission_reports',
                        name => &mt('Student Submission Reports'),
                        short_description => 
       &mt('Prepare reports of student submissions.'),
                    },
                      { internal_name => 'survey_reports',
                        name => &mt('Survey Reports'),
                        short_description => 
       &mt('Prepare reports on survey results.'),
                    },
                      { internal_name => 'correct_problems_plot',
                        name => &mt('Correct Problems Plot'),
                        short_description => 
       &mt('Display a histogram of student performance in the course.'),
                    },
   #                   { internal_name => 'grading_analysis',
   #                     name => &mt('Detailed Grading Analysis'),
   #                     short_description => 
   #    &mt('Display statistics about who graded who.'),
   #                 },
   #                   { internal_name => 'student_assessment',
   #                     name => &mt('Problem Status Chart'),
   #                     short_description => 
   #    &mt('Brief view of each students performance in course.'),
   #                 },
                      # 'percentage'  => 'Correct-problems Plot',
                      # 'activitylog' => 'Activity Log',
                      );
       #
       # Create the menu
       my $Str;
       $Str .= '<h2>'.&mt('Please select a report to generate').'</h2>';
       foreach my $reportdata (@reports) {
           $Str .='    <h3><a href="/adm/statistics?reportSelected='.
               $reportdata->{'internal_name'}.'" >'.
               $reportdata->{'name'}."</a></h3>\n";
           $Str .= '    '.('&nbsp;'x8).$reportdata->{'short_description'}.
               "\n";
       }
       $Str .="</dl>\n";
       #
     return $Str;      return $Str;
 }  }
   
 sub BuildStatistics {  ##############################################
     my ($r)=@_;  ##############################################
   sub handler {
     my $c = $r->connection;      my $r=shift;
     my @studentInformation=('fullname','section','id','domain','username');      my $c = $r->connection();
     my @headings=('Full Name', 'Section', 'PID', 'Domain', 'User Name');      #
     my $spacePadding = '   ';      # Check for overloading
     my %reports = ('classlist'          => 'Class list',      my $loaderror=&Apache::lonnet::overloaderror($r);
                    'problem_statistics' => 'Problem Statistics',      if ($loaderror) { return $loaderror; }
                    'student_assessment' => 'Student Assessment',      $loaderror=
                    'reportSelected'     => 'Class list');         &Apache::lonnet::overloaderror($r,
            $env{'course.'.$env{'request.course.id'}.'.home'});
     my %cache;      if ($loaderror) { return $loaderror; }
     my $courseID=$ENV{'request.course.id'};      #
     my $cacheDB = "/home/httpd/perl/tmp/$ENV{'user.name'}".      # Check for access
                   "_$ENV{'user.domain'}_$courseID\_statistics.db";      if (! &Apache::lonnet::allowed('vgr',$env{'request.course.id'})) {
           $env{'user.error.msg'}=
     &setbgcolor(0);              $r->uri.":vgr:0:0:Cannot view grades for complete course";
     my ($returnValue, $students) = &PrepareData($c, $cacheDB);          if (! &Apache::lonnet::allowed('vgr',
     if($returnValue ne 'OK') {                        $env{'request.course.id'}.'/'.$env{'request.course.sec'})) {
         $r->print('<html><body>'.$returnValue."\n".'</body></html>');              $env{'user.error.msg'}=
         return OK;                  $r->uri.":vgr:0:0:Cannot view grades with given role";
     }              return HTTP_NOT_ACCEPTABLE;
           }
     my $GoToPage;      }
     if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {      #
         $GoToPage = $cache{'reportSelected'};      # Send the header
         $reports{'reportSelected'} = $cache{'reportSelected'};      &Apache::loncommon::no_cache($r);
 #        if(defined($cache{'reportKey'}) && $cache{'reportKey'} ne 'false') {      &Apache::loncommon::content_type($r,'text/html');
 #            $reports{$cache{'reportKey'}} = $cache{'reportSelected'};      $r->send_http_header;
 #        }      if ($r->header_only) { return OK; }
       #
         if(defined($cache{'OptionResponses'})) {      # Extract form elements from query string
             $reports{'problem_analysis'} = 'Problem Analysis';      &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
                                               ['sort','reportSelected',
                                                'SelectedStudent']);
       #
       # Give the LON-CAPA page header
       my $style = <<ENDSTYLE;
   <style type="text/css">
       ul.sub_studentans { list-style-type: none }
       ul.sub_correctans { list-style-type: none }
       tr.even           { background-color: \#CCCCCC }
       td.essay          { border: 1px solid gray; }
   </style>
   ENDSTYLE
         
       $r->print(&Apache::loncommon::start_page('Course Statistics and Charts',
        $style));
       $r->rflush();
       # 
       # Either print out a menu for them or send them to a report
       &Apache::lonhtmlcommon::clear_breadcrumbs();
       &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/statistics',
                                               title=>'Statistics',
                                               text =>'Statistics',
                                               faq=>139,
                                               bug=>'Statistics and Charts'});
       if (! exists($env{'form.reportSelected'}) || 
           $env{'form.reportSelected'} eq '') {
           $r->print(&Apache::lonhtmlcommon::breadcrumbs
                     (undef,&mt('Statistics Main Page')).
                     &CreateMainMenu());
       } else {
       #
           if (! &Apache::lonmysql::verify_sql_connection()) {
               my $serveradmin = $r->dir_config('lonAdmEMail');
               $r->print('<h2><font color="Red">'.
                         &mt('Unable to connect to database!').
                         '</font></h2>');
               $r->print('<p>'.
                         &mt('Please notify the server administrator ').
                         '<b>'.$serveradmin.'</b></p>');
               $r->print('<p>'.
                         &mt('Course Statistics and Charts cannot be '.
                             'retrieved until the database is restarted.  '.
                             'Your data is intact but cannot be displayed '.
                             'at this time.').'</p>');
               $r->print(&Apache::loncommon::end_page());
               return;
           }
           #
           # Clean out the caches
           if (exists($env{'form.ClearCache'})) {
               &Apache::loncoursedata::delete_caches($env{'requres.course.id'});
         }          }
           #
         $r->print(&Apache::lonhtmlcommon::Title('LON-CAPA Statistics'));          # Begin form output
         $r->print('<form name="Statistics" ');          $r->print('<form name="Statistics" ');
         $r->print('method="post" action="/adm/statistics">');          $r->print('method="post" action="/adm/statistics">');
         $r->print(&Apache::lonhtmlcommon::CreateStatisticsMainMenu(          $r->rflush();
                                                              $cache{'Status'},           #
                                                              \%reports));          my $GoToPage = $env{'form.reportSelected'};
         untie(%cache);          #
     } else {          $r->print('<input type="hidden" name="reportSelected" value="'.
         $r->print('<html><body>Unable to tie database.</body></html>');                    $GoToPage.'">');
         return OK;          if($GoToPage eq 'activitylog') {
     }  #        &Apache::lonproblemstatistics::Activity();
           } elsif($GoToPage eq 'problem_statistics') {
     if($GoToPage eq 'Activity Log') {              &Apache::lonhtmlcommon::add_breadcrumb
         &Activity();                  ({href=>'/adm/statistics?reportselected=problem_statistics',
     } elsif($GoToPage eq 'Problem Statistics') {                    text=>'Overall Problem Statistics'});
         &BuildProblemStatisticsPage($cacheDB, $students, $courseID, $c);              &Apache::lonproblemstatistics::BuildProblemStatisticsPage($r,$c);
     } elsif($GoToPage eq 'Problem Analysis') {          } elsif($GoToPage eq 'problem_analysis') {
         &BuildProblemAnalysisPage($cacheDB);              &Apache::lonhtmlcommon::add_breadcrumb
     } elsif($GoToPage eq 'Student Assessment') {                  ({href=>'/adm/statistics?reportselected=problem_analysis',
         &BuildStudentAssessmentPage($cacheDB, $students, $courseID, $c);                    text=>'Detailed Problem Analysis'});
     } elsif($GoToPage eq 'Analyze') {              &Apache::lonproblemanalysis::BuildProblemAnalysisPage($r,$c);
         &BuildAnalyzePage($cacheDB, $students, $courseID);          } elsif($GoToPage eq 'submissiontime_analysis') {
     } elsif($GoToPage eq 'DoDiffGraph') {              &Apache::lonhtmlcommon::add_breadcrumb
         &BuildDiffGraph($courseID);                  ({href=>
     } elsif($GoToPage eq 'PercentWrongGraph') {                        '/adm/statistics?reportselected=submissiontime_analysis',
         &BuildWrongGraph($courseID);                        text=>'Submission Time Plots'});
     } elsif($GoToPage eq 'Class list') {              &Apache::lonsubmissiontimeanalysis::BuildSubmissionTimePage($r,$c);
         $r->print(&BuildClasslist($cacheDB, $students, \@studentInformation,          } elsif($GoToPage eq 'student_submission_reports') {
                                   \@headings, $spacePadding));              &Apache::lonhtmlcommon::add_breadcrumb
                   ({href=>
                     '/adm/statistics?reportselected=student_submission_reports',
                     text=>'Student Submission Reports'});
               &Apache::lonstudentsubmissions::BuildStudentSubmissionsPage($r,$c);
           } elsif($GoToPage eq 'survey_reports') {
               &Apache::lonhtmlcommon::add_breadcrumb
                   ({href=>
                     '/adm/statistics?reportselected=survey_reports',
                     text=>'Survey Reports'});
               &Apache::lonsurveyreports::BuildSurveyReportsPage($r,$c);
           } elsif($GoToPage eq 'correct_problems_plot') {
               &Apache::lonhtmlcommon::add_breadcrumb
                   ({href=>'/adm/statistics?reportselected=correct_problems_plot',
                     text=>'Correct Problems Plot'});
               &Apache::loncorrectproblemplot::BuildCorrectProblemsPage($r,$c);
           } elsif($GoToPage eq 'student_assessment') {
               &Apache::lonhtmlcommon::clear_breadcrumbs();
               &Apache::lonhtmlcommon::add_breadcrumb
                   ({href=>'/adm/statistics?reportselected=student_assessment',
                     text=>'Chart'});
               &Apache::lonstudentassessment::BuildStudentAssessmentPage($r,$c);
           } elsif($GoToPage eq 'grading_analysis') {
               &Apache::lonhtmlcommon::add_breadcrumb
                   ({href=>'/adm/statistics?reportselected=grading_anaylsis',
                     text=>'Grading Analysis'});
               &Apache::longradinganalysis::build_grading_analysis_page($r,$c);
    }
           #
           $r->print("</form>\n");
     }      }
       $r->print(&Apache::loncommon::end_page());
     $r->print('</form>'."\n");  
     $r->print("\n".'</body>'."\n".'</html>');  
     $r->rflush();      $r->rflush();
       #
     return OK;      return OK;
 }  }
   
 # ================================================================ Main Handler  1;
   
 sub handler {  
     $r=shift;  
     &initial();  
   
     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  =pod
     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'}) {  =back
  my $requrl=$r->uri;  
         $ENV{'user.error.msg'}="$requrl:bre:0:0:Course not initialized";  
         return HTTP_NOT_ACCEPTABLE;   
     }  
   
     $r->content_type('text/html');  =cut
     $r->send_http_header;  
   
     &BuildStatistics($r);  #######################################################
   #######################################################
   
     return OK;  
 }  
 1;  
 __END__  __END__
   

Removed from v.1.29  
changed lines
  Added in v.1.129


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>