#	Menu log analysis program
#	This assumes log files have been cleaned up
#	run by:
#		perl analyze.pl ? > summary
#	where "?" is a question mark and matches all the log files
#	to be summarized together.

# Copyright 1993, 1994, 1995 by the Regents of the University of California.
# see the file "Copyright" in the distribution for conditions of use.

#	Step 1 - scan log records, saving data

while( 1 ) {
	# get ">:" line with logon
	&get_line;
	last if( eof );
	die "expecting >:" if $key ne ">";
	$logon = $word;
	$sessions++;
	$user{$logon} = 1;

	# now "B:" line
	&get_line;
	die "expecting B:" if $key ne "B";
	$actions = 0;
	$commands = 0;

	&get_line;
	while( $key ne "E" ) {
		# now commands, menus, etc.
		$commands++ if $key eq "C";
		$actions++;
		$action_ch{$key}++;
		$action_dtl{$key.$word}++;
		$action_dtl_usr{$logon."|".$key.$word} = 1;
		&get_line;
	}

	# got E line,e.g. $word has "000188/00004"
	$time = $word;
	$time =~ s|/.*||;
	$time =~ s/^0*//;
	$m = int($time/60);	# minutes
	if( $m < 60 ) {
		$minutes[$m]++;
	} else {
		$hr = int($m/60);
		$hour[$hr]++;
		$maxhr = $hr if $hr > $maxhr;
	}
	$commands_per_session[$commands]++;
	$max_commands = $commands if $commands > $max_commands;

	# now "<:" line
	&get_line;
	$all_actions += $actions;
	die "expecting <:" if $key ne "<";
}

# end of input

#	Step 2 - print summary line - sessions, users, items logged

#	count distinct users
$i = 0;
foreach $key ( keys %user ) { $i++ };

print "$sessions sessions, ";
print "$i users, ";
print "$all_actions items logged\n";
print "\n";

#	Step 3 - print counts for major types

# action counts - by type
$name{ "B" } = "Begin";
$name{ "C" } = "Command";
$name{ "M" } = "Menu";
$name{ "?" } = "General menu help";
$name{ "*" } = "Item help";
$name{ "+" } = "Use short cut";
$name{ "a" } = "Add item short cut";
$name{ "A" } = "Add menu short cut";
$name{ "d" } = "Delete short cut";
$name{ "P" } = "Parity error";
$name{ "T" } = "Terminate abnormally";

print "Use counts for types of menu actions:\n";
foreach $key ( sort keys %action_ch ) {
	printf "    %s %7d  %s\n", $key, $action_ch{$key}, $name{$key};
}
print "\n";

#	Step 4 - print counts for all individual items

@line = ();
print "Use counts for all items, alpha sort:\n";
$i = 0;
foreach $key ( sort keys %action_dtl ) {
	$key =~ /^(.)(.*)$/;
	$line[$i] = sprintf("%6d %s %-11s",
			$action_dtl{$key}, $1, substr($2,0,11) );
	$i++;
}
&show_cols;

#	Step 5 - print counts for top few according to use counts

$show_max = 24;
print "\n";
print "Top $show_max items according to number of uses:\n";
@line = ();
$i = 0;
foreach $key ( reverse sort numeric_dtl keys %action_dtl ) {
	$key =~ /^(.)(.*)$/;
	$line[$i] = sprintf("%6d %s %-11s",
			$action_dtl{$key}, $1, substr($2,0,11) );
	$i++;
	last if $i >= $show_max;
}

#	Step 6 - print counts for top few according to distinct user counts

&show_cols;
print "\n";
print "Top $show_max items according to number of users:\n";
@line = ();
%action_by_user = ();
$i = 0;
foreach $key ( keys %action_dtl_usr ) {
	# keys are: logon.|.key.action
	# one key per user/action
	$key =~ /[^|]+\|(.*)$/;
	$action = $1;
	# one more user did this action
	$action_by_user{$action}++;
}

foreach $key ( reverse sort numeric_abu keys %action_by_user ) {
	$key =~ /^(.)(.*)$/;
	$line[$i] = sprintf("%6d %s %-11s",
			$action_by_user{$key}, $1, substr($2,0,11) );
	$i++;
	last if $i >= $show_max;
}
&show_cols;

#	Step 7 - distribution of elapsed time/session

