#!/usr/bin/perl -w

# RT3 queue metrics 
#
# This script counts metrics for queues.
#
# $Id: rt-metrics,v 0.5 2011/01/31 08:24 $

### Configuration

my(@queues); # do not delete this
my($now) = time; # do not delete this

# Location of RT3's libs
use lib ("/usr/share/request-tracker3.6/lib/RT/", "/usr/share/request-tracker3.6/lib/");

### -------------------------------------------------- Uzivatelsky modifikovatelne promenne -------------------------------------------------------

## Ladeni a systemova nastaveni

# oprava chybneho zachazeni RT s nekterymi transakcemi
my $fix_rt_bugs = 0;  # pokud je "dnesni" cas ($now) jiny nez datum spusteni, musi byt $fix_rt_bug = 0 
		      # (nesoulad ticket->Status a last transaction typu status neni pri posunu do minulosti posouditelny)

# ladici vypisy (doba trvani vypoctu)
my $debug_stat_time = 0;

# ladici vypisy (nazvy front apod.)
my $general_debug = 0;

# ladici vypisy (dle zadaneho cisla metriky)
my $debug_m1 = 0;
my $debug_m2 = 0;
my $debug_m3 = 0;
my $debug_m4 = 0;
my $debug_m5 = 0;
my $debug_m6 = 1;
my $debug_m7 = 0; #free
my $debug_m8 = 0;
my $debug_m9 = 0;
my $debug_m10 = 0;
my $debug_m15 = 0;
my $debug_m16 = 0;
my $debug_m17 = 0;

## Obecne udaje

# delka obdobi pro statistiku [s] 
#my(@stat_period) = qw ( 604800 1209600 2419200 );	# tyden, 2 tydny, 4 tydny [s]
my(@stat_period) = qw ( 604800 1209600 2419200 15768000);	# tyden, 2 tydny, 4 tydny, pulroku [s]
#my(@stat_period) = qw ( 14515200 );	

# horni casovy limit (tj. default je dnes)
#$now = time;
#$now = timelocal(0, 0, 1, 1, 1, 2008); #1.2.2008
$now = timelocal(0, 0, 1, 30, 10, 2010); # (vterina, minuta, hodina, den, mesic+1, rok)

# Queues to operate on (default is all)
#@queues = qw ( queue1 queue2 );
#@queues = qw ( security );
@queues = qw ( civ );
#@queues = qw ( security hostmaster hostmaster-lps aaa kps lps civ aup email helps iss operator pro sis);
#@queues = qw ( aaa aup civ civ-ek courseware cro email helps hostmaster hostmaster-lps ics is iss jis juk kis konta kps linux lps monitoring mysql operator orion pis portal portal-ostatni portal_dev presunyhw publ rdkd restore sdjis security sis stag stag-analyzy stag-db stag-eprihlaska stag-eva stag-formsy stag-ostatni stag-portal stag-portal-adm stag-reporty stag-web studium telefony tisk-safeq tiskarny tisky-special ts web zmeny zsc);

# pocet requestoru povazovany za viceuzivatelsky pozadavek
my $multi_requestor = 3;	

# obdobi, po kterych je nereseny ticket povazovan za shnily
my(@decay_period) = qw (259200 604800 1209600 2419200);	# 3 dny, tyden, 14 dni, 28 dni (v sekundach)

# seznam emailovych adres (resp. koncovek e.a.), ktere jsou povazovany za interni
my @internal_emails = qw ( @civ.zcu.cz );

## nastaveni vypisu

# detailni vypis neuzavrenych ticketu
my $show_openticket_details = 0;

# zobrazeni celkove prehledove tabulky (csv)
my $show_csv_table = 1;

# zobrazeni zaverecnych zprav pro jednotlive fronty
my $show_queue_stats = 0;

# jmeno vystupniho souboru
my $out_file_extension = ".csv";

# zobrazeni ukazatele prubehu
my $show_progress = 2;

### ---------------------------------------------------------------- Code -------------------------------------------------------------------------

use strict;
use Carp;
use Time::Local;

# Pull in the RT stuff
package RT;
use RT::Interface::CLI qw(CleanEnv GetCurrentUser GetMessageContent loc);
use RT::Date;
use RT::User;
use RT::Ticket;
use RT::Tickets;
use RT::Groups;
use RT::Transactions;
use RT::Links;
use RT::CustomField;
use RT::CustomFields;
use RT::CustomFieldValue;
use RT::CustomFieldValues;

# Clean our the environment
CleanEnv();

# Load the RT configuration
RT::LoadConfig();

# Initialise RT (connect to database)
RT::Init();

# Priprava casu
my $time_now = new RT::Date($RT::SystemUser);
$time_now->Set( Format => 'unix', Value => $now);

# Priprava vystupnich poli
my @out_stats = ();
my @out_csv_table = ();
my @openticket_details = ();

# Pokud nejsou specifikovany fronty v uvodni promenne, vlozi se tam vsechny
if (!@queues) {
    my $queues = new RT::Queues($RT::SystemUser);
    $queues->LimitToEnabled();
    foreach my $queue (@{$queues->ItemsArrayRef()}) {
	    push @queues, $queue->Name;
    }
}

# Vygenerovani nazvu vystupniho souboru a pokus o jeho otevreni
my $out_file = &formatFilename($now) . $out_file_extension;


open(OUTFILE, ">", $out_file) || die "Nelze otevrit soubor $!";

# Zpracovani jednotlivych front

