lilalo

diff l3-frontend @ 31:196c82b6e538

l3-cgi:
* Сделана поддержка кодировок клиента отличных от utf-8 (пока что почему-то не работает)
* Сделана поддержка комментирования из самой командной строки.
Комментарии вставлюятся с помощью символов #^, #v или #=
Комментарии записываются в элементы note и note_title

l3-frontend:

* Сделана поддержка комментирования из самой командной строки.
Комментарии вставлюятся с помощью символов #^, #v или #=
* Вместо использования программы mywi-client, обращение к mywi-серверу выполняется самостоятельно
* Выполняется разбор команды с целью выявления новых команд, ведения статистики, генерирования подсказок и т.д.
* Во всплывающих командах к подсказкам выводится информация от mywi
* Выводится статистическая информация о журнале
"Время первой команды журнала"
"Время последней команды журнала"
"Количество командных строк в журнале"
"Процент команд с кодом ненулевым кодом завершения, %"
"Суммарное время работы с терминалом <sup><font size='-2'>*</font></sup>, час"
"Количество командных строк в единицу времени, команда/мин"
"Частота использования команд"
"Частота использования команд"
"Частота использования этих команд < 0.5%"
* В заголовке страницы выводится информация о курсе и имя слушателя
* Расшифровка к информации о курсе выводится только если есть сама информация
* В оглавлении учитваются пометки notes, вставленные с помощью #=
* Добавлена справка по использованию журнала

