'."\n";
+ return $Str;
}
-sub BuildDiffGraph {
- my ($courseID)=@_;
+#######################################################
+#######################################################
- my $graphData = &GetGraphData('DiffGraph', $courseID);
- return '';
-}
+=pod
-sub BuildWrongGraph {
- my ($courseID)=@_;
+=item &CreateAndParseOutputSelector()
- my $graphData = &GetGraphData('WrongGraph', $courseID);
- return '';
-}
+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.
-#---- Activity log -------------------------------------------------------
+ Based on code from lonstudentassessment.pm.
-sub LoadDoDiffFile {
- my $file="/home/minaeibi/183d.txt";
- open(FILEID, "<$file");
- my $line=;
- my %DoDiff=();
- my @Act=split('&',$line);
-
-# $r->print(' '.$#Act);
- for(my $n=0;$n<=$#Act;$n++){
- my ($res,$Degree)=split('@',$Act[$n]);
- $DoDiff{$res}=$Degree;
- }
+=cut
- return \%DoDiff;
-}
+#######################################################
+#######################################################
+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',
+ },
+ { name => 'Degree of Difficulty Plot',
+ value => 'plot deg diff',
+ description => 'Generate a plot of the degree of difficulty of each '.
+ 'problem part.',
+ mode => 'plot',
+ show => 'deg of diff',
+ },
+ { name => 'Percent Wrong Plot',
+ value => 'plot per wrong',
+ description => 'Generate a plot showing the percent of students who '.
+ 'were unable to complete each problem part',
+ mode => 'plot',
+ show => 'per wrong',
+ },
+ );
-sub LoadClassFile {
- my $file="/home/minaeibi/class.txt";
- open(FILEID, "<$file");
- my $line;
- my %Grade=();
- while ($line=) {
- my ($id,$ex1,$ex2,$ex3,$ex4,$hw,$final,$grade)=split(' ',$line);
- $Grade{$id}=$grade;
+sub OutputDescriptions {
+ my $Str = '';
+ $Str .= "
Output Modes
\n";
+ $Str .= "
\n";
+ foreach my $outputmode (@OutputOptions) {
+ $Str .="
".$outputmode->{'name'}."
\n";
+ $Str .="
".$outputmode->{'description'}."
\n";
}
- return \%Grade;
+ $Str .= "
\n";
+ return $Str;
}
-#------- Classification
-sub Classify {
- my ($DiscFac, $students)=@_;
- my ($fileGrade) = &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=$fileGrade->{$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"));
- }
- }
+sub CreateAndParseOutputSelector {
my $Str = '';
- for(my $n=0;$n<$cs;$n++){$Str .= ' '.$LS[$n];}
- for(my $n=0;$n<$cm;$n++){$Str .= ' '.$LM[$n];}
- for(my $n=0;$n<$cf;$n++){$Str .= ' '.$LF[$n];}
-
+ 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};
+ }
+ }
+ #
+ # 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 ProcAct {
- # return;
- my ($Act,$Submit)=@_;
- my @Act=split(/\@/,$Act);
- @Act = sort(@Act);
-
- ##$r->print(' '.$#Act);
- ##for(my $n=0;$n<=$#Act;$n++){
-## $r->print(' n='.$n.')'.$Act[$n]);
-## }
+}
-# my $Beg=$Act[0];
- my $Dif=$Submit-$Act[0];
- $Dif = ($Dif>0) ? ($Dif/3600) : 0;
+###############################################
+###############################################
-# $r->print(' Access Number = '.$#Act.' Submit Time='.$Submit.' First Access='.$Act[0].' Last Access='.$Act[$#Act].' Submit - First = '.$Dif.'');
+=pod
+=item &Gather_Student_Data()
-#time spent for solving the problem
-# $r->print(' Def'.($Act[$#Act-1]-$Act[0]));
+Ensures all student data is up to date.
- return $Dif;
-}
+=cut
-sub LoadActivityLog {
-# my $CacheDB = "/home/minaeibi/act183.log.cache";
- my $CacheDB = "/home/httpd/perl/tmp/act183.log.cache";
-
- my %Activity;
- 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(\%Activity);
- }
- else {
-# $r->print("Unable to tie log Build hash to db file");
- }
+###############################################
+###############################################
+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');
}
- return \%Activity;
+ &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
+ $r->rflush();
}
-sub Build_log {
- my ($Activity)=@_;
- my $file="/home/minaeibi/act183.log";
- open(FILEID, "<$file");
- my $line;
- my $count=0;
- while ($line=) {
- 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(' '.$time.':'.$who.'---'.$res);
- &Update_PrgInit($count);
+###############################################
+###############################################
- }
- }
- }
+=pod
-# my $c=1;
-# foreach (sort keys %Activity) {
-# $r->print(' '.$c.')'.$_.' ... '.$Activity{$_});
-# $c++;
-# }
+=item &BuildProblemStatisticsPage()
-}
+Main interface to problem statistics.
-sub Activity {
-# $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(" Using $file");
- $r->rflush();
- open(FILEID, "<$file");
- my $line;
- my @allaccess;
- my $Count=0;
- while ($line=) {
- 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(" $Count) ".localtime($date).": $who --> $res");
-# if ($post) {
-# $Count++;
-# $r->print(" $Count) Sent data ".join(':',
-# &Apache::lonnet::unescape(@posts)).'');
-# }
- $r->rflush();
- }
- #push (@allaccess,unescape($access));
- #print $machine;
- }
+=cut
+
+###############################################
+###############################################
+sub BuildProblemStatisticsPage {
+ my ($r,$c)=@_;
+ #
+ $output_mode = 'html';
+ $show = 'grouped';
+ #
+ $r->print(&CreateInterface());
+ $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("
'."\n");
- $r->rflush();
-}
+Presents the statistics data in a single html table which can be sorted by
+different columns.
+=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;
+###############################################
+###############################################
+sub output_html_ungrouped {
+ my ($r,$option) = @_;
+ #
+ my $problem_num = 0;
+ my $show_container = 0;
+ my $show_part = 0;
+ #$r->print(&ProblemStatisticsLegend());
+ my @Header = ("Title","Part","#Stdnts","Tries","Mod",
+ "Mean","#YES","#yes","%Wrng","DoDiff",
+ "S.D.","Skew");#,"D.F.1st","D.F.2nd");
+ #
+ my $sortby = undef;
+ foreach (@Header) {
+ if ($ENV{'form.sortby'} eq $_) {
+ $sortby = $_;
}
-
- 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'}='-';
+ }
+ if (! defined($sortby) || $sortby eq '') {
+ $sortby = 'Container';
+ }
+ # If there is more than one sequence, list their titles
+ my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
+ if (@Sequences > 1) {
+ unshift(@Header,"Container");
+ $show_container = 1;
+ }
+ #
+ # If the option for showing the problem number is needed, push that
+ # on the list too
+ if (defined($option) && $option =~ /show probnum/) {
+ unshift(@Header,"P#");
+ }
+ #
+ $r->print('
'."\n");
+ $r->rflush();
+ #
+ # Compile the data
+ my @Statsarray;
+ foreach my $sequence (@Sequences) {
+ next if ($sequence->{'num_assess'}<1);
+ foreach my $resource (@{$sequence->{'contents'}}) {
+ next if ($resource->{'type'} ne 'assessment');
+ foreach my $part (@{$resource->{'parts'}}) {
+ $problem_num++;
+ my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD,
+ $SKEW) = &Apache::loncoursedata::get_problem_statistics
+ (undef,$resource->{'symb'},$part,
+ $ENV{'request.course.id'});
+ #
+ $show_part = 1 if ($part ne '0');
+ $part = ' ' if ($part == 0);
+ #
+ my $wrongpercent = 0;
+ if (defined($num) && $num > 0) {
+ $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
+ }
+ push (@Statsarray,
+ { 'sequence' => $sequence,
+ 'resource' => $resource,
+ 'Title' => $resource->{'title'},
+ 'Part' => $part,
+ '#Stdnts' => $num,
+ 'Tries' => $tries,
+ 'Mod' => $mod,
+ 'Mean' => $mean,
+ '#YES' => $Solved,
+ '#yes' => $solved,
+ '%Wrng' => $wrongpercent,
+ 'DoDiff' => $DegOfDiff,
+ 'S.D.' => $STD,
+ 'Skew' => $SKEW,
+ 'problem_num' => $problem_num,
+ });
}
-
- # 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;
+ }
+ }
+ #
+ # Table Headers
+ $r->print('
'."\n");
+ my $Str = '';
+ foreach (@Header) {
+ next if ($_ eq 'Part' && !$show_part);
+ # Do not allow sorting on some fields
+ if ($_ eq $sortby || /^(Part|P\#)$/) {
+ $Str .= '
'."\n";
-
- return $Ptr;
+ my @GData = ($title,$xaxis,$yaxis,$Max,$sendCount,$sendValues);
+ return '';
}
-sub ProblemStatisticsButtons {
- my ($displayFormat)=@_;
+###############################################
+###############################################
- my $Ptr = '
';
- $Ptr .= '';
$Ptr .= '
';
- $Ptr .= '#Stdnts:
';
- $Ptr .= '
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 .= 'Mod
';
+ $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 .= '
';
-# Kashy formula
-# ' DoDiff : Degree of Difficulty of the problem. '.
-# '[ Tries/(#YES+#yes+0.1) ] '.
- #Gerd formula
- $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: ';
@@ -1020,57 +910,10 @@ sub ProblemStatisticsLegend {
$Ptr .= '
Disc.
';
$Ptr .= '
Number of Students had at least one discussion.';
$Ptr .= '
';
-
return $Ptr;
}
#---- END Problem Statistics Web Page ----------------------------------------
-#---- Problem Statistics Graph Web Page --------------------------------------
-
-# ------------------------------------------- Prepare data for Graphical chart
-
-sub GetGraphData {
- my $ylab = shift;
- 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__