#!/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 "\n

General

\n"; _print_choose_stat("general",$S{gen}->{q}); print "
\n

Groupwise

\n"; _print_choose_stat("groupwise",$S{grw}->{q}); print "
\n

Word

\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; } #----------------------------------------------------------------------