lilalo
view l3-frontend @ 23:6d93c5f1d0e5
Выполнен шаг (2) в плане (N05) по построению распределённой системы lilalo.
Программа lm-report разрезана на две: l3-agent и l3-frontend.
Агент выполняет анализ script-файлов и записывает
результаты анализа в файл обмена (cache).
Фронтенд читает данные из файла обмена и представляет
их в требуемом формате (в настоящий момент только html).
Сейчас взаимодействие agent'а и frontend'а выполняется так:
. ^ . +-------+ . ^^ .
/ \ | | / \
( agent )-->| cache |--->( frontend )
\ / | | \ /
' . ' +-------+ ' .. '
Добавлены файлы:
l3-agent - агент
l3-frontend - фронтенд
l3-report - замена lm-report, использующая l3-agent и l3-frontend
l3-config.pm - модуль конфигурации системы
Новые конфигурационные параметры:
cache - Путь к временному XML-файлу, предназначенному
для обмена информацией между агентом и фронтендом
cache_head_lines - Количество строк вывода команды сверху, которые
должны быть сохранены в промежуточном XML-файле
cache_tail_lines - Количество строк вывода команды снизу, которые
должны быть сохранены в промежуточном XML-файле
Устаревшие параметры:
output_mask - Использование output_mask осуждается.
Параметр будет удалён из будущих версий
Использование lm-report осуждается.
В будущих версиях программа lm-report будет удалена из дистрибутива.
Вместо неё нужно использовать l3-report.
Программа lm-report разрезана на две: l3-agent и l3-frontend.
Агент выполняет анализ script-файлов и записывает
результаты анализа в файл обмена (cache).
Фронтенд читает данные из файла обмена и представляет
их в требуемом формате (в настоящий момент только html).
Сейчас взаимодействие agent'а и frontend'а выполняется так:
. ^ . +-------+ . ^^ .
/ \ | | / \
( agent )-->| cache |--->( frontend )
\ / | | \ /
' . ' +-------+ ' .. '
Добавлены файлы:
l3-agent - агент
l3-frontend - фронтенд
l3-report - замена lm-report, использующая l3-agent и l3-frontend
l3-config.pm - модуль конфигурации системы
Новые конфигурационные параметры:
cache - Путь к временному XML-файлу, предназначенному
для обмена информацией между агентом и фронтендом
cache_head_lines - Количество строк вывода команды сверху, которые
должны быть сохранены в промежуточном XML-файле
cache_tail_lines - Количество строк вывода команды снизу, которые
должны быть сохранены в промежуточном XML-файле
Устаревшие параметры:
output_mask - Использование output_mask осуждается.
Параметр будет удалён из будущих версий
Использование lm-report осуждается.
В будущих версиях программа lm-report будет удалена из дистрибутива.
Вместо неё нужно использовать l3-report.
| author | devi | 
|---|---|
| date | Wed Nov 02 19:16:11 2005 +0200 (2005-11-02) | 
| parents | |
| children | ba4d6515b8fd | 
 line source
     1 #!/usr/bin/perl -w
     3 use lib '.';
     4 use l3config;
     6 our @Command_Lines;
     8 # vvv Инициализация переменных выполняется процедурой init_variables
     9 our @Day_Name;
    10 our @Month_Name;
    11 our @Of_Month_Name;
    12 our %Search_Machines;
    13 our %Elements_Visibility;
    14 # ^^^
    16 sub search_buy;
    17 sub make_comment;
    18 sub load_command_lines_from_xml;
    19 sub print_command_lines;
    20 sub init_variables;
    21 sub main;
    23 main();
    25 sub main
    26 {
    27 	$| = 1;
    29 	init_variables();
    30 	init_config();
    32 	load_command_lines_from_xml($Config{"cache"});
    33 	print_command_lines($Config{"output"});
    34 }
    37 sub search_by
    38 {
    39 	my $sm = shift;
    40 	my $topic = shift;
    41 	$topic =~ s/ /+/;
    43 	return "<a href='".	$Search_Machines{$sm}->{"query"}."$topic'><img width='16' height='16' src='".
    44 				$Search_Machines{$sm}->{"icon"}."' border='0'/></a>";
    45 }
    47 sub make_comment
    48 {
    49 	my $commands = $_[0];
    50 	my $files = $_[1];
    51 	chomp $commands;
    52 	chomp $files;
    53 	return if (!$commands && !$files);
    55 	my $comment=""; 
    57 	# Commands
    58 	for my $command (split /\s+/,$commands) {
    59 		$command =~ s/'//g;
    60 		my $description="";
    61 		eval { $description=`mywi-client '$command'`; } ;
    62 		$description = join ("<br>\n", grep(/\([18]\)/, split(/\n/, $description)));
    63 		$description =~ s/.*?-//;
    64 		next if $description =~ /^\s*$/; 
    66 		my $query=$command." ".$Config{"keywords"};
    67 		$query =~ s/\ /+/g;
    68 		my $search= 	search_by("opennet",$query).
    69 				search_by("local",$command).
    70 				search_by("google",$query);
    72 		$comment .=     "<tr><td class='note_title'>$command</td>".
    73 				"<td class='note_search'>$search</td>".
    74 				"</tr><tr><td width='100%' colspan='2' class='note_text'>".
    75 				"$description</td></tr><tr/>";
    76 	}
    78 	# Files
    79 	for my $file (split /\s+/,$files) {
    80 		$file =~ s@.*/@@;
    81 		$file =~ s/'//g;
    82 		next if $file =~ /^\s*$/;
    83 		next if $file =~ /^-/;
    85 		my $description=`mywi '$file'`;
    86 		$description = join ("<br>\n", grep(/\(5\)/, split(/\n/, $description)));
    87 		next if $description =~ /^\s*$/; 
    89 		my $query=$file." ".$Config{"files_keywords"};
    90 		$query =~ s/\ /+/g;
    91 		my $search= 	search_by("opennet",$query).
    92 				search_by("local",$file).
    93 				search_by("google",$query);
    95 		$comment .=     "<tr><td class='note_title'>$file</td>".
    96 				"<td class='note_search'>$search</td>".
    97 				"</tr><tr><td width='100%' colspan='2' class='note_text'>".
    98 				"$description</td></tr><tr/>";
    99 	}
   102 	return $comment;
   103 }
   105 =cut
   106 Процедура load_command_lines_from_xml выполняет загрузку разобранного lab-скрипта
   107 из XML-документа в переменную @Command_Lines
   109 Предупреждение!
   110 Процедура не в состоянии обрабатывать XML-документ любой структуры.
   111 В действительности файл cache из которого загружаются данные 
   112 просто напоминает XML с виду.
   113 =cut
   114 sub load_command_lines_from_xml
   115 {
   116 	my $datafile = $_[0];
   118 	open (CLASS, $datafile)
   119 		or die "Can't open file of the class ",$datafile,"\n";
   120 	local $/;
   121 	$data = <CLASS>;
   122 	close(CLASS);
   124 	for $command ($data =~ m@<command>(.*?)</command>@sg) {
   125 		my %cl;
   126 		while ($command =~ m@<([^>]*?)>(.*?)</\1>@sg) {
   127 			$cl{$1} = $2;
   128 		}
   129 		push @Command_Lines, \%cl;
   130 	}
   131 }
   133 =cut
   134 Процедура print_command_lines выводит HTML-представление
   135 разобранного lab-скрипта. 
   137 Разобранный lab-скрипт должен находиться в массиве @Command_Lines
   138 =cut
   140 sub print_command_lines
   141 {
   142 	my $output_filename=$_[0];
   144 	my $course_name = $Config{"course-name"};
   145 	my $course_code = $Config{"course-code"};
   146 	my $course_date = $Config{"course-date"};
   147 	my $course_center = $Config{"course-center"};
   148 	my $course_trainer = $Config{"course-trainer"};
   149 	my $course_student = $Config{"course-student"};
   152 	# Результат выполнения процедуры равен 
   153 	# join("", @Result{header,body,stat,help,about,footer})
   154 	my %Result;
   155 	my $toc ="";  # Хранит оглавление по дням
   157 	$Result{"body"} = "<table width='100%'>\n";
   159 	my $cl;
   160 	my $last_tty="";
   161 	my $last_day="";
   162 	my $in_range=0;
   164 	for my $cl (@Command_Lines) {
   166 		if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) {
   167 			$in_range=1;
   168 			next;
   169 		}
   170 		if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) {
   171 			$in_range=0;
   172 			next;
   173 		}
   174 		next if ($Config{"from"} && $Config{"to"} && !$in_range) 
   175 			||
   176 		    	($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ )
   177 			||
   178 			($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0)
   179 			||
   180 			($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130);
   182 		#my @new_commands=@{$cl->{"new_commands"}};
   183 		#my @new_files=@{$cl->{"new_files"}};
   185 		my $cl_class="cline";
   186 		my $out_class="output";
   187 		if ($cl->{"class"}) {
   188 			$cl_class = $cl->{"class"}."_".$cl_class;
   189 			$out_class = $cl->{"class"}."_".$out_class;
   190 		}
   192 		my @new_commands;
   193 		my @new_files;
   194 		@new_commands = split (/\s+/, $cl->{"new_commands"}) if defined $cl->{"new_commands"};
   195 		@new_files = split (/\s+/, $cl->{"new_files"}) if defined $cl->{"new_files"};
   197 		my $output="";
   198 		if ($Config{"head_lines"} || $Config{"tail_lines"}) {
   199 			# Partialy output
   200 			my @lines = split '\n', $cl->{"output"};
   201 			# head
   202 			my $mark=1;
   203 			for (my $i=0; $i<= $#lines && $i < $Config{"head_lines"}; $i++) {
   204 				$output .= $lines[$i]."\n";
   205 			}
   206 			# tail
   207 			my $start=$#lines-$Config{"tail_lines"}+1;
   208 			if ($start < 0) {
   209 				$start=0;
   210 				$mark=0;
   211 			}	
   212 			if ($start < $Config{"head_lines"}) {
   213 				$start=$Config{"head_lines"};
   214 				$mark=0;
   215 			}	
   216 			$output .= $Config{"skip_text"}."\n" if $mark;
   217 			for (my $i=$start; $i<= $#lines; $i++) {
   218 				$output .= $lines[$i]."\n";
   219 			}
   220 		} 
   221 		else {
   222 			# Full output
   223 			$output .= $cl->{"output"};
   224 		}	
   225 		#$output .= "^C\n" if ($cl->{"err"} eq "130");
   227 		#
   228 		##
   229 		## Начинается собственно вывод
   230 		##
   231 		#
   233 		# <command>
   235 		my ($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime($cl->{time});
   236 		# Добавляем спереди 0 для удобочитаемости
   237 		$min = "0".$min if $min =~ /^.$/;
   238 		$hour = "0".$hour if $hour =~ /^.$/;
   239 		$sec = "0".$sec if $sec =~ /^.$/;
   241 		$class=$cl->{"out_class"};
   242 		$class =~ s/output$//;
   245 		$Result{"body"} .= "<tr class='command'>\n";
   248 		# DAY CHANGE
   249 		if ( $last_day ne $day) {
   250 			#$Result{"body"} .= "<td colspan='6'><p></p><h3>День ",$day,"</h4></td></tr><tr>";
   251 			$Result{"body"} .= "<td colspan='6'><p></p><h3 id='day$day'>".$Day_Name[$wday]."</h4></td></tr><tr>";
   252 			$toc .= "<li><a href='#day$day'>".$Day_Name[$wday]."</a></li>\n";
   253 			$last_day=$day;
   254 		}
   256 		# CONSOLE CHANGE
   257 		if ( $last_tty ne $cl->{"tty"}) {
   258 			$Result{"body"} .= "<td colspan='6'><table><tr><td class='ttychange' width='140' align='center'>".$cl->{"tty"}."</td><td/></tr></table></td></tr><tr>";
   259 			$last_tty=$cl->{"tty"};
   260 		}
   262 		# TIME
   263 		if ($Config{"show_time"} =~ /^y/i) {
   264 			$Result{"body"} .= "<td valign='top' class='time' width='$Config{time_width}'><pre>".
   265 				$hour. ":". $min. ":". $sec.
   266 				"</td>";
   267 		} else {
   268 			$Result{"body"} .= "<td width='0'/>"
   269 		}
   271 		# COMMAND
   272 		$Result{"body"} .= "<td class='script'>\n";
   273 		$Result{"body"} .= "<pre class='${class}cline'>\n";
   274 		my $cline = $cl->{"cline"};
   275 		$cline =~ s/\n//;
   276 		$Result{"body"} .= $cl->{"prompt"}.$cl->{"cline"};
   277 		$Result{"body"} .= "</pre>\n";
   279 		my $last_command = $cl->{"last_command"};
   280 		if (!( 
   281 		$Config{"suppress_editors"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"editors"}}) ||
   282 		$Config{"suppress_pagers"}  =~ /^y/i && grep ($_ eq $last_command, @{$Config{"pagers"}}) ||
   283 		$Config{"suppress_terminal"}=~ /^y/i && grep ($_ eq $last_command, @{$Config{"terminal"}})
   284 			)) {
   286 			$Result{"body"} .= "<pre class='".$cl->{out_class}."'>";
   287 			$Result{"body"} .= $output;
   288 			$Result{"body"} .= "</pre>\n";
   289 		}	
   291 		# DIFF
   292 		if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"}) {
   293 			$Result{"body"} .= "<table><tr><td width='5'/><td class='diff'><pre>";
   294 			$Result{"body"} .= $cl->{"diff"};
   295 			$Result{"body"} .= "</pre></td></tr></table>";
   296 		}
   298 		# COMMENT
   299 		if ( $Config{"show_comments"} =~ /^y/i) {
   300 			my $comment = make_comment(join(" ",@new_commands), join (" ",@new_files));
   301 			if ($comment) {
   302 				$Result{"body"} .= "<table width='$Config{comment_width}'>".
   303 						"<tr><td width='5'/><td>";
   304 				$Result{"body"} .= "<table class='note' width='100%'>";
   305 				$Result{"body"} .= $comment;
   306 				$Result{"body"} .= "</table>\n";
   307 				$Result{"body"} .= "</td></tr></table>";
   308 			}
   309 			else {
   310 				$Result{"body"} .= "<table width='$Config{comment_width}'>".
   311 						"<tr><td width='5'/><td>";
   312 				$Result{"body"} .= "<table class='note' width='100%'>";
   313 				$Result{"body"} .= "commands ".join(" ",@new_commands)."<br/>";
   314 				$Result{"body"} .= "files ".join(" ",@new_files)."<br/>";
   315 				$Result{"body"} .= "</table>\n";
   316 				$Result{"body"} .= "</td></tr></table>";
   317 			}
   318 		}
   320 		# Вывод очередной команды окончен
   321 		$Result{"body"} .= "</td>\n";
   322 		$Result{"body"} .= "</tr>\n";
   323 	}
   325 	$Result{"body"} .= "</table>\n";
   327 	$Result{"stat"} = "<hr/>";
   328 	$Result{"stat"} .= "<h2 id='stat'>Статистика</h2>";
   329 	$Result{"stat"} .= "Статистическая информация о журнале<br/>";
   330 	$Result{"help"} .= "<hr/>";
   331 	$Result{"help"} .= "<h2 id='help'>Справка</h2>";
   332 	$Result{"help"} .= "$Html_Help<br/>";
   333 	$Result{"about"} .= "<hr/>";
   334 	$Result{"about"} .= "<h2 id='about'>О программе</h2>";
   335 	$Result{"about"} .= "$Html_About";
   336 	$Result{"footer"} .= "</body>\n";
   337 	$Result{"footer"} .= "</html>\n";
   339 	# Заголовок генерируется позже всего
   340 	# Тогда, когда известно уже, что должно быть написано в 
   341 	# оглавлении
   342 	$Result{"header"} = <<HEADER;
   343 	<html>
   344 	<head>
   345 	<meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
   346 	<link rel='stylesheet' href='labmaker.css' type='text/css'/>
   347 	</head>
   348 	<body>
   349 	<script>
   350 	$Html_JavaScript
   351 	</script>
   352 	<h2>Журнал лабораторных работ</h2>
   354 	<p>
   355 	Выполнил $course_student<br/>
   356 	Проверил $course_trainer <br/>
   357 	Курс $course_name ($course_code),
   358 	$course_date<br/>
   359 	Учебный центр $course_center <br/>
   360 	</p>
   362 	<ul>
   363 		<li><a href='#log'>Журнал</a></li>
   364 		<ul>$toc</ul>
   365 		<li><a href='#stat'>Статистика</a></li>
   366 		<li><a href='#help'>Справка</a></li>
   367 		<li><a href='#about'>О программе</a></li>
   368 	</ul>
   370 	<h2 id="log">Журнал</h2>
   371 HEADER
   372 	$Result{"header"} .= "<table class='visibility_form'><tr><td><form>\n";
   373 	for my $element (keys %Elements_Visibility)
   374 	{
   375 		my @e = split /\s+/, $element;
   376 		my $showhide = join "", map { "ShowHide('$_');" } @e ;
   377 		$Result{"header"} .= "<input type='checkbox' name='$e[0]' onclick=\"$showhide\" checked>".
   378 				$Elements_Visibility{$element}.
   379 				"</input><br>\n";
   380 	}
   382 	$Result{"header"} .= "</form></td></tr></table>\n";
   384 	open(OUT, ">", $output_filename)
   385 		or die "Can't open $output_filename for writing\n";
   386 	print OUT $Result{"header"}, $Result{"body"}, $Result{"stat"}, $Result{"help"}, $Result{"about"}, $Result{"footer"};
   387 	close(OUT);
   388 }
   395 sub init_variables
   396 {
   397 $Html_Help = <<HELP;
   398 	Справка по использованию журнала
   399 HELP
   401 $Html_About = <<ABOUT;
   402 	<p>
   403 	LiLaLo (L3) расшифровывается как Live Lab Log.<br/>
   404 	Программа разработана для повышения эффективности обучения<br/>
   405 	Unix/Linux-системам.<br/>
   406 	(c) Игорь Чубин, 2004-2005<br/>
   407 	</p>
   408 ABOUT
   409 $Html_About.='$Id$ </p>';
   411 $Html_JavaScript = <<JS;
   412 	function getElementsByClassName(Class_Name)
   413 	{
   414 		var Result=new Array();
   415 		var All_Elements=document.all || document.getElementsByTagName('*');
   416 		for (i=0; i<All_Elements.length; i++)
   417 			if (All_Elements[i].className==Class_Name)
   418 		Result.push(All_Elements[i]);
   419 		return Result;
   420 	}
   421 	function ShowHide (name)
   422 	{
   423 		elements=getElementsByClassName(name);
   424 		for(i=0; i<elements.length; i++)
   425 			if (elements[i].style.display == "none")
   426 				elements[i].style.display = "";
   427 			else
   428 				elements[i].style.display = "none";
   429 			//if (elements[i].style.visibility == "hidden")
   430 			//	elements[i].style.visibility = "visible";
   431 			//else
   432 			//	elements[i].style.visibility = "hidden";
   433 	}
   434 	function filter_by_output(text)
   435 	{
   437 		var jjj=0;
   439 		elements=getElementsByClassName('command');
   440 		for(i=0; i<elements.length; i++) {
   441 			subelems = elements[i].getElementsByTagName('pre');
   442 			for(j=0; j<subelems.length; j++) {
   443 				if (subelems[j].className = 'output') {
   444 					var str = new String(subelems[j].nodeValue);
   445 					if (jjj != 1) { 
   446 						alert(str);
   447 						jjj=1;
   448 					}
   449 					if (str.indexOf(text) >0) 
   450 						subelems[j].style.display = "none";
   451 					else
   452 						subelems[j].style.display = "";
   454 				}
   456 			}
   457 		}		
   459 	}
   460 JS
   462 %Search_Machines = (
   463 		"google" => 	{ 	"query" => 	"http://www.google.com/search?q=" ,
   464 					"icon" 	=> 	"google.ico" },
   465 		"freebsd" => 	{ 	"query" => 	"http://www.freebsd.org/cgi/man.cgi?query=",
   466 					"icon"	=>	"freebsd.ico" },
   467 		"linux"  => 	{ 	"query" => 	"http://man.he.net/?topic=",
   468 					"icon"	=>	"linux.ico"},
   469 		"opennet"  => 	{ 	"query" => 	"http://www.opennet.ru/search.shtml?words=",
   470 					"icon"	=>	"opennet.ico"},
   471 		"local" => 	{ 	"query" => 	"http://www.freebsd.org/cgi/man.cgi?query=",
   472 					"icon"	=>	"freebsd.ico" },
   474 	);
   476 %Elements_Visibility = (
   477 		"note"		=>	"замечания",
   478 		"diff"		=>	"редактор",
   479 		"time"		=>	"время",
   480 		"ttychange" 	=>	"терминал",
   481 		"wrong_output wrong_cline wrong_root_output wrong_root_cline" 
   482 				=>	"команды с ошибками",
   483 		"interrupted_output interrupted_cline interrupted_root_output interrupted_root_cline" 
   484 				=>	"прерванные команды",
   485 		"tab_completion_output tab_completion_cline"	
   486 				=> 	"продолжение с помощью tab"
   487 );
   489 @Day_Name      = qw/ Воскресенье Понедельник Вторник Среда Четверг Пятница Суббота /;
   490 @Month_Name    = qw/ Январь Февраль Март Апрель Май Июнь Июль Август Сентябрь Октябрь Ноябрь Декабрь /;
   491 @Of_Month_Name = qw/ Января Февраля Марта Апреля Мая Июня Июля Августа Сентября Октября Ноября Декабря /;
   492 }