my $i;
foreach my $queuename (@queues) {
    # ulozeni casu zacatku zpracovani fronty
    my $time_queue_start = time;
    	
    # Nacteni fronty pod danym uzivatelem)
    my $queue = new RT::Queue($RT::SystemUser);
    $queue->Load($queuename);

    # Vypis hlavicky k dane fronte do zaverecne zpravy
    push @out_stats, "==== Queue " . $queue->Name . " ====\n";

    if ($show_progress > 0){
      print &formatDate($time_queue_start), " - zahajeno zpracovani fronty ", $queue->Name, "\n";
    }

    # Vytvoreni a zpracovani kolekce ticketu - zakladni udaje
    my $tickets = new RT::Tickets($RT::SystemUser);
    $tickets->LimitQueue(VALUE => $queue->Id);		#pouze analyzovana fronta
   
    print "DEBUG: Queue: ", $queue->Name if ($general_debug > 0);
    
    
    # priprava promenne do tabulky
    my $queue_stat_string = $queue->Name . ";" . &formatDate($now) . ";";

    ## inicialize frontovych promennych
    # globalni statistika fronty
    my @created = ();
    my @new = ();
    my @open = ();
    my @resolved = (); 
    my @deleted = ();
    my @stalled = ();
    my @rejected = ();
    my $processed_tickets = 0;
    my $processed_transactions = 0;

    for($i=0;$i<scalar(@stat_period);$i++){
    	$created[$i] = 0;
	$new[$i] = 0;
    	$open[$i] = 0;
    	$resolved[$i] =0; 
    	$deleted[$i] = 0;
    	$stalled[$i] = 0;
    	$rejected[$i] = 0;
    } # for i

    # M1, M2
    my $status_new = 0;
    my $status_open = 0;

    # M6 - casove intervaly neuzavrenych tiketu
    my $sum_waiting_time = 0;
    my $n_waiting_time = 0;
    my $min_waiting_time = $time_now; #nic nemohlo trvat dele nez od zacatku IT dosud
    my $max_waiting_time = 0;
    my $min_waiting_rtid = 0;
    my $max_waiting_rtid = 0;
     
    # M16* - casove intervaly 
    my @sum_all_answer_time = ();
    my @n_all_answer_time = ();
    my @max_all_answer_time = ();
    my @min_all_answer_time = ();
    my @max_all_rtid = ();
    my @min_all_rtid = ();
    
    for($i=0;$i<scalar(@stat_period);$i++){
    	$sum_all_answer_time[$i] = 0;
    	$n_all_answer_time[$i] = 0;
    	$max_all_answer_time[$i] = 0;
    	$min_all_answer_time[$i] = $time_now; #nic nemohlo trvat dele nez od zacatku IT dosud
	$max_all_rtid[$i] = "N/A";
	$min_all_rtid[$i] = "N/A";
    }

    # M17* - casove intervaly 
    my @sum_answer_time = ();
    my @n_answer_time = ();
    my @max_answer_time = ();
    my @min_answer_time = ();
    my @max_rtid = ();
    my @min_rtid = ();

    for($i=0;$i<scalar(@stat_period);$i++){
    	$sum_answer_time[$i] = 0;
    	$n_answer_time[$i] = 0;
    	$max_answer_time[$i] = 0;
    	$min_answer_time[$i] = $time_now; #nic nemohlo trvat dele nez od zacatku IT dosud
	$max_rtid[$i] = "N/A";
	$min_rtid[$i] = "N/A";
    }
    
    # M3
    my $no_correspond = 0;

    # M4
    my $external_open = 0;

    # M5
    my $internal_open = 0;

    # M8
    my $no_owner_open = 0;

    # M9
    my $last_ping_requestor = 0;

    # M10
    my @decay_ticket = ();
    for($i=0;$i<scalar(@decay_period);$i++){
	    $decay_ticket[$i] = 0;
    }

    # M15
    my $multi_ticket = 0;
    
    while (my $ticket = $tickets->Next) {
	# nalezeni vlastnika ticketu (pro spolecne pouziti)
        my $Owner = RT::User->new($RT::SystemUser);		
	$Owner->Load($ticket->Owner);

	# vytvoreni pole requestoru (pro spolecne pouziti)
	my @requestors = split(/,\s/, $ticket->RequestorAddresses);

	# ukazatel prubehu
	if (($show_progress > 1) && ($processed_tickets % 100 == 0)){
		printf("  %s - %d tiketu zpracovano\n", &formatDate(time), $processed_tickets);
	}
	
	# pocitadlo ticketu
	$processed_tickets++;
	
	# inicializace
	my @last_status = "none";
	my @last_create = 0;
	for($i=0;$i<scalar(@stat_period);$i++){
		$last_status[$i] = "none";
		$last_create[$i] = 0;
	}
	

	# M3
	my $internal_reply = 0;		
	my $internal_comment = 0;	

	# M16* casove intervaly
    	my $ticketrequest = 0;		
    	my $ticketrequest_time = 0;	
    	my $ticketanswer = 0;		
    	my $ticketanswer_time = 0;	
	
        # M17* casove intervaly
    	my $first_ticketrequest = 0;		
    	my $first_ticketrequest_time = 0;	
    	my $first_ticketanswer = 0;		
    	my $first_ticketanswer_time = 0;	

	# M9, M10
	my $last_ping_address = "";	
	my $last_ping_time = 0;
	my $last_int_ping_address = "";	
	my $last_int_ping_time = 0;

	# vytvoreni kolekce transakci a jejich omezeni na dany ticket
    	my $Transactions = new RT::Transactions($RT::SystemUser);
	$Transactions->LimitToTicket($ticket->Id);	    

	while(my $Transaction = $Transactions->Next) {
	   # nalezeni emailove adresy autora transakce (pro spolecne pouziti)
	   my $trUser = new RT::User($RT::SystemUser);
	   $trUser->Load($Transaction->Creator);

	   if ($Transaction->CreatedObj->Unix <= $now){
		# pocitadlo transakci
		$processed_transactions++;
		
		# M16* vypocet doby cekani na odpoved od requestora
		if ($ticketrequest == 0) {
			if ((($Transaction->Type eq "Create") || ($Transaction->Type eq "Correspond")) && (&isExternal($trUser->EmailAddress, @requestors) == 0)){
				# email autora transakce je mezi requestory
				$ticketrequest = 1;
				$ticketanswer = 0;
				$ticketrequest_time = $Transaction->CreatedObj->Unix;
			}
		}
		else {
			# nalezeni prvni nasledujici korespondence od resitelu
			if (($Transaction->Type eq "Correspond") && (&isExternal($trUser->EmailAddress, @internal_emails) == 0)) {
				#email autora transakce je mezi internimi adresami
				$ticketrequest = 0;
				$ticketanswer = 1;
				$ticketanswer_time = $Transaction->CreatedObj->Unix;
				#zpracovani pro jednotlive casove intervaly
				$i = 0;
				foreach my $period (@stat_period){
					my $time_prev = new RT::Date($RT::SystemUser);
					$time_prev->Set( Format => 'unix', Value => $now - $period);

					if (($time_prev->Diff($Transaction->CreatedObj->Unix) < 0) && ($time_now->Diff($Transaction->CreatedObj->Unix) >= 0)){
						print "DEBUG: M16* Doba odpovedi pro RT#", $ticket->EffectiveId(), " = ", &formatTime($ticketanswer_time - $ticketrequest_time), "( ", &formatDate($ticketrequest_time), " - " , &formatDate($ticketanswer_time), " ), ", $n_all_answer_time[$i]+1, "\n" if ($debug_m16 > 0);
						if ($max_all_answer_time[$i] < $ticketanswer_time - $ticketrequest_time) {
							$max_all_answer_time[$i] = $ticketanswer_time - $ticketrequest_time;
							$max_all_rtid[$i] = $ticket->Id;
						}
						if ($min_all_answer_time[$i] > $ticketanswer_time - $ticketrequest_time) {
							$min_all_answer_time[$i] = $ticketanswer_time - $ticketrequest_time;
							$min_all_rtid[$i] = $ticket->Id;
						}
						$sum_all_answer_time[$i] += $ticketanswer_time - $ticketrequest_time;
						$n_all_answer_time[$i]++;
					}
					$i++;
				} #foreach
			}
		}
		
		# M17* nalezeni prvni transakce od requestora
		if ($first_ticketrequest == 0) {
			if ((($Transaction->Type eq "Create") || ($Transaction->Type eq "Correspond")) && (&isExternal($trUser->EmailAddress, @requestors) == 0)) {
				# email autora transakce je mezi requestory
				#print "RT#", $ticket->EffectiveId() ," first requestor transaction:\n";
				#print "  ", $Transaction->Description, "(", $Transaction->Type, ") by ", $trUser->EmailAddress, "\n";
				$first_ticketrequest = 1;
				$first_ticketrequest_time = $Transaction->CreatedObj->Unix;
			}
		}
		else {
			# nalezeni prvni nasledujici korespondence od resitelu
			if (($Transaction->Type eq "Correspond") && ($first_ticketanswer == 0)){
				if (&isExternal($trUser->EmailAddress, @internal_emails) == 0) {
					#email autora transakce je mezi internimi adresami
					$first_ticketanswer = 1;
					$first_ticketanswer_time = $Transaction->CreatedObj->Unix;
				}
			}
		}

		# M3 zjisteni poctu internich odpovedi (comment, reply)
		if (($ticket->Status eq "open") || ($ticket->Status eq "new")){
			if (($Transaction->Type eq "Correspond") && (&isExternal($trUser->EmailAddress, @internal_emails) == 0)){
				$internal_reply++;
			}
			if (($Transaction->Type eq "Comment") && (&isExternal($trUser->EmailAddress, @internal_emails) == 0)){
				$internal_comment++;
			}
		}
		
		# M9, M10 zjisteni posledniho prispevatele (Correspond nebo Comment) a doby, kdy se tak stalo
		if (($Transaction->Type eq "Correspond") || ($Transaction->Type eq "Comment") || ($Transaction->Type eq "Create")){
			$last_ping_address = $trUser->EmailAddress;
			$last_ping_time = $Transaction->CreatedObj->Unix;
			if (&isExternal($trUser->EmailAddress, @requestors) == 1){
				$last_int_ping_address = $trUser->EmailAddress;
				$last_int_ping_time = $Transaction->CreatedObj->Unix;
			}
		}

		$i = 0;	
		#zpracovani textove zavislych polozek
		foreach my $period (@stat_period){
			my $time_prev = new RT::Date($RT::SystemUser);
			$time_prev->Set( Format => 'unix', Value => $now - $period);

			# M1 - pocty zmen stavu ticketu v uvednem casovem rozsahu
			if (($time_prev->Diff($Transaction->CreatedObj->Unix) < 0) && ($time_now->Diff($Transaction->CreatedObj->Unix) >= 0)){
				# pocet vytvoreni ticketu
			 	if ($Transaction->Type eq "Create"){
					$last_create[$i]++;
			 	}
				# zmena statusu
			 	if ($Transaction->Type eq "Status"){
					if ($Transaction->Description =~ /to '.*' by/){
						$last_status[$i] = $&;			
						if ($fix_rt_bugs == 1){
							#oprava nesouladu mezi posledni zmenou statusu a ulozenym statusem (ulozeny status ma prednost)
							if ($ticket->Status eq "resolved") {
								$last_status[$i] = "to 'resolved' by";
							}		
							if ($ticket->Status eq "deleted") {
								$last_status[$i] = "to 'deleted' by";
							}		
							if ($ticket->Status eq "stalled") {
								$last_status[$i] = "to 'stalled' by";
							}		
							if ($ticket->Status eq "rejected") {
								$last_status[$i] = "to 'rejected' by";
							}		
							if ($ticket->Status eq "open") {
								$last_status[$i] = "to 'open' by";
							}		
							if ($ticket->Status eq "new") {
								$last_status[$i] = "to 'new' by";
							}		

						} # if fix_rt_bugs
					}				
			 	}
		 	} # transakce v danem casovem intervalu
	 		$i++;
		} #foreach vsechny casove intervaly
	    } # if transaction-created <= now
	} #while transactions
	
  if ($last_ping_time > 0) {
	# M11, M12, M13, M14
	for($i=0;$i<scalar(@stat_period);$i++){
		if ($last_create[$i] > 0){
			$created[$i]++;
		}
		if ($last_status[$i] eq "to 'resolved' by") {
			$resolved[$i]++;
		}
		if ($last_status[$i] eq "to 'deleted' by") {
			$deleted[$i]++;
		}	
		if ($last_status[$i] eq "to 'stalled' by") {
			$stalled[$i]++;
		}    
		if ($last_status[$i] eq "to 'rejected' by") {
			$rejected[$i]++;
		}	
		if ($last_status[$i] eq "to 'new' by") {
			$new[$i]++;
		}	
		if ($last_status[$i] eq "to 'open' by") {
			$open[$i]++;
		}	
	} #for i
	
	# M1, M2
	if ($ticket->Status eq "new") {
		$status_new++;
		print "DEBUG: M1-2 ticket new:  #", $ticket->EffectiveId(), " ", $ticket->Subject, "\n" if ($debug_m1 > 0);
	}
	if ($ticket->Status eq "open") {
		print "DEBUG: M1 ticket open:  #", $ticket->EffectiveId(), " ", $ticket->Subject, "\n" if ($debug_m2 > 0);
		$status_open++;
	}


	# statistiky neuzavrenych pozadavku
	if (($ticket->Status eq "open") || ($ticket->Status eq "new")){
		# M3
		if ($internal_reply == 0) {	
			print "DEBUG: M3 ticket:  #", $ticket->EffectiveId(), " ", $ticket->Subject, "\n" if ($debug_m3 > 0);
			$no_correspond++
		}
		
		# M4
		if (&isExternal($ticket->RequestorAddresses, @internal_emails) == 1){
			print "DEBUG: M4 ticket:  #", $ticket->EffectiveId(), " ", $ticket->Subject, "\n" if ($debug_m4 > 0);
			$external_open ++;		
		}
		else {
			if (&hasExternalLinks($ticket) == 1) {
				print "DEBUG: M4 ticket:  #", $ticket->EffectiveId(), " ", $ticket->Subject, "\n" if ($debug_m4 > 0);
				$external_open++;		
			}
		}

		# M5
		if (&isExternal($ticket->RequestorAddresses, @internal_emails) == 0){
			print "DEBUG: M5 ticket open:  #", $ticket->EffectiveId(), " ", $ticket->Subject, "\n" if ($debug_m5 > 0);
			$internal_open++;
		}

		# M6
		my $ref_time = $now;
		if ($last_int_ping_time > 0){
			$ref_time = $last_int_ping_time;
		}
		else {
			$ref_time = $last_ping_time;	#pokud spravce fronty neodpovedel, pocita vytvoreni ticketu
		}
		
		if ($ref_time < $now){
			$sum_waiting_time += ($now - $ref_time);
			$n_waiting_time ++;
			if (($now - $ref_time) < $min_waiting_time){
				$min_waiting_time = ($now - $ref_time);
				$min_waiting_rtid = $ticket->EffectiveId();
			} 
			if (($now - $ref_time) > $max_waiting_time){
				$max_waiting_time = ($now - $ref_time);
				$max_waiting_rtid = $ticket->EffectiveId();
			} 
			if ($debug_m6 > 0){
				print "DEBUG: M6 ticket:  #", $ticket->EffectiveId(), " ", $ticket->Subject, " (", &formatDate($now), " - ", &formatDate($ref_time), " = ", &formatTime($now - $ref_time), ")\n";
				print "  DEBUG: M6 values (n, sum, min, max):", $n_waiting_time, ", ", &formatTime($sum_waiting_time), ", ", &formatTime($min_waiting_time), ", ", &formatTime($max_waiting_time), "\n";			
			}
		}

		# M8
		if ($Owner->EmailAddress eq ""){
			print "DEBUG: M8 ticket:  #", $ticket->EffectiveId(), " ", $ticket->Subject, "\n" if ($debug_m8 > 0);
			$no_owner_open++;
		}

		# M9
		if (&isExternal($last_ping_address, @requestors) == 0) { 
			print "DEBUG: M9 ticket:  #", $ticket->EffectiveId(), " ", $ticket->Subject, "\n" if ($debug_m9 > 0);
			$last_ping_requestor++;
		}
		# M10
		$i = 0;
		foreach my $period (@decay_period){
			if ($time_now->Diff($last_int_ping_time) > $period){ 	
				print "DEBUG: M10 (", &formatTime($period),") ticket:  #", $ticket->EffectiveId(), " ", $ticket->Subject, "\n" if ($debug_m10 > 0);
				$decay_ticket[$i]++;
			}
			$i++;
		} #foreach decay period
		# M15
		if (scalar(@requestors) >= $multi_requestor){
			print "DEBUG: M15 ticket:  #", $ticket->EffectiveId(), " ", $ticket->Subject, "\n" if ($debug_m15 > 0);
			$multi_ticket++;
		}
    	} # if new/open

	# statistiky zavisle na casovem rozmezi	
	$i = 0;
	foreach my $period (@stat_period){	
		my $time_prev = new RT::Date($RT::SystemUser);
		$time_prev->Set( Format => 'unix', Value => $now - $period);
		
		# M17
		if (($first_ticketanswer_time - $first_ticketrequest_time > 0) && ($time_prev->Diff($first_ticketanswer_time) < 0) && ($time_now->Diff($first_ticketanswer_time) >= 0)){
			# pouze odpovedi v danem casovem intervalu
			print "DEBUG: M17* Doba prvni odpovedi pro RT#", $ticket->EffectiveId(), " = ", &formatTime($first_ticketanswer_time - $first_ticketrequest_time), "\n" if ($debug_m17 > 0);
			if ($max_answer_time[$i] < $first_ticketanswer_time - $first_ticketrequest_time) {
				$max_answer_time[$i] = $first_ticketanswer_time - $first_ticketrequest_time;
				$max_rtid[$i] = $ticket->Id;
			}
			if ($min_answer_time[$i] > $first_ticketanswer_time - $first_ticketrequest_time) {
				$min_answer_time[$i] = $first_ticketanswer_time - $first_ticketrequest_time;
				$min_rtid[$i] = $ticket->Id;
			}
			$sum_answer_time[$i] += $first_ticketanswer_time - $first_ticketrequest_time;
			$n_answer_time[$i]++;
		}
		$i++;
	} #foreach jednotliva casova rozmezi
  } # if last_ping > 0
} # while tickets
    
    # vypis kriterii do vystupnich promennych
     
    push @out_stats, sprintf("* Hodnoceni aktualniho stavu\n");
    push @out_stats, sprintf("** Pocet neuzavrenych pozadavku (M1) = %d\n", $status_new + $status_open);
    $queue_stat_string = $queue_stat_string . sprintf("%d;", $status_new + $status_open);    
    push @out_stats, sprintf("*** z toho vubec neresenych pozadavku (M2) = %d\n", $status_new);
    $queue_stat_string = $queue_stat_string . sprintf("%d;", $status_new);    
    push @out_stats, sprintf("*** z toho bez reply (M3) = %d\n", $no_correspond);
    $queue_stat_string = $queue_stat_string . sprintf("%d;", $no_correspond);    
    push @out_stats, sprintf("*** z toho vnejsi pozadavky (M4) = %d\n", $external_open);
    $queue_stat_string = $queue_stat_string . sprintf("%d;", $external_open);   
    push @out_stats, sprintf("*** z toho vnitrni pozadavky (M5) = %d\n", $internal_open);
    $queue_stat_string = $queue_stat_string . sprintf("%d;", $internal_open);

    if ($status_new + $status_open > 0) {
	    push @out_stats, sprintf("*** z toho bez vlastnika (M8) = %d (%2.0f\%)\n", 
		    $no_owner_open, 
		    $no_owner_open / ($status_new + $status_open)*100);
	    $queue_stat_string = $queue_stat_string . sprintf("%d;%2.0f\%;", $no_owner_open, $no_owner_open / ($status_new + $status_open)*100);
    }
    else {
    	    push @out_stats, "*** z toho bez vlastnika (M8) = ", $no_owner_open, "(0%)\n";
	    $queue_stat_string = $queue_stat_string . sprintf("%d;0%;", $no_owner_open);
    }
    push @out_stats,  sprintf("*** z toho s posledni zpravou od requestora (M9) = %d\n", $last_ping_requestor);
    $queue_stat_string = $queue_stat_string . sprintf("%d;", $last_ping_requestor);
    push @out_stats,  sprintf("*** z toho multi requestorske (vice jak %d requestoru) (M15) = %d\n", $multi_requestor, $multi_ticket);
    $queue_stat_string = $queue_stat_string . sprintf("%d;", $multi_ticket);

    $i = 0;
    foreach my $period (@decay_period){
    	push @out_stats,  sprintf("*** z toho neresenych dele nez limit (od %s, tj. %s) (M10) = %d\n", &formatDate($now-$period), &formatTime($period), $decay_ticket[$i]);
    	$queue_stat_string = $queue_stat_string . sprintf("%d;", $decay_ticket[$i]);
	$i++;
    }

    if ($n_waiting_time > 0) {
	    push @out_stats, sprintf("*** minimalni doba od posledni aktivity (M6b) = %s (RT#%s)\n", &formatTime($min_waiting_time), $min_waiting_rtid);
	    $queue_stat_string = $queue_stat_string . sprintf("%s (RT#%s);", &formatTime($min_waiting_time), $min_waiting_rtid);
	    push @out_stats, sprintf("*** maximalni doba od posledni aktivity (M6c) = %s (RT#%s)\n", &formatTime($max_waiting_time), $max_waiting_rtid);
	    $queue_stat_string = $queue_stat_string . sprintf("%s (RT#%s);", &formatTime($max_waiting_time), $max_waiting_rtid);
	    push @out_stats, sprintf("*** prumerna doba od posledni aktivity (M6d) = %s\n", &formatTime($max_waiting_time), $max_waiting_rtid);
	    $queue_stat_string = $queue_stat_string . sprintf("%s;", &formatTime($sum_waiting_time/$n_waiting_time));
    }
    else{
	    push @out_stats, sprintf("*** minimalni doba od posledni aktivity (M6b) = N/A\n");
    	    push @out_stats, sprintf("*** maximalni doba od posledni aktivity (M6c) = N/A\n");
    	    push @out_stats, sprintf("*** prumerna doba od posledni aktivity (M6d) = N/A\n");
	    $queue_stat_string = $queue_stat_string . "N/A (N/A);N/A (N/A);N/A (N/A);";
    }
    
    $i = 0;
    foreach my $period (@stat_period){
	my $time_prev = new RT::Date($RT::SystemUser);
	$time_prev->Set( Format => 'unix', Value => $now - $period);

    	push @out_stats, sprintf("* Hodnoceni uplynuleho obdobi (%s - %s)\n",  $time_prev->ISO(), $time_now->ISO());
	push @out_stats, sprintf("** Obrat pozadavku\n");
	push @out_stats, sprintf("*** Vznikle pozadavky (M11) = %d\n", $created[$i]);
    	$queue_stat_string = $queue_stat_string . sprintf("%d;", $created[$i]);
    	push @out_stats, sprintf("*** Obslouzene pozadavky (M12) = %d\n", $resolved[$i]+$deleted[$i]+$stalled[$i]+$rejected[$i]);
    	$queue_stat_string = $queue_stat_string . sprintf("%d;", $resolved[$i]+$deleted[$i]+$stalled[$i]+$rejected[$i]);
    	push @out_stats, sprintf("*** Zvetseni fronty (M13) = %d\n", $created[$i] - ($resolved[$i]+$deleted[$i]+$stalled[$i]+$rejected[$i]));
    	$queue_stat_string = $queue_stat_string . sprintf("%d;", $created[$i] - ($resolved[$i]+$deleted[$i]+$stalled[$i]+$rejected[$i]));
    	if ($created[$i] != 0){
	    push @out_stats, sprintf("*** Pomer vyresenych pozadavku (M14) = %2.0f\%\n", ($resolved[$i]+$deleted[$i]+$stalled[$i]+$rejected[$i])/$created[$i]*100);
	    $queue_stat_string = $queue_stat_string . sprintf("%2.0f\%;", ($resolved[$i]+$deleted[$i]+$stalled[$i]+$rejected[$i])/$created[$i]*100);
	}
    	else {
	    push @out_stats, sprintf("*** Pomer vyresenych pozadavku (M14) = N/A\n");
	    $queue_stat_string = $queue_stat_string . sprintf("N/A;");
    	}
    	push @out_stats, sprintf("** Casove metriky \n");
	
    	push @out_stats, sprintf("*** Prvni odpovedi na RT (M17)\n");
    	push @out_stats, sprintf("**** Pocet prvnich odpovedi (M17a) = %d\n", $n_answer_time[$i]);
    	$queue_stat_string = $queue_stat_string . sprintf("%d;", $n_answer_time[$i]);
    	if ($n_answer_time[$i] > 0) {
	    push @out_stats, sprintf("**** Minimalni doba prvni odpovedi (M17b) = %s (RT#%s)\n", &formatTime($min_answer_time[$i]), $min_rtid[$i]);
	    $queue_stat_string = $queue_stat_string . sprintf("%s (RT#%s);", &formatTime($min_answer_time[$i]), $min_rtid[$i]);
	    push @out_stats, sprintf("**** Maximalni doba prvni odpovedi (M17c) = %s (RT#%s)\n", &formatTime($max_answer_time[$i]), $max_rtid[$i]);
	    $queue_stat_string = $queue_stat_string . sprintf("%s (RT#%s);", &formatTime($max_answer_time[$i]), $max_rtid[$i]);
	    push @out_stats,  sprintf("**** Prumerna doba prvni odpovedi (M17d) = %s\n", &formatTime($sum_answer_time[$i]/$n_answer_time[$i]));
	    $queue_stat_string = $queue_stat_string . sprintf("%s;", &formatTime($sum_answer_time[$i]/$n_answer_time[$i]));
    	}
    	else {
	    push @out_stats, sprintf("**** Minimalni doba prvni odpovedi (M17b) = N/A\n");
	    $queue_stat_string = $queue_stat_string . sprintf("N/A;");
    	    push @out_stats, sprintf("**** Maximalni doba prvni odpovedi (M17c) = N/A\n");
       	    $queue_stat_string = $queue_stat_string . sprintf("N/A;");
    	    push @out_stats,  sprintf("**** Prumerna doba prvni odpovedi (M17d) = N/A\n");
    	    $queue_stat_string = $queue_stat_string . sprintf("N/A;");
    	}

    	push @out_stats, sprintf("*** Doba cekani requestora na sve zpravy v pozadavku (M16)\n");
    	push @out_stats, sprintf("**** Pocet odpovedi (M16a) = %d\n", $n_all_answer_time[$i]);
    	$queue_stat_string = $queue_stat_string . sprintf("%d;", $n_all_answer_time[$i]);
    	if ($n_all_answer_time[$i] > 0) {
	    push @out_stats, sprintf("**** Minimalni doba odpovedi (M16b) = %s (RT#%s)\n", &formatTime($min_all_answer_time[$i]), $min_all_rtid[$i]);
	    $queue_stat_string = $queue_stat_string . sprintf("%s (RT#%s);", &formatTime($min_all_answer_time[$i]), $min_all_rtid[$i]);
	    push @out_stats, sprintf("**** Maximalni doba odpovedi (M16c) = %s (RT#%s)\n", &formatTime($max_all_answer_time[$i]), $max_all_rtid[$i]);
	    $queue_stat_string = $queue_stat_string . sprintf("%s (RT#%s);", &formatTime($max_all_answer_time[$i]), $max_all_rtid[$i]);
	    push @out_stats,  sprintf("**** Prumerna doba odpovedi (M16d) = %s\n", &formatTime($sum_all_answer_time[$i]/$n_all_answer_time[$i]));
	    $queue_stat_string = $queue_stat_string . sprintf("%s;", &formatTime($sum_all_answer_time[$i]/$n_all_answer_time[$i]));
    	}
    	else {
	    push @out_stats, sprintf("**** Minimalni doba odpovedi (M16b) = N/A\n");
	    $queue_stat_string = $queue_stat_string . sprintf("N/A;");
    	    push @out_stats, sprintf("**** Maximalni doba odpovedi (M16c) = N/A\n");
       	    $queue_stat_string = $queue_stat_string . sprintf("N/A;");
    	    push @out_stats,  sprintf("**** Prumerna doba odpovedi (M16d) = N/A\n");
    	    $queue_stat_string = $queue_stat_string . sprintf("N/A;");
    	}

	$i++;
    } #foreach casovy interval
           
    # je-li treba detailniho vypisu, projdou se prislusne tickety znova
    if ($show_openticket_details > 0) {
	    # Vytvoreni kolekce ticketu a jejich omezeni podminkami (limit)
	    $tickets = new RT::Tickets($RT::SystemUser);
	    
	    $tickets->LimitQueue(VALUE => $queue->Id);		#pouze analyzovana fronta
	    $tickets->LimitStatus( VALUE => 'open' );		#pouze otevrene
	    $tickets->LimitStatus( VALUE => 'new' );		#pouze nove (spojka OR)
	    #$tickets->LimitCustomField( CUSTOMFIELD => "Area", OPERATOR => "=", VALUE => "Namety");
	    #$tickets->LimitCustomField( CUSTOMFIELD => "Area", OPERATOR => "=", VALUE => "Area/Incidenty");
	    #$tickets->LimitCustomField( CUSTOMFIELD => "Area", OPERATOR => "NOT LIKE", VALUE => "Namety");

	    # prochazeni kolekce ticketu (vzdy zacne od zacatku, tj. lze spustit nekolikrat po sobe)
	    while (my $ticket = $tickets->Next) {
	    
	  	print "  #", $ticket->EffectiveId(), " ", $ticket->Subject, "\n";

		#zjisteni, zda je ticket interni zalezitost CIV 
		my $external = &isExternal($ticket->RequestorAddresses, @internal_emails);
		if ($external == 1) {
			print "    External ticket\n";
		}
		else {
			print "    Internal ticket\n";
		}

		#zjisteni stavu
		print "    Status = ", $ticket->Status, "\n";

		#zjisteni majitele ticketu (kdo jej vlastni)
		my $Owner = RT::User->new($RT::SystemUser);		
		$Owner->Load($ticket->Owner);
		print "    Owner = ", $Owner->EmailAddress, "\n";
	
		#zjisteni kdo ticket vytvoril (CIV / zbytek sveta)
		my $Creator = RT::User->new($RT::SystemUser);		
		$Creator->Load($ticket->Creator);
		print "    Creator = ", $Creator->EmailAddress, "\n";
				
		#zjisteni requestoru, admin cc a cc
		print "    AdminCCs = ", $ticket->AdminCcAddresses, "\n";
		print "    CCs = ", $ticket->CcAddresses, "\n";
		print "    Requestors = ", $ticket->RequestorAddresses, "\n";

		#my $cf = RT::CustomField->new( $RT::SystemUser );
		#$cf->Load('Area');
		#my $cf_id = $cf->id;
		
		#my $CustomFieldValues = new RT::CustomFieldValues($RT::SystemUser);
		#$CustomFieldValues->LimitToCustomField ($cf_id);
		#while(my $CFV = $CustomFieldValues->Next) {
		#	print "        ", $CFV->Name, "\n";
		#}
		
		#my $cf_obj = RT::CustomField->new($RT::SystemUser);
		#my $cf_name = "Area"; 
		#$cf_obj->LoadByName( Name => $cf_name ); 
		#my $cf_values = $cf_obj->Values();
		#while(my $value = $cf_values->Next){
		#		print "       " . $value->Name() . "--" . $value->Id() . "--" . $value->SortOrder() . "--" .  "\n";
		#}
				
		#analyza jednotlivych transakci
		my $Transactions = new RT::Transactions($RT::SystemUser);
		$Transactions->LimitToTicket($ticket->Id);
		my $lastSent = "";
		my $lastArea = "";
		print "    Transactions:\n";
		while(my $Transaction = $Transactions->Next) {
			#if(($Transaction->Type eq "Create") || ($Transaction->Type eq "Correspond") || ($Transaction->Type eq "Comment"))  {
				my $trUser = new RT::User($RT::SystemUser);
				$trUser->Load($Transaction->Creator);
	            		$lastSent = $trUser->EmailAddress;
				print "      ", &formatDate($Transaction->CreatedObj->Unix), "  ", $lastSent, " (", $Transaction->Type, ")\n";
				#if ($Transaction->Type eq "Status"){
					print "        ", $Transaction->Description,"\n";
				#}
			#}
			if ($Transaction->Type eq "CustomField"){
				$Transaction->Description =~ /Area /;
				$lastArea = $';
				$lastArea =~ / added by/;
				$lastArea = $`;
			}
		}
		#custom-fields
		print "    Custom Fields Area = ", $lastArea, "\n";

		#zjisteni linku na dalsi RT - TODO
		my $tickets_tmp = new RT::Tickets($RT::SystemUser);
		$tickets_tmp->LimitLinkedFrom(TYPE => "RefersTo", BASE => $ticket->EffectiveId());
		#$links->LimitReferredToBy($ticket->EffectiveId());
		print "    Links:\n";
		print "      Referes to:\n";
		while(my $ticket_tmp = $tickets_tmp->Next) {
			print "        #", $ticket_tmp->EffectiveId(), " ", $ticket_tmp->Subject, "\n";
		}

		$tickets_tmp->LimitLinkedTo(TYPE => "RefersTo", TARGET => $ticket->EffectiveId());
		#$links->LimitReferredToBy($ticket->EffectiveId());
		print "      Links:\n";
		print "      Referred to by:\n";
		while(my $ticket_tmp = $tickets_tmp->Next) {
			print "        #", $ticket_tmp->EffectiveId(), " ", $ticket_tmp->Subject, "\n";
		}
	
		$tickets_tmp = new RT::Tickets($RT::SystemUser);
		$tickets_tmp->LimitLinkedFrom(TYPE => "DependsOn", BASE => $ticket->URI);
		#$links->LimitReferredToBy($ticket->EffectiveId());
		print "      DependsOn:\n";
		while(my $ticket_tmp = $tickets_tmp->Next) {
			print "        #", $ticket_tmp->EffectiveId(), " ", $ticket_tmp->Subject, "\n";
		}

		$tickets_tmp = new RT::Tickets($RT::SystemUser);
		$tickets_tmp->LimitLinkedTo(TYPE => "DependsOn", TARGET => $ticket->URI);
		#$links->LimitReferredToBy($ticket->EffectiveId());
		print "      DependsOn:\n";
		while(my $ticket_tmp = $tickets_tmp->Next) {
			print "        #", $ticket_tmp->EffectiveId(), " ", $ticket_tmp->Subject, "\n";
		}

		print "\n";
	   } #while ticket
   } # if show_openticket_details

   # ulozeni casu dokonceni zpracovani fronty
   my $time_queue_end = time;

   if ($debug_stat_time == 1){
	   print "DEBUG: Fronta ", $queue->Name, " zpracovana za ", &formatTime($time_queue_end - $time_queue_start), " (", &formatDate($time_queue_start), " - ", &formatDate($time_queue_end), "), zpracovano ", $processed_tickets, " pozadavku a ", $processed_transactions, " transakci\n";
   }

    if ($show_progress > 0){
          print &formatDate($time_queue_start), " - dokonceno zpracovani fronty ", $queue->Name, " (za ", &formatTime($time_queue_end - $time_queue_start), " ", $processed_tickets, " tiketu, ", $processed_transactions, " transakci)\n";
    }

    push @out_stats, "* Statistika zpracovani\n";
    push @out_stats, "** Doba zpracovani = " . &formatTime($time_queue_end - $time_queue_start) ."\n";
    push @out_stats, "** Zpracovane tikety = " . $processed_tickets ."\n";
    push @out_stats, "** Zpracovane transakce = " . $processed_transactions ."\n";
    $queue_stat_string = $queue_stat_string . &formatTime($time_queue_end - $time_queue_start) . ";" . $processed_tickets . ";" . $processed_transactions . ";";    

    #ulozeni radku tabulky
    push @out_csv_table, $queue_stat_string;

} #foreach queue

#vypis statistik pro jednotlive fronty
if ($show_queue_stats == 1){
	foreach my $line (@out_stats){
		print OUTFILE $line;
	}#foreach
}#if

#vypis statistik ve forme tabulky
if ($show_csv_table == 1){
	print OUTFILE "Queue;RunAt;M1;M2;M3;M4;M5;M8;M8(%);M9;M15;";
	foreach my $period (@decay_period){
		print OUTFILE "M10(" . &formatTime($period) . ");";
	}
	print OUTFILE "M6b;M6c;M6d;";
	foreach my $period (@stat_period){	
		print OUTFILE "M11(" . &formatDate($now - $period) . ");M12;M13;M14;M17a;M17b;M17c;M17d;M16a;M16b;M16c;M16d;";
	}
	print OUTFILE "Doba zpracovani;Zpracovane tikety;Zpracovane transakce;\n";
	foreach my $line (@out_csv_table){
		print OUTFILE $line, "\n";
	}#foreach
}#if

close(OUTFILE) || warn "Nelze zavrit soubor $!";

#Disconnect before we finish off
$RT::Handle->Disconnect();
exit 0;

#=======================================================================================
# Subroutines
#=======================================================================================

sub formatDate() {
	my($unixtime) = @_;
   	my(@days) = ( "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" );
    	# Return an empty string if we haven't been given a time
    	return "" if $unixtime <= 0;
    	my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($unixtime);
    	#return sprintf "%s %02d.%02d.%04d %02d:%02d:%02d", $days[$wday], $mday, $mon+1, $year % 100, $hour, $min, $sec;
    	return sprintf "%02d.%02d.%04d %02d:%02d:%02d", $mday, $mon+1, $year+1900, $hour, $min, $sec;
}

sub formatFilename() {
	my($unixtime) = @_;
   	my(@days) = ( "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" );
    	# Return an empty string if we haven't been given a time
    	return "" if $unixtime <= 0;
    	my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($unixtime);
    	#return sprintf "%s %02d.%02d.%04d %02d:%02d:%02d", $days[$wday], $mday, $mon+1, $year % 100, $hour, $min, $sec;
    	return sprintf "%04d-%02d-%02d_%02d%02d%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec;
}

sub formatTime() {
	my($unixtime) = @_;
    	# Return an empty string if we haven't been given a time
    	return "" if $unixtime <= 0;
	my $sec = $unixtime % 60;
	$unixtime = $unixtime - $sec;
	$unixtime = $unixtime / 60;
	my $min = $unixtime % 60;
	$unixtime = $unixtime - $min;
	$unixtime = $unixtime / 60;
	my $hour = $unixtime % 24;
	$unixtime = $unixtime - $hour;
	$unixtime = $unixtime / 24;
	
    	return sprintf "%3d dni %2d:%02d:%02d", $unixtime, $hour, $min, $sec;
}

sub isExternal() {
	my($requestors, @list) = @_;

	# vytazeni jednotlivych requestoru
	my @reqs = split(/,\s/, $requestors);

	# zjisteni je requestor mimo interni seznam 
	foreach my $req (@reqs){
		my $external = 1;
		foreach my $email (@list){
			if (index($req, $email) >= 0){		
			#if ($req =~ /$email/){
			   $external = 0;
		   	}
		} # foreach email
		if ($external == 1){
			return 1;  #mezi requestory je alespon jeden externista
		}
	} #foreach requestor
	return 0;
}

sub hasExternalLinks() {
	my($ticket) = @_;
	# vrati jedna pokud existuje link DependOn nebo ReffersTo na ticket s externim requestorem
	
	# seznam ticketu "RefersTo"
	my $tickets_tmp = new RT::Tickets($RT::SystemUser);
	$tickets_tmp->LimitLinkedFrom(TYPE => "RefersTo", BASE => $ticket->EffectiveId());
	while(my $ticket_tmp = $tickets_tmp->Next) {
		print "M4 #", $ticket->EffectiveId(), "RefersTo #", $ticket_tmp->EffectiveId(), " ", $ticket_tmp->Subject, "\n" if ($debug_m4 > 0);
		my $external = &isExternal($ticket_tmp->RequestorAddresses, @internal_emails);
		if ($external == 1){
			print "  M4 External requestor in ", $ticket_tmp->RequestorAddresses, "\n" if ($debug_m4 > 0);
		       	return 1;
		}
	}

	# seznam ticketu "DependsOn"
	$tickets_tmp = new RT::Tickets($RT::SystemUser);
	$tickets_tmp->LimitLinkedFrom(TYPE => "DependsOn", BASE => $ticket->URI);
	while(my $ticket_tmp = $tickets_tmp->Next) {
		print "M4 #", $ticket->EffectiveId(), "DependsOn #", $ticket_tmp->EffectiveId(), " ", $ticket_tmp->Subject, "\n" if ($debug_m4 > 0);
		my $external = &isExternal($ticket_tmp->RequestorAddresses, @internal_emails);
		if ($external == 1){
			print "  M4 External requestor in ", $ticket_tmp->RequestorAddresses, "\n" if ($debug_m4 > 0);
		       	return 1;
		}
	}
	
	return 0;	# neexistuje link na ticket s externim requestorem
}