Новые параметры:
show_notes - нужно ли показывать заметки "notes"
> note_width - ширина заметок "notes"
mywi_server - IP-адрес сервера mywi
mywi_port - порт сервера mywi
stat_inactivity_interval - при подсчёте времени работы с терминалом,
интервалы превышающие какую длительность не должны учитываться, сек
author devi
date Fri Nov 11 21:29:49 2005 +0200 (2005-11-11)
parents b3f5f5560802
children 4d252e7dd478
line diff
     1.1 --- a/l3-frontend	Mon Nov 07 13:28:15 2005 +0200
     1.2 +++ b/l3-frontend	Fri Nov 11 21:29:49 2005 +0200
     1.3 @@ -1,9 +1,15 @@
     1.4  #!/usr/bin/perl -w
     1.5  
     1.6 +use IO::Socket;
     1.7  use lib '.';
     1.8  use l3config;
     1.9 +use locale;
    1.10  
    1.11  our @Command_Lines;
    1.12 +our @Command_Lines_Index;
    1.13 +our %Commands_Description;
    1.14 +our %Args_Description;
    1.15 +our $Mywi_Socket;
    1.16  
    1.17  # vvv Инициализация переменных выполняется процедурой init_variables
    1.18  our @Day_Name;
    1.19 @@ -13,12 +19,18 @@
    1.20  our %Elements_Visibility;
    1.21  # ^^^
    1.22  
    1.23 +our %Stat;
    1.24 +our %CommandsFDistribution;	# Сколько раз в журнале встречается какая команда
    1.25 +
    1.26  sub search_buy;
    1.27  sub make_comment;
    1.28  sub load_command_lines_from_xml;
    1.29  sub print_command_lines;
    1.30 +sub sort_command_lines;
    1.31 +sub process_command_lines;
    1.32  sub init_variables;
    1.33  sub main;
    1.34 +sub collapse_list($);
    1.35  
    1.36  main();
    1.37  
    1.38 @@ -29,8 +41,12 @@
    1.39  	init_variables();
    1.40  	init_config();
    1.41  
    1.42 +	open_mywi_socket;
    1.43  	load_command_lines_from_xml($Config{"backend_datafile"});
    1.44 +	sort_command_lines;
    1.45 +	process_command_lines;
    1.46  	print_command_lines($Config{"output"});
    1.47 +	close_mywi_socket;
    1.48  }
    1.49  
    1.50  
    1.51 @@ -44,62 +60,139 @@
    1.52  				$Search_Machines{$sm}->{"icon"}."' border='0'/></a>";
    1.53  }
    1.54  
    1.55 +sub extract_from_cline
    1.56 +# Разобрать командную строку $_[1] и возвратить хэш, содержащий 
    1.57 +# номер первого появление команды в строке:
    1.58 +# 	команда => первая позиция
    1.59 +{
    1.60 +	my $what = $_[0];
    1.61 +	my $cline = $_[1];
    1.62 +	my @lists = split /\;/, $cline;
    1.63 +	
    1.64 +	
    1.65 +	my @commands = ();
    1.66 +	for my $list (@lists) {
    1.67 +		push @commands, split /\|/, $list;
    1.68 +	}
    1.69 +
    1.70 +	my %commands;
    1.71 +	my %args;
    1.72 +	my $i=0;
    1.73 +	for my $command (@commands) {
    1.74 +		$command =~ s@^\s*\S+/@@;
    1.75 +		$command =~ /\s*(\S+)\s*(.*)/;
    1.76 +		if ($1 && $1 eq "sudo" ) {
    1.77 +			$commands{"$1"}=$i++;
    1.78 +			$command =~ s/\s*sudo\s+//;
    1.79 +		}
    1.80 +		$command =~ s@^\s*\S+/@@;
    1.81 +		$command =~ /\s*(\S+)\s*(.*)/;
    1.82 +		if ($1 && !defined $commands{"$1"}) {
    1.83 +				$commands{"$1"}=$i++;
    1.84 +		};	
    1.85 +		if ($2) {
    1.86 +			my $args = $2;
    1.87 +			my @args = split (/\s+/, $args);
    1.88 +			for my $a (@args) {
    1.89 +				$args{"$a"}=$i++
    1.90 +					if !defined $args{"$a"};
    1.91 +			};	
    1.92 +
    1.93 +				
    1.94 +		}
    1.95 +	}
    1.96 +
    1.97 +	if ($what eq "commands") {
    1.98 +		return \%commands;
    1.99 +	} else {
   1.100 +		return \%args;
   1.101 +	}
   1.102 +	
   1.103 +}
   1.104 +
   1.105 +sub open_mywi_socket
   1.106 +{
   1.107 +	$Mywi_Socket = IO::Socket::INET->new(
   1.108 +				PeerAddr => $Config{mywi_server},
   1.109 +				PeerPort => $Config{mywi_port},
   1.110 +				Proto	 => "tcp",
   1.111 +				Type	 => SOCK_STREAM);
   1.112 +}
   1.113 +
   1.114 +sub close_mywi_socket
   1.115 +{
   1.116 +	close ($Mywi_Socket);
   1.117 +}
   1.118 +
   1.119 +
   1.120 +sub mywi_client
   1.121 +{
   1.122 +	my $query = $_[0];
   1.123 +	my $mywi;
   1.124 +
   1.125 +	open_mywi_socket;
   1.126 +	if ($Mywi_Socket) {
   1.127 +		local $| = 1;
   1.128 +		local $/ = "";
   1.129 +		print $Mywi_Socket $query."\n";
   1.130 +		$mywi = <$Mywi_Socket>;
   1.131 +		$mywi = "" if $mywi =~ /nothing app/;
   1.132 +	}
   1.133 +	close_mywi_socket;
   1.134 +	return $mywi;
   1.135 +}
   1.136 +
   1.137  sub make_comment
   1.138  {
   1.139 -	my $commands = $_[0];
   1.140 -	my $files = $_[1];
   1.141 -	chomp $commands;
   1.142 -	chomp $files;
   1.143 -	return if (!$commands && !$files);
   1.144 +	my $cline = $_[0];
   1.145 +	#my $files = $_[1];
   1.146  
   1.147 -	my $comment=""; 
   1.148 +	my @comments=(); 
   1.149 +	my @commands = keys %{extract_from_cline("commands", $cline)};
   1.150 +	my @args = keys %{extract_from_cline("args", $cline)};
   1.151 +	return if (!@commands && !@args);
   1.152 +	#return "commands=".join(" ",@commands)."; files=".join(" ",@files);
   1.153  
   1.154  	# Commands
   1.155 -	for my $command (split /\s+/,$commands) {
   1.156 +	for my $command (@commands) {
   1.157  		$command =~ s/'//g;
   1.158 -		my $description="";
   1.159 -		eval { $description=`/home/devi/bin/mywi-client '$command'`; } ;
   1.160 -		$description = join ("<br>\n", grep(/\([18]\)/, split(/\n/, $description)));
   1.161 -		$description =~ s/.*?-//;
   1.162 -		next if $description =~ /^\s*$/; 
   1.163 -		
   1.164 -		my $query=$command." ".$Config{"keywords"};
   1.165 -		$query =~ s/\ /+/g;
   1.166 -		my $search= 	search_by("opennet",$query).
   1.167 -				search_by("local",$command).
   1.168 -				search_by("google",$query);
   1.169 +		$CommandsFDistribution{$command}++;
   1.170 +		if (!$Commands_Description{$command}) {
   1.171 +			my $mywi;
   1.172 +			$mywi = mywi_client ($command);
   1.173 +			$mywi = join ("\n", grep(/\([18]\)/, split(/\n/, $mywi)));
   1.174 +			$mywi =~ s/\s+/ /;
   1.175 +			if ($mywi !~ /^\s*$/) {
   1.176 +				$Commands_Description{$command} = $mywi;
   1.177 +			}
   1.178 +			else {
   1.179 +				next;
   1.180 +			}
   1.181 +		}
   1.182  
   1.183 -		$comment .=     "<tr><td class='note_title'>$command</td>".
   1.184 -				"<td class='note_search'>$search</td>".
   1.185 -				"</tr><tr><td width='100%' colspan='2' class='note_text'>".
   1.186 -				"$description</td></tr><tr/>";
   1.187 +		push @comments, $Commands_Description{$command};
   1.188  	}
   1.189 +	return join("&#10;\n", @comments);
   1.190  	
   1.191  	# Files
   1.192 -	for my $file (split /\s+/,$files) {
   1.193 -		$file =~ s@.*/@@;
   1.194 -		$file =~ s/'//g;
   1.195 -		next if $file =~ /^\s*$/;
   1.196 -		next if $file =~ /^-/;
   1.197 -		
   1.198 -		my $description=`mywi '$file'`;
   1.199 -		$description = join ("<br>\n", grep(/\(5\)/, split(/\n/, $description)));
   1.200 -		next if $description =~ /^\s*$/; 
   1.201 +	for my $arg (@args) {
   1.202 +		$arg =~ s/'//g;
   1.203 +		if (!$Args_Description{$arg}) {
   1.204 +			my $mywi;
   1.205 +			$mywi = mywi_client ($arg);
   1.206 +			$mywi = join ("\n", grep(/\([5]\)/, split(/\n/, $mywi)));
   1.207 +			$mywi =~ s/\s+/ /;
   1.208 +			if ($mywi !~ /^\s*$/) {
   1.209 +				$Args_Description{$arg} = $mywi;
   1.210 +			}
   1.211 +			else {
   1.212 +				next;
   1.213 +			}
   1.214 +		}
   1.215  
   1.216 -		my $query=$file." ".$Config{"files_keywords"};
   1.217 -		$query =~ s/\ /+/g;
   1.218 -		my $search= 	search_by("opennet",$query).
   1.219 -				search_by("local",$file).
   1.220 -				search_by("google",$query);
   1.221 -
   1.222 -		$comment .=     "<tr><td class='note_title'>$file</td>".
   1.223 -				"<td class='note_search'>$search</td>".
   1.224 -				"</tr><tr><td width='100%' colspan='2' class='note_text'>".
   1.225 -				"$description</td></tr><tr/>";
   1.226 +		push @comments, $Args_Description{$arg};
   1.227  	}
   1.228  
   1.229 -
   1.230 -	return $comment;
   1.231  }
   1.232  
   1.233  =cut
   1.234 @@ -130,6 +223,123 @@
   1.235  	}
   1.236  }
   1.237  
   1.238 +sub sort_command_lines
   1.239 +{
   1.240 +	# Sort Command_Lines
   1.241 +	# Write Command_Lines to Command_Lines_Index
   1.242 +
   1.243 +	my @index;
   1.244 +	for (my $i=0;$i<=$#Command_Lines;$i++) {
   1.245 +		$index[$i]=$i;
   1.246 +	}
   1.247 +
   1.248 +	@Command_Lines_Index = sort {
   1.249 +		$Command_Lines[$index[$a]]->{"time"} <=> $Command_Lines[$index[$b]]->{"time"}
   1.250 +	} @index;
   1.251 +
   1.252 +}
   1.253 +
   1.254 +sub process_command_lines
   1.255 +{
   1.256 +	for my $i (@Command_Lines_Index) {
   1.257 +
   1.258 +		my $cl = \$Command_Lines[$i];
   1.259 +		@{${$cl}->{"new_commands"}} =();
   1.260 +		@{${$cl}->{"new_files"}} =();
   1.261 +		$$cl->{"class"} = ""; 
   1.262 +
   1.263 +		if ($$cl->{"err"}) {
   1.264 +			$$cl->{"class"}="wrong";
   1.265 +			$$cl->{"class"}="interrupted"
   1.266 +				if ($$cl->{"err"} eq 130);
   1.267 +		}	
   1.268 +		if (!$$cl->{"euid"}) {
   1.269 +			$$cl->{"class"}.="_root";
   1.270 +		}
   1.271 +		
   1.272 +#tab#		my @tab_words=split /\s+/, $$cl->{"output"};
   1.273 +#tab#		my $last_word= $$cl->{"cline"} =~ /(\S*)$/;
   1.274 +#tab#		$last_word =~ s@.*/@@;
   1.275 +#tab#		my $this_is_tab=1;
   1.276 +#tab#
   1.277 +#tab#		if ($last_word && @tab_words >2) {
   1.278 +#tab#			for my $tab_words (@tab_words) {
   1.279 +#tab#				if ($tab_words !~ /^$last_word/) {
   1.280 +#tab#					$this_is_tab=0;
   1.281 +#tab#					last;
   1.282 +#tab#				}
   1.283 +#tab#			}
   1.284 +#tab#		}	
   1.285 +#tab#		$$cl->{"class"}="tab" if $this_is_tab;
   1.286 +		
   1.287 +
   1.288 +#		if ( !$$cl->{"err"}) {
   1.289 +#			# Command does not contain mistakes
   1.290 +#			
   1.291 +#			my %commands = extract_from_cline("commands", ${$cl}->{"cline"});
   1.292 +#			my %files = extract_from_cline("files", ${$cl}->{"cline"});
   1.293 +#
   1.294 +#			# Searching for new commands only
   1.295 +#			for my $command (keys  %commands) {
   1.296 +#				if (!defined $Commands_Stat{$command}) {
   1.297 +#					push @{$$cl->{new_commands}}, $command;
   1.298 +#				}	
   1.299 +#				$Commands_Stat{$command}++;
   1.300 +#			}
   1.301 +#			
   1.302 +#			for my $file (keys  %files) {
   1.303 +#				if (!defined $Files_Stat{$file}) {
   1.304 +#					push @{$$cl->{new_files}}, $file;
   1.305 +#				}	
   1.306 +#				$Files_Stat{$file}++;
   1.307 +#			}
   1.308 +#		}	
   1.309 +
   1.310 +		if ($$cl->{cline}=~ m@cat[^#]*#([\^=v])\s*(.*)@) {
   1.311 +			if ($1 eq "=") {
   1.312 +				$$cl->{"class"} = "note";
   1.313 +				$$cl->{"note"} = $$cl->{"output"};
   1.314 +				$$cl->{"note_title"} = $2;
   1.315 +			}
   1.316 +			else {
   1.317 +				my $j = $i;
   1.318 +				if ($1 eq "^") {
   1.319 +					$j--;
   1.320 +					$j-- while ($j >=0  && $Command_Lines[$j]->{tty} ne $$cl->{tty} || !$Command_Lines[$j]);
   1.321 +				}
   1.322 +				elsif ($1 eq "v") {
   1.323 +					$j++;
   1.324 +					$j++ while ($j <= @Command_Lines  && $Command_Lines[$j]->{tty} ne $$cl->{tty} || !$Command_Lines[$j]);
   1.325 +				}
   1.326 +				$Command_Lines[$j]->{note_title}="$2";
   1.327 +				$Command_Lines[$j]->{note}=$$cl->{output};
   1.328 +				$$cl=0;
   1.329 +			}
   1.330 +		}
   1.331 +		elsif ($$cl->{cline}=~ /#([\^=v])(.*)/) {
   1.332 +			if ($1 eq "=") {
   1.333 +				$$cl->{"class"} = "note";
   1.334 +				$$cl->{"note"} = $2;
   1.335 +			}
   1.336 +			else {
   1.337 +				my $j=$i;
   1.338 +				if ($1 eq "^") {
   1.339 +					$j--;
   1.340 +					$j-- while ($j >=0  && (!$Command_Lines[$j] || $Command_Lines[$j]->{tty} ne $$cl->{tty}));
   1.341 +				}
   1.342 +				elsif ($1 eq "v") {
   1.343 +					$j++;
   1.344 +					$j++ while ($j <= @Command_Lines  && $Command_Lines[$j]->{tty} ne $$cl->{tty} || !$Command_Lines[$j]);
   1.345 +				}
   1.346 +				$Command_Lines[$j]->{note}="$2";
   1.347 +				$$cl=0;
   1.348 +			}
   1.349 +		}
   1.350 +	}	
   1.351 +
   1.352 +}
   1.353 +
   1.354 +
   1.355  =cut
   1.356  Процедура print_command_lines выводит HTML-представление
   1.357  разобранного lab-скрипта. 
   1.358 @@ -152,7 +362,8 @@
   1.359  	# Результат выполнения процедуры равен 
   1.360  	# join("", @Result{header,body,stat,help,about,footer})
   1.361  	my %Result;
   1.362 -	my $toc ="";  # Хранит оглавление по дням
   1.363 +	my @toc;  # Хранит оглавление
   1.364 +	my $note_number=0;
   1.365  
   1.366  	$Result{"body"} = "<table width='100%'>\n";
   1.367  	
   1.368 @@ -161,7 +372,12 @@
   1.369  	my $last_day="";
   1.370  	my $in_range=0;
   1.371  
   1.372 -	for my $cl (@Command_Lines) {
   1.373 +	my $i=0;
   1.374 +	for my $k (@Command_Lines_Index) {
   1.375 +
   1.376 +		my $cl=$Command_Lines[$Command_Lines_Index[$i++]];
   1.377 +		
   1.378 +		next unless $cl;
   1.379  
   1.380  		if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) {
   1.381  			$in_range=1;
   1.382 @@ -182,6 +398,20 @@
   1.383  		#my @new_commands=@{$cl->{"new_commands"}};
   1.384  		#my @new_files=@{$cl->{"new_files"}};
   1.385  
   1.386 +		if ($cl->{class} eq "note") {
   1.387 +			my $note = $cl->{note};
   1.388 +			$note = join ("\n", map ("<p>$_</p>", split (/-\n/, $note)));
   1.389 +			$Result{"body"} .= "<tr><td colspan='6'>";
   1.390 +			$Result{"body"} .= "<h4 id='note$note_number'>".$cl->{note_title}."</h4>" if $cl->{note_title};
   1.391 +			$Result{"body"} .= "".$note."<p/><p/></td></td>";
   1.392 +
   1.393 +			if ($cl->{note_title}) {
   1.394 +				push @{$toc[@toc]},"<a href='#note$note_number'>".$cl->{note_title}."</a>";
   1.395 +				$note_number++;
   1.396 +			}
   1.397 +			next;
   1.398 +		}
   1.399 +
   1.400  		my $cl_class="cline";
   1.401  		my $out_class="output";
   1.402  		if ($cl->{"class"}) {
   1.403 @@ -233,6 +463,14 @@
   1.404  		# <command>
   1.405  
   1.406  		my ($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime($cl->{time});
   1.407 +		$Stat{FirstCommand} = $cl->{time} unless $Stat{FirstCommand};
   1.408 +		$Stat{LastCommand} = 0 unless defined $Stat{LastCommand};	
   1.409 +		$Stat{TotalTime} += $cl->{time} - $Stat{LastCommand}
   1.410 +			if $cl->{time} - $Stat{LastCommand} < $Config{stat_inactivity_interval};
   1.411 +		$Stat{LastCommand} = $cl->{time};
   1.412 +		$Stat{TotalCommands} = 0 unless $Stat{TotalCommands};
   1.413 +		$Stat{TotalCommands}++;
   1.414 +
   1.415  		# Добавляем спереди 0 для удобочитаемости
   1.416  		$min = "0".$min if $min =~ /^.$/;
   1.417  		$hour = "0".$hour if $hour =~ /^.$/;
   1.418 @@ -241,6 +479,8 @@
   1.419  		$class=$cl->{"out_class"};
   1.420  		$class =~ s/output$//;
   1.421  
   1.422 +		$Stat{ErrorCommands}++
   1.423 +			if $class =~ /wrong/;
   1.424  		
   1.425  		$Result{"body"} .= "<tr class='command'>\n";
   1.426  						
   1.427 @@ -249,7 +489,7 @@
   1.428  		if ( $last_day ne $day) {
   1.429  			#$Result{"body"} .= "<td colspan='6'><p></p><h3>День ",$day,"</h4></td></tr><tr>";
   1.430  			$Result{"body"} .= "<td colspan='6'><p></p><h3 id='day$day'>".$Day_Name[$wday]."</h4></td></tr><tr>";
   1.431 -			$toc .= "<li><a href='#day$day'>".$Day_Name[$wday]."</a></li>\n";
   1.432 +			push @toc, "<a href='#day$day'>".$Day_Name[$wday]."</a>\n";
   1.433  			$last_day=$day;
   1.434  		}
   1.435  
   1.436 @@ -271,9 +511,12 @@
   1.437  		# COMMAND
   1.438  		$Result{"body"} .= "<td class='script'>\n";
   1.439  		$Result{"body"} .= "<pre class='${class}cline'>\n";
   1.440 -		my $cline = $cl->{"cline"};
   1.441 +		my $cline = $cl->{"prompt"}.$cl->{"cline"};
   1.442  		$cline =~ s/\n//;
   1.443 -		$Result{"body"} .= $cl->{"prompt"}.$cl->{"cline"};
   1.444 +		my $hint = make_comment($cl->{"cline"});
   1.445 +	#	join(" ",@new_commands), join (" ",@new_files));
   1.446 +		$cline = "<div title='$hint'>$cline</div>" if $hint;
   1.447 +		$Result{"body"} .= $cline;
   1.448  		$Result{"body"} .= "</pre>\n";
   1.449  
   1.450  		my $last_command = $cl->{"last_command"};
   1.451 @@ -294,10 +537,22 @@
   1.452  			$Result{"body"} .= $cl->{"diff"};
   1.453  			$Result{"body"} .= "</pre></td></tr></table>";
   1.454  		}
   1.455 +		
   1.456 +		#NOTES
   1.457 +		if ( $Config{"show_notes"} =~ /^y/i && $cl->{"note"}) {
   1.458 +			my $note=$cl->{"note"};
   1.459 +			$note =~ s/\n/<br\/>\n/msg;
   1.460 +		#	Ширину пока не используем
   1.461 +		#	$Result{"body"} .= "<table width='$Config{note_width}' class='note'>";
   1.462 +			$Result{"body"} .= "<table class='note'>";
   1.463 +			$Result{"body"} .= "<tr><td class='note_title'>".$cl->{note_title}."</td></tr>" if $cl->{note_title};
   1.464 +			$Result{"body"} .= "<tr><td width='100%' class='note_text'>".$note."</td></tr>";
   1.465 +			$Result{"body"} .= "</table>\n";
   1.466 +		}
   1.467  
   1.468  		# COMMENT
   1.469  		if ( $Config{"show_comments"} =~ /^y/i) {
   1.470 -			my $comment = make_comment(join(" ",@new_commands), join (" ",@new_files));
   1.471 +			my $comment = make_comment($cl->{"cline"});
   1.472  			if ($comment) {
   1.473  				$Result{"body"} .= "<table width='$Config{comment_width}'>".
   1.474  						"<tr><td width='5'/><td>";
   1.475 @@ -315,18 +570,100 @@
   1.476  
   1.477  	$Result{"body"} .= "</table>\n";
   1.478  
   1.479 -	$Result{"stat"} = "<hr/>";
   1.480 +	#$Result{"stat"} = "<hr/>";
   1.481 +
   1.482 +	%StatNames = (
   1.483 +		FirstCommand => "Время первой команды журнала",
   1.484 +		LastCommand => "Время последней команды журнала",
   1.485 +		TotalCommands => "Количество командных строк в журнале",
   1.486 +		ErrorsPercentage => "Процент команд с кодом ненулевым кодом завершения, %",
   1.487 +		TotalTime => "Суммарное время работы с терминалом <sup><font size='-2'>*</font></sup>, час",
   1.488 +		CommandsPerTime => "Количество командных строк в единицу времени, команда/мин",
   1.489 +		CommandsFDistribution => "Частота использования команд",
   1.490 +		CommandsFDistribution => "Частота использования команд",
   1.491 +		RareCommands	=> "Частота использования этих команд < 0.5%",
   1.492 +	);
   1.493 +	@StatOrder = (
   1.494 +		FirstCommand,
   1.495 +		LastCommand,
   1.496 +		TotalCommands,
   1.497 +		ErrorsPercentage,
   1.498 +		TotalTime,
   1.499 +		CommandsPerTime,
   1.500 +		CommandsFDistribution,
   1.501 +		RareCommands,
   1.502 +	);
   1.503 +
   1.504 +	# Подготовка статистики к выводу
   1.505 +	# Некоторые значения пересчитываются!
   1.506 +	# Дальше их лучше уже не использовать!!!
   1.507 +
   1.508 +	my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($Stat{FirstCommand});
   1.509 +	$Stat{FirstCommand} = sprintf "%02i:%02i:%02i %04i-%2i-%2i", $hour, $min, $sec,  $year+1900, $mon+1, $mday;
   1.510 +	($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($Stat{LastCommand});
   1.511 +	$Stat{LastCommand} = sprintf "%02i:%02i:%02i %04i-%2i-%2i", $hour, $min, $sec,  $year+1900, $mon+1, $mday;
   1.512 +	$Stat{ErrorsPercentage} = sprintf "%5.2f", $Stat{ErrorCommands}*100/$Stat{TotalCommands}	
   1.513 +		if $Stat{TotalCommands};
   1.514 +	$Stat{CommandsPerTime} = sprintf "%5.2f", $Stat{TotalCommands}*60/$Stat{TotalTime}
   1.515 +		if $Stat{TotalTime};
   1.516 +	$Stat{TotalTime} = sprintf "%5.2f", $Stat{TotalTime}/60/24;
   1.517 +
   1.518 +	my $total_commands=0;
   1.519 +	for $command (keys %CommandsFDistribution){
   1.520 +		$total_commands += $CommandsFDistribution{$command};
   1.521 +	}
   1.522 +	if ($total_commands) {
   1.523 +		for $command (reverse sort {$CommandsFDistribution{$a} <=> $CommandsFDistribution{$b}} keys %CommandsFDistribution){
   1.524 +			my $percentage = sprintf "%5.2f",$CommandsFDistribution{$command}*100/$total_commands;
   1.525 +			if ($percentage < 0.5) {
   1.526 +				my $hint = make_comment($command);
   1.527 +				my $command_html = "$command";
   1.528 +				$command_html = "<span title='$hint' class='hint'>$command_html</span>" if $hint;
   1.529 +				my $command_html = "<tt>$command_html</tt>";
   1.530 +				$Stat{RareCommands} .= $command_html."<sub><font size='-2'>".$CommandsFDistribution{$command}."</font></sub> , ";
   1.531 +			}
   1.532 +			else {
   1.533 +				my $hint = make_comment($command);
   1.534 +				my $command_html = "$command";
   1.535 +				$command_html = "<span title='$hint' class='hint'>$command_html</span>" if $hint;
   1.536 +				my $command_html = "<tt>$command_html</tt>";
   1.537 +				$percentage = sprintf "%5.2f",$percentage;
   1.538 +				$Stat{CommandsFDistribution} .= "<tr><td>".$command_html."</td><td>".$CommandsFDistribution{$command}."</td>".
   1.539 +					"<td>|".("="x int($CommandsFDistribution{$command}*100/$total_commands))."| $percentage%</td></tr>";
   1.540 +			}
   1.541 +		}
   1.542 +		$Stat{CommandsFDistribution} = "<table>".$Stat{CommandsFDistribution}."</table>";
   1.543 +		$Stat{RareCommands} =~ s/, $//;
   1.544 +	}
   1.545 +
   1.546  	$Result{"stat"} .= "<h2 id='stat'>Статистика</h2>";
   1.547 -	$Result{"stat"} .= "Статистическая информация о журнале<br/>";
   1.548 -	$Result{"help"} .= "<hr/>";
   1.549 +	$Result{"stat"} .= "<table>";
   1.550 +	for my $stat (@StatOrder) {
   1.551 +	$Result{"stat"} .= "<tr valign='top'><td width='300'>".$StatNames{"$stat"}."</td><td>".$Stat{"$stat"}."</td></tr>";
   1.552 +	}
   1.553 +	
   1.554 +	$Result{"stat"} .= "</table>";
   1.555 +	$Result{"stat"} .= "<font size='-2'>____<br/>*) Интервалы неактивности длительностью ".($Config{stat_inactivity_interval}/60)." минут и более не учитываются</font></br>";
   1.556 +
   1.557 +	#$Result{"help"} .= "<hr/>";
   1.558  	$Result{"help"} .= "<h2 id='help'>Справка</h2>";
   1.559  	$Result{"help"} .= "$Html_Help<br/>";
   1.560 -	$Result{"about"} .= "<hr/>";
   1.561 +	#$Result{"about"} .= "<hr/>";
   1.562  	$Result{"about"} .= "<h2 id='about'>О программе</h2>";
   1.563  	$Result{"about"} .= "$Html_About";
   1.564  	$Result{"footer"} .= "</body>\n";
   1.565  	$Result{"footer"} .= "</html>\n";
   1.566  
   1.567 +	$Result{"title"} = "Журнал лабораторных работ";
   1.568 +	$Result{"title"}.= " -- ".$course_student if $course_student;
   1.569 +	if ($course_date) {
   1.570 +		$Result{"title"}.= " -- ".$course_date; 
   1.571 +		$Result{"title"}.= "/".$course_code if $course_code;
   1.572 +	}
   1.573 +	else {
   1.574 +		$Result{"title"}.= " -- ".$course_code if $course_code;
   1.575 +	}
   1.576 +
   1.577  	# Заголовок генерируется позже всего
   1.578  	# Тогда, когда известно уже, что должно быть написано в 
   1.579  	# оглавлении
   1.580 @@ -335,21 +672,27 @@
   1.581  	<head>
   1.582  	<meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
   1.583  	<link rel='stylesheet' href='$Config{frontend_css}' type='text/css'/>
   1.584 +	<title>$Result{title}</title>
   1.585  	</head>
   1.586  	<body>
   1.587  	<script>
   1.588  	$Html_JavaScript
   1.589  	</script>
   1.590 -	<h2>Журнал лабораторных работ</h2>
   1.591 +	<h1>Журнал лабораторных работ</h1>
   1.592  
   1.593 -	<p>
   1.594 -	Выполнил $course_student<br/>
   1.595 -	Проверил $course_trainer <br/>
   1.596 -	Курс $course_name ($course_code),
   1.597 -	$course_date<br/>
   1.598 -	Учебный центр $course_center <br/>
   1.599 -	</p>
   1.600 +HEADER
   1.601 +	$Result{"header"} .= "<p>" if $course_student || $course_trainer || $course_name || $course_code || $course_date || $course_center;
   1.602 +	$Result{"header"} .= "Выполнил $course_student<br/>" if $course_student;
   1.603 +	$Result{"header"} .= "Проверил $course_trainer <br/>" if $course_trainer;
   1.604 +	$Result{"header"} .= "Курс " if $course_name || $course_code || $course_date;
   1.605 +	$Result{"header"} .= "$course_name " if $course_name;
   1.606 +	$Result{"header"} .= "($course_code)" if $course_code;
   1.607 +	$Result{"header"} .= ", $course_date<br/>" if $course_date;
   1.608 +	$Result{"header"} .= "Учебный центр $course_center <br/>" if $course_center;
   1.609 +	$Result{"header"} .= "</p>" if $course_student || $course_trainer || $course_name || $course_code || $course_date || $course_center;
   1.610  
   1.611 +	my $toc = collapse_list (\@toc);
   1.612 +	$Result{"header"} .= <<HEADER;
   1.613  	<ul>
   1.614  		<li><a href='#log'>Журнал</a></li>
   1.615  		<ul>$toc</ul>
   1.616 @@ -360,7 +703,7 @@
   1.617  
   1.618  	<h2 id="log">Журнал</h2>
   1.619  HEADER
   1.620 -	$Result{"header"} .= "<table class='visibility_form'><tr><td><form>\n";
   1.621 +	$Result{"header"} .= "<table id='visibility_form' class='visibility_form'><tr><td><form>\n";
   1.622  	for my $element (keys %Elements_Visibility)
   1.623  	{
   1.624  		my @e = split /\s+/, $element;
   1.625 @@ -385,13 +728,205 @@
   1.626  
   1.627  
   1.628  
   1.629 +sub collapse_list($)
   1.630 +{
   1.631 +	my $res = "";
   1.632 +	for my $elem (@{$_[0]}) {
   1.633 +		if (ref $elem eq "ARRAY") {
   1.634 +			$res .= "<ul>".collapse_list($elem)."</ul>";
   1.635 +		}
   1.636 +		else
   1.637 +		{
   1.638 +			$res .= "<li>".$elem."</li>";
   1.639 +		}
   1.640 +	}
   1.641 +	return $res;
   1.642 +}
   1.643 +
   1.644  
   1.645  
   1.646  
   1.647  sub init_variables
   1.648  {
   1.649  $Html_Help = <<HELP;
   1.650 -	Справка по использованию журнала
   1.651 +	Для того чтобы использовать LiLaLo, не нужно знать ничего особенного:
   1.652 +	всё происходит само собой.
   1.653 +	Однако, чтобы ведение и последующее использование журналов
   1.654 +	было как можно более эффективным, желательно иметь в виду следующее:
   1.655 +	<ul>
   1.656 +	<li><p> 
   1.657 +	В журнал автоматически попадают все команды, данные в любом терминале системы.
   1.658 +	</p></li>
   1.659 +	<li><p>
   1.660 +	Для того чтобы убедиться, что журнал на текущем терминале ведётся, 
   1.661 +	и команды записываются, дайте команду w.
   1.662 +	В поле WHAT, соответствующем текущему терминалу, 
   1.663 +	должна быть указана программа script.
   1.664 +	</p></li>
   1.665 +	<li><p>
   1.666 +	Команды, код завершения которых отличен от нуля, выделяются цветом.
   1.667 +	Если код завершения команды равен нулю, 
   1.668 +	команда была выполнена без ошибок.
   1.669 +<table>
   1.670 +<tr class='command'>
   1.671 +<td class='script'>
   1.672 +<pre class='wrong_cline'>
   1.673 +\$ l s-l</pre>
   1.674 +<pre class='wrong_output'>bash: l: command not found
   1.675 +</pre>
   1.676 +</td>
   1.677 +</tr>
   1.678 +</table>
   1.679 +<br/>
   1.680 +	</p></li>
   1.681 +	<li><p>
   1.682 +	Команды, ход выполнения которых был прерван пользователем, выделяются цветом.
   1.683 +<table>
   1.684 +<tr class='command'>
   1.685 +<td class='script'>
   1.686 +<pre class='interrupted_cline'>
   1.687 +\$ find / -name abc</pre>
   1.688 +<pre class='interrupted_output'>find: /home/devi-orig/.gnome2: Keine Berechtigung
   1.689 +find: /home/devi-orig/.gnome2_private: Keine Berechtigung
   1.690 +find: /home/devi-orig/.nautilus/metafiles: Keine Berechtigung
   1.691 +find: /home/devi-orig/.metacity: Keine Berechtigung
   1.692 +find: /home/devi-orig/.inkscape: Keine Berechtigung
   1.693 +^C
   1.694 +</pre>
   1.695 +</td>
   1.696 +</tr>
   1.697 +</table>
   1.698 +<br/>
   1.699 +	</p></li>
   1.700 +	<li><p>
   1.701 +	Команды, выполненные с привилегиями суперпользователя,
   1.702 +	выделяются слева красной чертой.
   1.703 +<table>
   1.704 +<tr class='command'>
   1.705 +<td class='script'>
   1.706 +<pre class='_root_cline'>
   1.707 +# id</pre>
   1.708 +<pre class='_root_output'>
   1.709 +uid=0(root) gid=0(root) Gruppen=0(root)
   1.710 +</pre>
   1.711 +</td>
   1.712 +</tr>
   1.713 +</table>
   1.714 +	<br/>
   1.715 +	</p></li>
   1.716 +	<li><p>
   1.717 +	Изменения, внесённые в текстовый файл с помощью редактора, 
   1.718 +	запоминаются и показываются в журнале в формате ed.
   1.719 +	Строки, начинающиеся символом "&lt;", удалены, а строки,
   1.720 +	начинающиеся символом "&gt;" -- добавлены.
   1.721 +<table>
   1.722 +<tr class='command'>
   1.723 +<td class='script'>
   1.724 +<pre class='cline'>
   1.725 +\$ vi ~/.bashrc</pre>
   1.726 +<table><tr><td width='5'/><td class='diff'><pre>2a3,5
   1.727 +&gt; 	if [ -f /usr/local/etc/bash_completion ]; then
   1.728 +&gt;         . /usr/local/etc/bash_completion
   1.729 +&gt;     	fi
   1.730 +</pre></td></tr></table></td>
   1.731 +</tr>
   1.732 +</table>
   1.733 +	<br/>
   1.734 +	</p></li>
   1.735 +	<li><p>
   1.736 +	Для того чтобы получить краткую справочную информацию о команде, 
   1.737 +	нужно подвести к ней мышь. Во всплывающей подсказке появится краткое
   1.738 +	описание команды.
   1.739 +	</p></li>
   1.740 +	<li><p>
   1.741 +	Время ввода команды, показанное в журнале, соответствует времени 
   1.742 +	<i>начала ввода командной строки</i>, которое равно тому моменту, 
   1.743 +	когда на терминале появилось приглашение интерпретатора
   1.744 +	</p></li>
   1.745 +	<li><p>
   1.746 +	Имя терминала, на котором была введена команда, показано в специальном блоке.
   1.747 +	Этот блок показывается только в том случае, если терминал
   1.748 +	текущей команды отличается от терминала предыдущей.
   1.749 +	</p></li>
   1.750 +	<li><p>
   1.751 +	Вывод не интересующих вас в настоящий момент элементов журнала,
   1.752 +	таких как время, имя терминала и других, можно отключить.
   1.753 +	Для этого нужно воспользоваться <a href='#visibility_form'>формой управления журналом</a>
   1.754 +	вверху страницы.
   1.755 +	</p></li>
   1.756 +	<li><p>
   1.757 +	Небольшие комментарии к командам можно вставлять прямо из командной строки.
   1.758 +	Комментарий вводится прямо в командную строку, после символов #^ или #v.
   1.759 +	Символы ^ и v показывают направление выбора команды, к которой относится комментарий:
   1.760 +	^ - к предыдущей, v - к следующей.
   1.761 +	Например, если в командной строке было введено:
   1.762 +<pre class='cline'>
   1.763 +\$ whoami
   1.764 +</pre>
   1.765 +<pre class='output'>
   1.766 +user
   1.767 +</pre>
   1.768 +<pre class='cline'>
   1.769 +\$ #^ Интересно, кто я?
   1.770 +</pre>
   1.771 +	в журнале это будет выглядеть так:
   1.772 +	
   1.773 +<pre class='cline'>
   1.774 +\$ whoami
   1.775 +</pre>
   1.776 +<pre class='output'>
   1.777 +user
   1.778 +</pre>
   1.779 +<table class='note'><tr><td width='100%' class='note_text'>
   1.780 +<tr> <td> Интересно, кто я?<br/> </td></tr></table> 
   1.781 +	</p></li>
   1.782 +	<li><p>
   1.783 +	Если комментарий содержит несколько строк,
   1.784 +	его можно вставить в журнал следующим образом:
   1.785 +<pre class='cline'>
   1.786 +\$ whoami
   1.787 +</pre>
   1.788 +<pre class='output'>
   1.789 +user
   1.790 +</pre>
   1.791 +<pre class='cline'>
   1.792 +\$ cat > /dev/null #^ Интересно, кто я?
   1.793 +</pre>
   1.794 +<pre class='output'>
   1.795 +Программа whoami выводит имя пользователя, под которым 
   1.796 +мы зарегистрировались в системе.
   1.797 +-
   1.798 +Она не может ответить на вопрос о нашем назначении 
   1.799 +в этом мире.
   1.800 +</pre>
   1.801 +	В журнале это будет выглядеть так:
   1.802 +<table>
   1.803 +<tr class='command'>
   1.804 +<td class='script'>
   1.805 +<pre class='cline'>
   1.806 +\$ whoami</pre>
   1.807 +<pre class='output'>user
   1.808 +</pre>
   1.809 +<table class='note'><tr><td class='note_title'>Интересно, кто я?</td></tr><tr><td width='100%' class='note_text'>
   1.810 +Программа whoami выводит имя пользователя, под которым<br/>
   1.811 +мы зарегистрировались в системе.<br/>
   1.812 +<br/>
   1.813 +Она не может ответить на вопрос о нашем назначении<br/>
   1.814 +в этом мире.<br/>
   1.815 +</td></tr></table>
   1.816 +</td>
   1.817 +</tr>
   1.818 +</table>
   1.819 +	Для разделения нескольких абзацев между собой
   1.820 +	используйте символ "-", один в строке.
   1.821 +	<br/>
   1.822 +</p></li>
   1.823 +	<li><p>
   1.824 +	Комментарии, не относящиеся непосредственно ни к какой из команд, 
   1.825 +	добавляются точно таким же способом, только вместо симолов #^ или #v 
   1.826 +	нужно использовать символы #=
   1.827 +	</p></li>
   1.828 +</ul>
   1.829  HELP
   1.830  
   1.831  $Html_About = <<ABOUT;