#!/usr/bin/perl -w
## statistics from all concerned
use AnApache;
use Session;
use Type;
use Final;
use Utils;
use Choose;
use Html;
use strict;
my $EXAMDIR="/the/exam/home"
my $examdir="$EXAMDIR/exam";
sub _gather;
sub _print_choose_stat;
my $session = new Session;
exit( 0 ) unless( defined $session );
my $r = $session->{request};
if( $r->method !~ /^get$/i ){
$r->status(Apache::FORBIDDEN);
return;
}
my $you = $r->user(); # the user who is authenticated
my %S; # statistics hash
$r->content_type("text/html");
Html::header("Exam statistics","Statistics");
# collect information
_gather($examdir);
$S{type_done} = 1 if( ! $S{type_done} );
$S{type_n} = 1 if( ! $S{type_n} );
printf "Total: $S{n}
Done: $S{done}
- finished typing: $S{type_done}
- finished test: $S{test_done}
Typing: total number of attempts: $S{type_n}
- average number of attempts: %.2f
- average typing speed: %.0f chars/min
- average number of errors: %.2f
",
$S{type_n}/$S{type_done},
$S{type_speed}/$S{type_n},
$S{type_mistake}/$S{type_n};
printf "Multiple choice - correct answers (number of trials)
- general: %.2f%% (%d)
- groupwise %.2f%% (%d)
- word %.2f%% (%d)
",
$S{gen}->{w}*100.0/$S{gen}->{n},$S{gen}->{nn},
$S{grw}->{w}*100.0/$S{grw}->{n},$S{grw}->{nn},
$S{wrd}->{w}*100.0/$S{wrd}->{n},$S{wrd}->{nn};
printf "Offline: total number of attempts: $S{offline} for $S{offline_n} exams
- accepted: $S{done}
- offline taken on average: %.2f times
- offline taken successfully %.2f%% (percentage of exams)
",
$S{offline}/$S{offline_n},$S{done}*100.0/$S{offline_n};
print "\nGeneral
\n";
_print_choose_stat("general",$S{gen}->{q});
print "
\nGroupwise
\n";
_print_choose_stat("groupwise",$S{grw}->{q});
print "
\nWord
\n";
_print_choose_stat("word",$S{wrd}->{q});
print "
\n";
Html::tail();
return;
#----------------------------------------------------------------------
sub _choose {
my ($qa,$title,$ans) = @_;
return if(! $ans);
return if( $ans !~ /^([0-9]+):([0-9]+):[0-9]*:(.*)$/ );
my ($wrong,$total,$err)=($1,$2,$3);
printf "$title Correct: %d/%d\n", $total-$wrong,$total;
printf "0);
print ">\n";
foreach (split /,/,"$err," ){
next if( ! m#^([0-9]+)/([0-9]*)# );
Choose::question($qa,$1,$2);
}
printf "
\n";
}
#----------------------------------------------------------------------
sub _choose_stat {
# choose statistics
my($qa,$ans,$stat)=@_;
return if( ! $ans );
return if( $ans !~ /^([0-9]+):([0-9]+):([0-9]*):(.*)$/ );
my ($wrong,$total,$seed,$err)=($1,$2,$3,$4);
$stat->{n} += 0+$total; $stat->{w} += $total - $wrong;
$stat->{nn}++;
my (@peq,%ast);
Choose::question_idx($qa,$seed,$total,\@peq);
for my $i (1..$total) {
my $idx=$peq[$i];
$ast{"$idx"}="OK";
}
foreach (split /,/,"$err," ){
next if( ! m#^([0-9]+)/([0-9]*)# );
if ($ast{"$1"} ne "OK" ){ print "[ $S{nxt} [ err: $1:$2, val=",$ast{"$1"}," ]]";}
$ast{"$1"} = ($2 eq "" ? "NONE" : $2);
## $1: question no, $2: wrong answer; $2 eq "": no answer
## Choose::question($qa,$1,$2);
}
foreach ( keys %ast ){
$stat->{q}->{$_}{$ast{$_}}=0
if(! defined $stat->{q}->{$_} || ! defined $stat->{q}->{$_}{$ast{$_}} );
$stat->{q}->{$_}{$ast{$_}}++;
}
###
### generate list by $seed, then make statistics good/wrong/not marked
### $qa = name...
###
# if($qa eq "word"){
# printf " [(%d) %d, (%d) %d] ",$stat->{n},$total,$stat->{w},$total-$wrong;
# }
}
sub _print_choose_stat {
my ($qa,$stat) = @_;
## sort by hardness
foreach my $que (keys %{$stat} ){
my $ok=$stat->{$que}{"OK"};
my $wrong=0;
for my $i (1..10){
my $v=$stat->{$que}{"$i"};
$wrong += $v if(defined $v);
}
$ok=$ok/($ok+$wrong) if($ok+$wrong>0);
$stat->{$que}{"PCT"} = $ok;
}
## foreach my $que (sort { 0+$a <=> 0+$b } keys %{$stat})
foreach my $que (sort { ($stat->{$a}{"PCT"}) <=> ($stat->{$b}{"PCT"}) } keys %{$stat} )
{
printf " OK %3.0f%% (%d),
missing: %d, ",
100.0*($stat->{$que}{"PCT"}),$stat->{$que}{"OK"},$stat->{$que}{"NONE"};
for my $i (1..10){
my $v=$stat->{$que}{"$i"};
printf " $i: %d, ",$v if(defined $v && $v ne "" );
}
print "
\n";
Choose::question($qa,$que);
}
}
sub _stat_attempt {
## called for each attempt in order
my ($attno,$res) = @_;
## filetest: $res->{ftn} errors (if it was taken at all)
if( defined $res->{ftn} && $res->{ftn} ne "" ){
$S{file_n}++; $S{file_err} += 0+$res->{ftn};
}
## typetest: ttno, tttime,tterr,ttt
if( $res->{ttno} && $res->{ttt} && $res->{tterr} < 100){
$S{type_n}++; $S{type_mistake} += $res->{tterr};
$S{type_speed}+= 60*$res->{ttt}/$res->{tttime};
}
## offline exam
if( $res->{done} ){ $S{offline}++; }
_choose_stat("general",$res->{gen},$S{gen});
_choose_stat("groupwise",$res->{grw},$S{grw});
_choose_stat("word",$res->{wrd},$S{wrd});
}
sub _statuser {
## gather stat from the user
my($data)=@_;
return if(! open(DATA,"$data") );
my $attempt=0; my $res={}; my $done=0;
while(){
chomp;
if( /^Attempt:([0-9]+),/ ){
my $new=$1; _stat_attempt($attempt,$res) if($attempt>0);
$attempt=$new; $res={}; $res->{done}=$done;
}
if( /^Filetest Result:([0-9]+):/ ){ $res->{ftn}=$1; }
if( /^Typetest: ([0-9]+)$/ ){ $res->{ttno}=$1; }
if( /^Typetest Result:([0-9]+):([0-9]+):(.*)$/ ){
$res->{tttime}=$1; $res->{tterr}=$2; $res->{ttt}=length($3);
}
if( /^general:(.*)$/ ){ $res->{gen}=$1; }
if( /^groupwise:(.*)$/ ){ $res->{grw}=$1; }
if( /^word:(.*)$/ ){ $res->{wrd}=$1; }
if( /^Result: done$/ ){
$done=1; $res->{done}=$done;
}
}
close(DATA);
_stat_attempt($attempt,$res) if($attempt>0);
$S{offline_n}++ if($done);
}
sub _gather {
## gather statistics information
my($ex)=@_;
# global: number of users, how many did it, etc.
$S{n}=$S{done}=$S{type_done}=$S{test_done}=0;
# filetest:
$S{file_n}=$S{file_err}=0;
# typing test:
$S{type_n}=$S{type_speed}=$S{type_mistake}=0;
$S{offline}=0; $S{offline_n}=0;
# test general groupwise, word total/wrong
# (including all attempts, except where all answers are wrong)
foreach my $v ( qw( gen grw wrd ) ){
$S{$v}={}; $S{$v}->{n}=0; $S{$v}->{w}=0; $S{$v}->{nn}=0;
}
# $S{gen}=$S{grw}=$S{wrd}={};
# $S{gen}->{n}=$S{gen}->{w}=
# $S{grw}->{n}=$S{grw}->{w}=
# $S{wrd}->{n}=$S{wrd}->{w}=0;
open(DIR,"/bin/ls $ex |") || die "Cannot list exam directory\n";
while(){
chomp;
my $next=$_;
$S{nxt}=$next;
$S{n}++; # one more user
$S{done}++ if( -e "$ex/$next/done" );
$S{type_done}++ if( -e "$ex/$next/typing" );
$S{test_done}++ if( -e "$ex/$next/choice" );
_statuser("$ex/$next/data");
}
close(DIR);
return;
}
#----------------------------------------------------------------------