print "\n";
print "Sessions sorted by elapsed time:\n";
$cum_pc = 0;
&show_min( 0 );
&show_min( 1 );
&show_min( 2 );
&show_min( 3 );
&show_min( 4 );
sub show_min{
	local( $n ) = @_;
	local( $pc );

	$pc = $minutes[$n]/$sessions*100.0;
	$cum_pc += $pc;
	printf "  %2d min-%2d min %5d  %6.1f%%  %6.1f%%\n",
			$n, $n+1, $minutes[$n], $pc, $cum_pc;
}
&show_mins( 5, 10 );
&show_mins( 10, 15 );
&show_mins( 15, 20 );
&show_mins( 20, 30 );
&show_mins( 30, 45 );
&show_mins( 45, 60 );
sub show_mins{
	local( $low, $hi ) = @_;
	local( $count, $pc );

	$count = &min_sum( $low, $hi );
	$pc = $count/$sessions*100.0;
	$cum_pc += $pc;
	printf "  %2d min-%2d min %5d  %6.1f%%  %6.1f%%\n",
			$low, $hi, $count, $pc, $cum_pc;
}
&show_hr( 1 );
&show_hr( 2 );
&show_hr( 3 );
&show_hr( 4 );
&show_hr( 5 );
sub show_hr{
	local( $hr ) = @_;
	local( $pc );

	$pc = $hour[$hr]/$sessions*100.0;
	$cum_pc += $pc;
	printf "  %2d hr -%2d hr  %5d  %6.1f%%  %6.1f%%\n",
			$hr, $hr+1, $hour[$hr], $pc, $cum_pc;
}

$count = 0;
for( $i = 6; $i <= $maxhr; $i++ ) {
	$count += $hour[$i];
}
$pc = $count/$sessions*100.0;
$cum_pc += $pc;
printf "      > 6 hrs     %3d  %6.1f%%  %6.1f%%\n", $count, $pc, $cum_pc;

sub min_sum {
	local( $low, $hi ) = @_;
	local( $i, $count );

	for( $i = $low; $i < $hi; $i++ ) {
		$count += $minutes[$i];
	}
	$count;
}

#	Step 8 - sessions by number of commands

print "\n";
print "Commands executed per menu session:\n";
print "(Actual commands, other items excluded)\n";

$cum_pc = 0;

for( $i = 0; $i < 10; $i++ ) {
	&show_com( $i );
}

sub show_com {
	local( $n ) = @_;
	local( $pc );

	$pc = $commands_per_session[$n]/$sessions*100.0;
	$cum_pc += $pc;
	printf "      %2d  %6d  %6.1f%%  %6.1f%%\n",
		$n, $commands_per_session[$n], $pc, $cum_pc;
}

	&show_com_range( 10, 14 );
	&show_com_range( 15, 19 );
	&show_com_range( 20, 29 );
	&show_com_range( 30, 39 );
	&show_com_range( 40, 49 );
	&show_com_range( 50, 74 );
	&show_com_range( 75, $max_commands ) if $max_commands >= 75;

sub show_com_range {
	local( $lo, $hi ) = @_;
	local( $count, $i, $pc );

	for( $i = $lo; $i <= $hi; $i++ ) {
		$count += $commands_per_session[$i];
	}
	$pc = $count/$sessions*100.0;
	$cum_pc += $pc;
	print  "   " if $hi < 100;
	print  "  " if $hi >= 100;
	printf "%2d-%2d  %6d  %6.1f%%  %6.1f%%\n",
				$lo, $hi, $count, $pc, $cum_pc;
}

sub numeric_dtl { $action_dtl{$a} <=> $action_dtl{$b} }

sub numeric_abu { $action_by_user{$a} <=> $action_by_user{$b} }

sub get_line {
	$_ = <>;
	s/^.(.)(.)//;
	$key = $1;
	die "missing colon" if $2 ne ":";
	/(\w+)/;
	$word = $1;
}

sub show_cols {
	local( $cols, $rows, $i, $j );

	$cols = 4;
	$rows = int((@line+$cols-1)/$cols);
	for ( $i = 0; $i < $rows; $i++ ) {
		for( $j = $i; $j < @line; $j += $rows ) {
			print $line[$j];
		}
		print "\n";
	}
}
