diff options
author | ivan <ivan> | 2007-06-20 22:28:28 +0000 |
---|---|---|
committer | ivan <ivan> | 2007-06-20 22:28:28 +0000 |
commit | f5af4fcceb8a36c3d0885dfa197798a77de64727 (patch) | |
tree | 4a1d6ec0be2a409d83dac63a3001728d3c2306aa /rt/html/Reports/Activity | |
parent | 732703b42a01b98fe6e3a8d032e173d69b48c4e8 (diff) |
integrate RTx::Statistics package, part of merging spiritone RT changes (#1661)
Diffstat (limited to 'rt/html/Reports/Activity')
-rw-r--r-- | rt/html/Reports/Activity/ActivityDetail.html | 83 | ||||
-rw-r--r-- | rt/html/Reports/Activity/ActivitySummary.html | 61 | ||||
-rw-r--r-- | rt/html/Reports/Activity/Elements/LimitReport | 23 | ||||
-rw-r--r-- | rt/html/Reports/Activity/Elements/MiniPlot | 57 | ||||
-rw-r--r-- | rt/html/Reports/Activity/Elements/PrintFooter | 7 | ||||
-rw-r--r-- | rt/html/Reports/Activity/Elements/PrintHeader | 32 | ||||
-rw-r--r-- | rt/html/Reports/Activity/Elements/ScreenFooter | 13 | ||||
-rw-r--r-- | rt/html/Reports/Activity/Elements/ScreenHeader | 8 | ||||
-rw-r--r-- | rt/html/Reports/Activity/Elements/Tabs | 52 | ||||
-rw-r--r-- | rt/html/Reports/Activity/Elements/Wrapper | 16 | ||||
-rw-r--r-- | rt/html/Reports/Activity/ResolutionComments.html | 62 | ||||
-rw-r--r-- | rt/html/Reports/Activity/ResolutionStatistics.html | 95 | ||||
-rw-r--r-- | rt/html/Reports/Activity/index.html | 29 |
13 files changed, 538 insertions, 0 deletions
diff --git a/rt/html/Reports/Activity/ActivityDetail.html b/rt/html/Reports/Activity/ActivityDetail.html new file mode 100644 index 000000000..ef0d830f7 --- /dev/null +++ b/rt/html/Reports/Activity/ActivityDetail.html @@ -0,0 +1,83 @@ +<&|Elements/Wrapper, %ARGS, title => loc("Activity detail"), + path => "Reports/Activity/ActivityDetail.html", + &> + +<& Elements/MiniPlot, data => \%counts &> + +<table style="width: 100%"> +<tr class="titlerow"> +<th>Queue</th><th>Activity</th><th>Date</th><th>Time</th><th>Ticket #</th><th>User</th><th>Short description</th> +</tr> +% for my $item (@items) { +<tr> +<td><% $item->{queue} %></td> +<td><% $item->{status} %></td> +<td><% $item->{date} %></td> +<td><% $item->{time} %></td> +<td><% $item->{id} %></td> +<td><% $item->{actor} %></td> +<td><% $item->{notes} %></td> +</tr> +% } +</table> + +</&> +<%args> +$query => 'id > 0' +$start => "2005/01/01" +$end => "2006/01/01" +</%args> +<%init> + + +my $summary_tickets = RT::Tickets->new($session{'CurrentUser'}); +$summary_tickets->FromSQL($query . " AND ( Updated >= '$start' AND Updated <= '$end')"); +my %counts; +while (my $ticket = $summary_tickets->Next) { + my $txns = $ticket->Transactions; + $txns->Limit(FIELD => 'Created', OPERATOR => '>=', VALUE => $start); + $txns->Limit(FIELD => 'Created', OPERATOR => '<=', VALUE => $end); + # I think they really don't just want status changes + $txns->Limit(FIELD => 'Type', VALUE => 'Status', ENTRYAGGREGATOR => 'OR'); + $txns->Limit(FIELD => 'Type', VALUE => 'Create'); + + while (my $txn = $txns->Next){ + my $date = substr($txn->Created, 0, 10); + # we don't have data on the status of a new ticket, default to 'new' + $counts{$date}{$txn->NewValue || 'new'}++; + } +} + + +my $tickets = RT::Tickets->new($session{'CurrentUser'}); +$tickets->FromSQL($query); +my @items; +while (my $ticket = $tickets->Next) { + my $txns = $ticket->Transactions; + $txns->Limit(FIELD => 'Created', OPERATOR => '>=', VALUE => $start); + $txns->Limit(FIELD => 'Created', OPERATOR => '<=', VALUE => $end); + # I think they really don't just want status changes + $txns->Limit(FIELD => 'Type', VALUE => 'Status', ENTRYAGGREGATOR => 'OR'); + $txns->Limit(FIELD => 'Type', VALUE => 'Create'); + + while (my $txn = $txns->Next) { + push @items, { queue => $txn->TicketObj->QueueObj->Name, + id => $txn->TicketObj->id, + date => (split ' ', $txn->CreatedObj->ISO)[0], + time => (split ' ', $txn->CreatedObj->ISO)[1], + status => $txn->NewValue || 'new', + actor => $txn->CreatorObj->Name, + notes => ($txn->Content ne 'This transaction appears to have no content' ? substr($txn->Content, 0, 60) : $txn->BriefDescription) + }; + } +} + +@items = sort { + $a->{queue} cmp $b->{'queue'} + || $a->{'status'} cmp $b->{'status'} + || $a->{'id'} <=> $b->{'id'} + || $a->{'actor'} cmp $b->{'actor'} + || $a->{'notes'} <=> $b->{'notes'} +} @items; + +</%init> diff --git a/rt/html/Reports/Activity/ActivitySummary.html b/rt/html/Reports/Activity/ActivitySummary.html new file mode 100644 index 000000000..7bb756fbc --- /dev/null +++ b/rt/html/Reports/Activity/ActivitySummary.html @@ -0,0 +1,61 @@ +<&|Elements/Wrapper, %ARGS, title => loc("Activity summary"), + path => "Reports/Activity/ActivitySummary.html", + &> + +<& Elements/MiniPlot, data => \%queues &> + +<table style="width: 100%"> +<tr class="titlerow"> +<th>Queue</th> +% for my $status (sort keys %status) { +<th><% $status %></th> +% } +<th>Total</th> +</tr> +% for my $queue (sort keys %queues) { +<th class="label"><% $queue %></th> +% for my $status (sort keys %status) { +<td><% $queues{$queue}{$status} || 0 %> +% } +<td><% $total{$queue} %></td> +</tr> +% } +<tr class="grandtotal"> +<th class="label" >Grand Total</th> +% for my $status (sort keys %status) { +<td><% $status{$status} %></td> +% } +<td><% $total %></td> +</table> +</&> +<%args> +$query => 'id > 0' +$start => "2005/01/01" +$end => "2006/01/01" +</%args> +<%init> + +my $tickets = RT::Tickets->new($session{'CurrentUser'}); +$tickets->FromSQL($query . " AND ( Updated >= '$start' AND Updated <= '$end')"); + +my %queues; +my %status; +my %total; +my $total; +while (my $ticket = $tickets->Next) { + my $txns = $ticket->Transactions; + $txns->Limit(FIELD => 'Created', OPERATOR => '>=', VALUE => $start); + $txns->Limit(FIELD => 'Created', OPERATOR => '<=', VALUE => $end); + $txns->Limit(FIELD => 'Type', VALUE => 'Status', ENTRYAGGREGATOR => 'OR'); + $txns->Limit(FIELD => 'Type', VALUE => 'Create'); + + while (my $txn = $txns->Next) { + $queues{$txn->TicketObj->QueueObj->Name}{$txn->NewValue || 'new'}++; + $status{$txn->NewValue || 'new'}++; + $total{$txn->TicketObj->QueueObj->Name}++; + $total++; + } +} + + +</%init> diff --git a/rt/html/Reports/Activity/Elements/LimitReport b/rt/html/Reports/Activity/Elements/LimitReport new file mode 100644 index 000000000..7c4aac73b --- /dev/null +++ b/rt/html/Reports/Activity/Elements/LimitReport @@ -0,0 +1,23 @@ +<form action="index.html" method="POST" enctype="multipart/form-data"> +Query: +<textarea name="query" rows="5" cols="80"><% $query %></textarea><br /> + +Report type: <select name="type"> +<option value="ActivityDetail" <% $ARGS{path} =~ /ActivityDetail/ ? 'selected' : '' %>>Activity detail</option> +<option value="ActivitySummary" <% $ARGS{path} =~ /ActivitySummary/ ? 'selected' : '' %>>Activity summary</option> +<option value="ResolutionComments" <% $ARGS{path} =~ /ResolutionComments/ ? 'selected' : '' %>>Resolution comments</option> +<option value="ResolutionStatistics" <% $ARGS{path} =~ /ResolutionStatistics/ ? 'selected' : '' %>>Resolution statistics</option> +</select><br /> + +Start date: <input type="text" name="start" value="<% $start %>" /><br /> +End date: <input type="text" name="end" value="<% $end %>" /><br /> +<& /Elements/Submit, Label => loc('Report') &> +</form> +<%args> +$type => undef +$start => undef +$end => undef +$query => undef +</%args> +<%init> +</%init> diff --git a/rt/html/Reports/Activity/Elements/MiniPlot b/rt/html/Reports/Activity/Elements/MiniPlot new file mode 100644 index 000000000..f92032818 --- /dev/null +++ b/rt/html/Reports/Activity/Elements/MiniPlot @@ -0,0 +1,57 @@ +<table class="miniplot"><tr> +% for my $major (@major) { +<td><div class="graph"> + <ul> +% my $i = 0; +% for my $minor (@minor) { +% my $percent = int( 100 * ($data->{$major}{$minor} || 0) / $max ); + <li class="c<% ($i % 6) + 1%>" style="width: <% $barwidth %>%; + left: <% $baroffset + $each * $i %>%; + height: <% $percent %>%;"><div class="data"><% $minor %>: <% $percent %>%</div></li> +% $i++; +% } + </ul> +</div></td> +% } +</tr><tr> +% for my $major (@major) { +<th class="legend"><% $major %></th> +% } +</tr> +</table> + +<table class="miniplot"><tr> +% my $i = 0; +% for my $minor (@minor) { +<th><span class="demoblock c<% ($i++ % 6) + 1 %>"></span> <% $minor %></th> +% } +</tr> +</table> + +<%args> +$data +$major => undef +$minor => undef +</%args> +<%init> + +my $max = 1; + +my %minor; +for my $major (keys %{$data}) { + for (keys %{$data->{$major}}) { + $minor{$_}++; + $max = $data->{$major}{$_} if $data->{$major}{$_} > $max; + } +} + +my @major = $major ? @{$major} : sort keys %{$data}; +my @minor = $minor ? @{$minor} : sort keys %minor; + +return unless @minor and @major; + +my $each = int( (100 / @minor) ); +my $barwidth = int( (100 / @minor) * (3/4) ); +my $baroffset = int( (100 / @minor) * (1/8) ); + +</%init> diff --git a/rt/html/Reports/Activity/Elements/PrintFooter b/rt/html/Reports/Activity/Elements/PrintFooter new file mode 100644 index 000000000..fa9f47582 --- /dev/null +++ b/rt/html/Reports/Activity/Elements/PrintFooter @@ -0,0 +1,7 @@ +<hr/> +<div style="text-align: center;"> +<%$RT::ReportFooterMessage || 'Proprietary and Confidential' %> +</div> +</body> +</html> +%$m->abort(); diff --git a/rt/html/Reports/Activity/Elements/PrintHeader b/rt/html/Reports/Activity/Elements/PrintHeader new file mode 100644 index 000000000..b7c4b3419 --- /dev/null +++ b/rt/html/Reports/Activity/Elements/PrintHeader @@ -0,0 +1,32 @@ +<%args> +$title => undef +$path => undef +$query => undef +</%args> +<HTML> +<HEAD> +<TITLE><%$title%></TITLE> +<link rel="shortcut icon" href="<%$RT::WebImagesURL%>/favicon.png" type="image/png" /> +<link media="all" rel="stylesheet" href="<%$RT::WebPath%>/NoAuth/webrt.css" type="text/css" /> +<link media="print" rel="stylesheet" href="<%$RT::WebPath%>/NoAuth/printrt.css" type="text/css" /> +%# XXX TODO THIS SHOULD NOT BE A TABLE +<body> +<table width="100%"> +<tr> +<td align="left"> +<div id="username">User: <%$session{'CurrentUser'}->Name%></div> +<div id="reportdate"> +%my $d= RT::Date->new($session{'CurrentUser'}); $d->SetToNow; +<%$d->AsString%></div> +</td> +<td align="center"> +<h1><%$title%></h1> +</td> +<td align="right"> +<img src="<%$RT::LogoURL%>" alt="RT Logo"/> +</td> +</tr> +</table> +<hr/> +<&|/l&>Report criteria:</&> <%$query%> +<hr /> diff --git a/rt/html/Reports/Activity/Elements/ScreenFooter b/rt/html/Reports/Activity/Elements/ScreenFooter new file mode 100644 index 000000000..235b7b306 --- /dev/null +++ b/rt/html/Reports/Activity/Elements/ScreenFooter @@ -0,0 +1,13 @@ +<& LimitReport, %ARGS &> +% if ($show_print_link) { +<div align="right"> +% my %printable_args = %ARGS; +% delete $printable_args{$_} for (qw/path title mode/); +% $printable_args{'mode'} = 'print'; +% my $url = $ARGS{'path'} .'?'. join(';', map { $_."=".$printable_args{$_} } keys %printable_args); +<a href="<%$RT::WebPath|n%>/<%$url|n%>"><&|/l&>Printable version</&></a> +</div> +% } +<%args> +$show_print_link => 1 +</%args> diff --git a/rt/html/Reports/Activity/Elements/ScreenHeader b/rt/html/Reports/Activity/Elements/ScreenHeader new file mode 100644 index 000000000..080efc0dd --- /dev/null +++ b/rt/html/Reports/Activity/Elements/ScreenHeader @@ -0,0 +1,8 @@ +<%args> +$title => undef +$path => undef +</%args> +<& /Elements/Header, Title => $title &> +<& Tabs, + current_subtab => $path, + Title => $title &> diff --git a/rt/html/Reports/Activity/Elements/Tabs b/rt/html/Reports/Activity/Elements/Tabs new file mode 100644 index 000000000..a9498209e --- /dev/null +++ b/rt/html/Reports/Activity/Elements/Tabs @@ -0,0 +1,52 @@ +<& /Elements/Tabs, + tabs => $tabs, + subtabs => $subtabs, + current_toptab => 'Tools/Offline.html', + current_tab => 'Reports/Activity/index.html'.$args, + Title => $Title &> + +<%INIT> +my $subtabs = {}; + +my $top = $m->caller_args(-1); +my $args = "?" . $m->comp( '/Elements/QueryString', + query => $top->{query}, + start => $top->{start}, + end => $top->{end}); +if ($m->caller_args(-1)->{'query'}) { + $current_subtab .= $args; + $subtabs = { + a => { title => 'Activity detail', + path => 'Reports/Activity/ActivityDetail.html'.$args, + }, + b => { title => 'Activity summary', + path => 'Reports/Activity/ActivitySummary.html'.$args, + }, + c => { title => 'Resolution comments', + path => 'Reports/Activity/ResolutionComments.html'.$args, + }, + d => { title => 'Resolution statistics', + path => 'Reports/Activity/ResolutionStatistics.html'.$args, + }, + }; +} + +my $tabs = { + a => { title => loc('Offline'), + path => 'Tools/Offline.html', + }, + r => { title => loc('Reports'), + path => 'Reports/Activity/index.html'.$args, + subtabs => $subtabs, + current_subtab => $current_subtab, + } + }; + +</%INIT> + + +<%ARGS> +$current_tab => undef +$current_subtab => undef +$Title => undef +</%ARGS> diff --git a/rt/html/Reports/Activity/Elements/Wrapper b/rt/html/Reports/Activity/Elements/Wrapper new file mode 100644 index 000000000..6f81f5f50 --- /dev/null +++ b/rt/html/Reports/Activity/Elements/Wrapper @@ -0,0 +1,16 @@ +<%args> +$mode => 'screen' +</%args> + +% if ($mode eq 'print') { +<& PrintHeader, %ARGS &> +%} else { +<& ScreenHeader, %ARGS &> +% } +<%$m->content |n%> +% if ($mode eq 'print') { +<& PrintFooter, %ARGS &> +%} else { +<& ScreenFooter, %ARGS &> +% } + diff --git a/rt/html/Reports/Activity/ResolutionComments.html b/rt/html/Reports/Activity/ResolutionComments.html new file mode 100644 index 000000000..81ca301cc --- /dev/null +++ b/rt/html/Reports/Activity/ResolutionComments.html @@ -0,0 +1,62 @@ +<&|Elements/Wrapper, %ARGS, title => loc("Resolution Comments"), + path => "Reports/Activity/ResolutionComments.html", + &> + +<table style="width: 100%"> +<tr> +<th>Queue</th><th>Ticket #</th><th>Created</th><th>Resolved</th><th>Time to resolve</th> +</tr> +<tr> +<th colspan="5">Resolution comments</th> +</tr> +% for my $item (@items) { +<tr class="titlerow"> +<td><% $item->{queue} %></td> +<td><% $item->{id} %></td> +<td><% $item->{created} %></td> +<td><% $item->{resolved} %></td> +<td><% $item->{duration} %></td> +</tr> +<tr> +<td colspan="5"><% $item->{whiteboard} %></td> +</tr> +% } +</table> +</&> + +<%args> +$query => 'id > 0' +$start => "2005/01/01" +$end => "2006/01/01" +</%args> +<%init> + +use Time::Duration; + +my $summary_tickets = RT::Tickets->new( $session{'CurrentUser'} ); +$summary_tickets->FromSQL( + $query . " AND (Status = 'resolved') AND ( Updated >= '$start' AND Updated <= '$end')" ); + +my @items; +while ( my $ticket = $summary_tickets->Next ) { + push @items, { + queue => $ticket->QueueObj->Name, + id => $ticket->id, + created => $ticket->CreatedObj->AsString, + resolved => $ticket->ResolvedObj->AsString, + duration => Time::Duration::concise( + Time::Duration::duration( + $ticket->ResolvedObj->Unix - $ticket->CreatedObj->Unix + ) + ), + whiteboard => $ticket->FirstCustomFieldValue('Whiteboard') + }; +} + +@items = sort { $a->{queue} cmp $b->{queue} || $a->{id} <=> $b->{id} } @items; + + + + + +</%init> diff --git a/rt/html/Reports/Activity/ResolutionStatistics.html b/rt/html/Reports/Activity/ResolutionStatistics.html new file mode 100644 index 000000000..4ecde2c82 --- /dev/null +++ b/rt/html/Reports/Activity/ResolutionStatistics.html @@ -0,0 +1,95 @@ +<&|Elements/Wrapper, %ARGS, title => loc("Resolution statistics"), + path => "Reports/Activity/ResolutionStatistics.html", + &> + +<& Elements/MiniPlot, + data => \%plot, + major => ['Date range','Last 30 days','Last 60 days','Last 90 days','Ever'], + minor => [(sort keys %queues), "Average"] + &> + +<table style="width: 100%"> +<tr> +<td></td><th colspan="4">Number of tickets closed / Average resolution time per ticket</th> +</tr> +<tr class="titlerow"> +<th>Queue</th> +<th>Date range</th> +<th>Last 30 days</th> +<th>Last 60 days</th> +<th>Last 90 days</th> +<th>Ever</th> +</tr> +% for my $queue (sort keys %queues) { +<tr> +<th><% $queue %></th> +% for my $period ('Date range','Last 30 days','Last 60 days','Last 90 days','Ever') { +<td><% scalar @{$closed{$period}{$queue}} %> / <% $average_resolve_times{$period}{$queue} %></td> +% } +</tr> +% } +<tr class="grandtotal"> +<th>Ticket average</th> +% for my $period ('Date range','Last 30 days','Last 60 days','Last 90 days','Ever') { +<td><% $average_resolve_times{$period}{_all_count} %> / <% $average_resolve_times{$period}{_all} %></td> +% } +</tr> +</table> + +</&> +<%args> +$query => 'id > 0' +$start => "2005/01/01" +$end => "2006/01/01" +</%args> +<%init> + +my $in_30_days = RT::Date->new($session{'CurrentUser'}); +$in_30_days->Set(Format => 'Unix', Value => ( time - (86400*30))); +my $in_60_days = RT::Date->new($session{'CurrentUser'}); +$in_60_days->Set(Format => 'Unix', Value => ( time - (86400*60))); +my $in_90_days = RT::Date->new($session{'CurrentUser'}); +$in_90_days->Set(Format => 'Unix', Value => ( time - (86400*90))); + +my %queries; +$queries{'Date range'} = "(Resolved >= '$start' AND Resolved <= '$end')"; +$queries{'Last 30 days'} = "(Resolved >= '".$in_30_days->ISO."')"; +$queries{'Last 60 days'} = "(Resolved >= '".$in_60_days->ISO."')"; +$queries{'Last 90 days'} = "(Resolved >= '".$in_90_days->ISO."')"; +$queries{'Ever'} = "(Status = 'resolved' OR Status = 'rejected')"; + + +my %closed; +my %queues; +foreach my $period (keys %queries) { + my $tix = RT::Tickets->new($session{'CurrentUser'}); + $tix->FromSQL($query . " AND " .$queries{$period}); + + while (my $ticket = $tix->Next) { + push @{ $closed{$period}{$ticket->QueueObj->Name}}, $ticket; + $queues{$ticket->QueueObj->Name}++; + } +} + +my %restimes; +my %average_resolve_times; +my %plot; +use Time::Duration; +foreach my $period ( keys %closed ) { + foreach my $queue ( keys %{$closed{$period}} ) { + foreach my $ticket (@{$closed{$period}{$queue}} ) { + push @{$restimes{$period}{$queue}}, ( $ticket->ResolvedObj->Unix - $ticket->CreatedObj->Unix); + } + + my $total_time = 0; + $total_time+= $_ for @{$restimes{$period}{$queue}}; + $average_resolve_times{$period}{'_all_time'} += $total_time; + $average_resolve_times{$period}{'_all_count'} += @{$restimes{$period}{$queue}}; + $plot{$period}{$queue} = $total_time / @{$restimes{$period}{$queue}}; + $average_resolve_times{$period}{$queue} = Time::Duration::concise(Time::Duration::duration($plot{$period}{$queue})); + } + $plot{$period}{Average} = $average_resolve_times{$period}{'_all_time'} / $average_resolve_times{$period}{'_all_count'}; + $average_resolve_times{$period}{'_all'} = Time::Duration::concise(Time::Duration::duration($plot{$period}{Average})); +} + +</%init> diff --git a/rt/html/Reports/Activity/index.html b/rt/html/Reports/Activity/index.html new file mode 100644 index 000000000..1f6ddb0d5 --- /dev/null +++ b/rt/html/Reports/Activity/index.html @@ -0,0 +1,29 @@ +<&| Elements/Wrapper, %ARGS, title => loc("Activity reports"), show_print_link => 0 &> + + +</&> + +<%args> +$type => undef +$start => undef +$end => undef +$query => "Status = 'resolved'" +</%args> +<%init> + +unless ($start) { + my $then = RT::Date->new($session{'CurrentUser'}); + $then->Set(Format => 'Unix', Value => time - (86400*7)); + $ARGS{start} = substr($then->ISO,0,10); +} + +unless ($end) { + my $now = RT::Date->new($session{'CurrentUser'}); + $now->SetToNow(); + $ARGS{end} = substr($now->ISO,0,10); +} + +if ($type) { + $m->redirect($type . ".html?" . $m->comp('/Elements/QueryString', query => $query, start => $start, end => $end)); +} +</%init> |