'."\n";
+=item &CreateInterface()
+
+Create the main intereface for the statistics page. Allows the user to
+select sections, maps, and output.
+
+=cut
+
+###############################################
+###############################################
+sub CreateInterface {
+ my $Str = '';
+ $Str .= '
'."\n";
+ return $Str;
+}
+
+#######################################################
+#######################################################
+
+=pod
+
+=item &CreateAndParseOutputSelector()
+
+Construct a selection list of options for output and parse output selections.
+The current output selected is indicated by the values of the two package
+variables $output_mode and $show. @OutputOptions holds the descriptions of
+the output options and the values for $output_mode and $show.
+
+Based on code from lonstudentassessment.pm.
+
+=cut
+
+#######################################################
+#######################################################
+my $output_mode;
+my $show;
+
+my @OutputOptions =
+ (
+ { name => 'problem statistics grouped by sequence',
+ value => 'HTML problem statistics grouped',
+ description => 'Output statistics for the problem parts.',
+ mode => 'html',
+ show => 'grouped',
+ },
+ { name => 'problem statistics ungrouped',
+ value => 'HTML problem statistics ungrouped',
+ description => 'Output statistics for the problem parts.',
+ mode => 'html',
+ show => 'ungrouped',
+ },
+ { name => 'problem statistics, Excel',
+ value => 'Excel problem statistics',
+ description => 'Output statistics for the problem parts '.
+ 'in an Excel workbook',
+ mode => 'excel',
+ show => 'all',
+ },
+ );
+
+sub OutputDescriptions {
+ my $Str = '';
+ $Str .= "
Output Modes
\n";
+ $Str .= "
\n";
+ foreach my $outputmode (@OutputOptions) {
+ $Str .="
".$outputmode->{'name'}."
\n";
+ $Str .="
".$outputmode->{'description'}."
\n";
+ }
+ $Str .= "
\n";
+ return $Str;
+}
+
+sub CreateAndParseOutputSelector {
+ my $Str = '';
+ my $elementname = 'statsoutputmode';
+ #
+ # Format for output options is 'mode, restrictions';
+ my $selected = 'HTML problem statistics grouped';
+ if (exists($ENV{'form.'.$elementname})) {
+ if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
+ $selected = $ENV{'form.'.$elementname}->[0];
+ } else {
+ $selected = $ENV{'form.'.$elementname};
+ }
}
- $Result .= "\n".'
'."\n";
- $r->print($Result);
- $r->rflush();
+ #
+ # Set package variables describing output mode
+ $output_mode = 'html';
+ $show = 'all';
+ foreach my $option (@OutputOptions) {
+ next if ($option->{'value'} ne $selected);
+ $output_mode = $option->{'mode'};
+ $show = $option->{'show'};
+ }
+ #
+ # Build the form element
+ $Str = qq/";
+ return $Str;
}
-sub CloseTable {
- my ($cache,$r)=@_;
- if($cache->{'DisplayFormat'} eq 'Display CSV Format') {
- return;
- }
- $r->print("\n".'
'."\n");
+###############################################
+###############################################
+
+=pod
+
+=item &Gather_Student_Data()
+
+Ensures all student data is up to date.
+
+=cut
+
+###############################################
+###############################################
+sub Gather_Student_Data {
+ my ($r) = @_;
+ my $c = $r->connection();
+ #
+ my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
+ #
+ my @Students = @Apache::lonstatistics::Students;
+ #
+ # Open the progress window
+ my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
+ ($r,'Statistics Compilation Status',
+ 'Statistics Compilation Progress', scalar(@Students));
+ #
+ while (my $student = shift @Students) {
+ return if ($c->aborted());
+ my ($status,undef) = &Apache::loncoursedata::ensure_current_data
+ ($student->{'username'},$student->{'domain'},
+ $ENV{'request.course.id'});
+ &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
+ 'last student');
+ }
+ &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
$r->rflush();
}
+###############################################
+###############################################
-
-# ------ Dump the Student's DB file and handling the data for statistics table
-sub ExtractStudentData {
- my ($cache,$name,$list,$Discuss,$r)=@_;
- my %discriminantFactor;
-
- my $totalTries = 0;
- my $totalAwarded = 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;
- my $tempSequenceOrder=100;
-
-#$Apache::lonxml::debug=1;
-#&Apache::lonhomework::showhash(%$cache);
-#$Apache::lonxml::debug=0;
-
- foreach my $sequence (split(':', $cache->{'orderedSequences'})) {
- my $tempProblemOrder=100;
- $tempSequenceOrder++;
-# if($cache->{'ProblemStatisticsMap'} ne 'All Maps' &&
-# $cache->{'ProblemStatisticsMap'} ne $cache->{$sequence.':title'}) {
-# $r->print(" sequences=$cache->{'orderedSequences'}");
-# $r->print(" ".$cache->{$sequence.':title'});
-# next;
-# }
-
- my $Dis = '';
- foreach my $problemID (split(':', $cache->{$sequence.':problems'})) {
-# $r->print(" problems=$cache->{$sequence.':problems'}");
- my $problem = $cache->{$problemID.':problem'};
-# $r->print(" $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'})) {
- $tempProblemOrder++;
-# $r->print(" parts=$cache->{$sequence.':'.$problemID.':parts'}");
- $partData{$part.':order'}=$tempProblemOrder;
- $partData{$part.':tries'}=0;
- $partData{$part.':code'}='-';
- }
+=pod
- # 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.
- 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;
- }
+=item &BuildProblemStatisticsPage()
- my $tries=0;
- my $time=0;
- my $awarded=0;
- $Discussed=0;
- my $code='-';
-
- $awarded = $cache->{$name.
- "$Version:$problem:resource.".
- "$part.awarded"};
- $partData{$part.':awarded'} = ($awarded) ? $awarded : 0;
- $totalAwarded += $awarded;
-
- $tries = $cache->{$name.":$Version:$problem".
- ":resource.$part.tries"};
- $partData{$part.':tries'} = ($tries) ? $tries : 0;
- $partData{$part.':wrong'} = $partData{$part.':tries'};
- $totalTries += $tries;
-
- my $val = $cache->{$name.":$Version:$problem".
- ":resource.$part.solved"};
- if ($val eq 'correct_by_student') {$code = 'C';}
- elsif ($val eq 'correct_by_override') {$code = 'O';}
- elsif ($val eq 'incorrect_attempted') {$code = 'I';}
- elsif ($val eq 'incorrect_by_override'){$code = 'I';}
- elsif ($val eq 'excused') {$code = 'x';}
- $partData{$part.':code'}=$code;
- if($partData{$part.':wrong'} ne 0 &&
- ($code eq 'C' || $code eq 'O')) {
- $partData{$part.':wrong'}--;
- }
- }
- }
+Main interface to problem statistics.
-#$Apache::lonxml::debug=1;
-#&Apache::lonhomework::showhash(%partData);
-#$Apache::lonxml::debug=0;
-
- # Loop through all the parts for the current problem in the
- # correct order and prepare the output
- foreach (split(/\:/,$cache->{$sequence.':'.$problemID.
- ':parts'})) {
- #$r->print(" ".$cache->{$sequence.':title'});
-
- my $Yes = 0;
- if($partData{$_.':code'} eq 'C' ||
- $partData{$_.':code'} eq 'O') {
- $Yes=1;
- }
- my $pOrder=$partData{$_.':order'};
- my $ptr = $tempSequenceOrder.':'.$pOrder.':'.$problemID;
+=cut
- if($_ > 1) {
- $ptr .= "*(part $_)";
- $Dis .= '&';
- }
+###############################################
+###############################################
+sub BuildProblemStatisticsPage {
+ my ($r,$c)=@_;
+ #
+ $output_mode = 'html';
+ $show = 'grouped';
+ #
+ $r->print(&CreateInterface());
+ $r->print('');
+ $r->print('');
+ $r->print('');
+ if (! exists($ENV{'form.statsfirstcall'})) {
+ $r->print(<
+
+Please make your selections in the boxes above and hit
+the button marked "Update Display".
+
+
+ENDMSG
+ return;
+ }
+ #
+ &Gather_Student_Data($r);
+ #
+ #
+ if ($output_mode eq 'html') {
+ $r->print("
");
+ }
+ return;
+}
- my ($pr_no,$dod)=split('&',$ptr);
-# my $DoDiff=$DoDiff->{$dod};
-# $r->print(' '.$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{$_.':wrong'}.
- '&'.$partData{$_.':code'};
- push (@$list, $ptr."&$Discussed");
-# $r->print(' '.$_.$name.'---'.$ptr);
-
-#### if ($DoDiff>0.85) {
+=pod
- $TimeTot += $time;
+=item &output_html_grouped_by_sequence()
- 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(' '.$name.'---'.$problem.'---'.$spe);
-# }
- $TotalOpend++;
- $ProbTot++;
+Presents the statistics data as an html table organized by the order
+the assessments appear in the course.
- $tempProblemOrder++;
+=cut
+
+###############################################
+###############################################
+sub output_html_grouped_by_sequence {
+ my ($r) = @_;
+ my $problem_num = 0;
+ #$r->print(&ProblemStatisticsLegend());
+ foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
+ next if ($sequence->{'num_assess'}<1);
+ $r->print("
".$sequence->{'title'}."
");
+ $r->print('
'."\n");
+ $r->print('
'."\n");
+ $r->print('
');
+ my $Str = &statistics_table_header('no container no plots');
+ $r->print('
'.$Str."
\n");
+ foreach my $resource (@{$sequence->{'contents'}}) {
+ next if ($resource->{'type'} ne 'assessment');
+ foreach my $part (@{$resource->{'parts'}}) {
+ $problem_num++;
+ my $data = &get_statistics($sequence,$resource,$part,
+ $problem_num);
+ my $option = '';
+ $r->print('
Total Number of Students opened the problem.';
+ $Ptr .= '#Stdnts
';
+ $Ptr .= '
Total number of students attempted the problem.';
$Ptr .= '
';
- $Ptr .= 'Tries:
';
- $Ptr .= '
Total Number of Tries for solving the problem.';
+ $Ptr .= 'Tries
';
+ $Ptr .= '
Total number of tries for solving the problem.';
$Ptr .= '
';
- $Ptr .= 'Mod:
';
- $Ptr .= '
Maximunm Number of Tries for solving the problem.';
+ $Ptr .= 'Max Tries
';
+ $Ptr .= '
Largest number of tries for solving the problem by a student.';
$Ptr .= '
';
- $Ptr .= 'Mean:
';
- $Ptr .= '
Average Number of the tries. [ Tries / #Stdnts ]';
+ $Ptr .= 'Mean
';
+ $Ptr .= '
Average number of tries. [ Tries / #Stdnts ]';
$Ptr .= '
';
- $Ptr .= '#YES:
';
+ $Ptr .= '#YES';
$Ptr .= '
Number of students solved the problem correctly.';
$Ptr .= '
';
- $Ptr .= '#yes:
';
+ $Ptr .= '#yes';
$Ptr .= '
Number of students solved the problem by override.';
$Ptr .= '
';
- $Ptr .= '%Wrng:
';
- $Ptr .= '
Percentage of students tried to solve the problem ';
- $Ptr .= 'but still incorrect. [ 100*((#Stdnts-(#YES+#yes))/#Stdnts) ]';
+ $Ptr .= '%Wrong
';
+ $Ptr .= '
Percentage of students who tried to solve the problem ';
+ $Ptr .= 'but is still incorrect. [ 100*((#Stdnts-(#YES+#yes))/#Stdnts) ]';
$Ptr .= '
';
- $Ptr .= 'DoDiff:
';
+ $Ptr .= 'DoDiff';
$Ptr .= '
Degree of Difficulty of the problem. ';
$Ptr .= '[ 1 - ((#YES+#yes) / Tries) ]';
$Ptr .= '
';
- $Ptr .= 'S.D.:
';
+ $Ptr .= 'S.D.';
$Ptr .= '
Standard Deviation of the tries. ';
$Ptr .= '[ sqrt(sum((Xi - Mean)^2)) / (#Stdnts-1) ';
$Ptr .= 'where Xi denotes every student\'s tries ]';
$Ptr .= '
';
- $Ptr .= 'Skew.:
';
+ $Ptr .= 'Skew.';
$Ptr .= '
Skewness of the students tries.';
$Ptr .= '[(sqrt( sum((Xi - Mean)^3) / #Stdnts)) / (S.D.^3)]';
$Ptr .= '
';
- $Ptr .= 'Dis.F.:
';
+ $Ptr .= 'Dis.F.';
$Ptr .= '
Discrimination Factor: A Standard for evaluating the ';
$Ptr .= 'problem according to a Criterion ';
- $Ptr .= '[Applied Criterion in %27 Upper Students - ';
- $Ptr .= 'Applied the same Criterion in %27 Lower Students] ';
+ $Ptr .= '[Criterion to group students into %27 Upper Students - ';
+ $Ptr .= 'and %27 Lower Students] ';
$Ptr .= '1st Criterion for Sorting the Students: ';
$Ptr .= 'Sum of Partial Credit Awarded / Total Number of Tries ';
$Ptr .= '2nd Criterion for Sorting the Students: ';
@@ -784,146 +955,10 @@ sub ProblemStatisticsLegend {
$Ptr .= '
Disc.
';
$Ptr .= '
Number of Students had at least one discussion.';
$Ptr .= '
';
-
return $Ptr;
}
-#------- Processing upperlist and lowerlist according to each problem
-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} ");
- $Sum1=0;
- $Sum2=0;
- $nStudent=0;
- }
-
- return %Proc;
-}
-
-#------- Creating Discimination factor
-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);
-}
-
-
-
#---- END Problem Statistics Web Page ----------------------------------------
-#---- Problem Statistics Graph Web Page --------------------------------------
-
-# ------------------------------------------- Prepare data for Graphical chart
-
-sub BuildDiffGraph {
- my ($r)=@_;
-
- my $graphData = &GetGraphData('DiffGraph', $r);
- return '';
-}
-
-sub BuildWrongGraph {
- my ($r)=@_;
-
- my $graphData = &GetGraphData('WrongGraph', $r);
- return '';
-}
-
-
-sub GetGraphData {
- my ($ylab,$r,%GraphDat)=@_;
- my $Col;
- my $data='';
- my $count = 0;
- 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;
- my $GData=$Course.'&'.'Problems'.'&'.$ylab.'&'.
- $Max.'&'.$count.'&'.$data;
- }
- else {
- $r->print("Unable to tie hash to db file");
- }
- }
-}
-
-
1;
__END__