diff options
Diffstat (limited to 'rt/html')
19 files changed, 761 insertions, 0 deletions
diff --git a/rt/html/Callbacks/ActivityReports/Elements/Tabs/Default b/rt/html/Callbacks/ActivityReports/Elements/Tabs/Default new file mode 100644 index 000000000..f85d2e010 --- /dev/null +++ b/rt/html/Callbacks/ActivityReports/Elements/Tabs/Default @@ -0,0 +1,7 @@ +<%init> +if ($ARGS{current_toptab} eq "Tools/Offline.html") { + $ARGS{tabs}{r} ||= { path => 'Reports/Activity/index.html', + title => 'Reports', + }; +} +</%init>
\ No newline at end of file diff --git a/rt/html/Callbacks/ActivityReports/NoAuth/webrt.css/Default b/rt/html/Callbacks/ActivityReports/NoAuth/webrt.css/Default new file mode 100644 index 000000000..30480f7b6 --- /dev/null +++ b/rt/html/Callbacks/ActivityReports/NoAuth/webrt.css/Default @@ -0,0 +1,71 @@ +table.miniplot { + width: 100%; + border-collapse: collapse; +} +table.miniplot td { + margin: 0; + padding: 0; + border-bottom: 1px solid black; +} +table.miniplot .graph { + margin-left: auto; + margin-right: auto; + position: relative; + width: 60px; +} +table.miniplot .graph ul { + height: 100px; + margin: 0; + padding: 0; +} +table.miniplot .graph ul li { + list-style: none; + position: absolute; + bottom: 0px; + padding: 0 !important; + margin: 0 !important; + border-bottom: none; +} +table.miniplot .graph ul li .data { + display: none; +} + +.miniplot .demoblock { margin: 0 10px; padding: 0 30px; } + +.miniplot .c1 { border: 2px solid #990000; background: #ff0000; } +.miniplot .c2 { border: 2px solid #996600; background: #ff9900; } +.miniplot .c3 { border: 2px solid #009900; background: #00ff00; } +.miniplot .c4 { border: 2px solid #009999; background: #00ffff; } +.miniplot .c5 { border: 2px solid #000099; background: #0000ff; } +.miniplot .c6 { border: 2px solid #990099; background: #ff00ff; } +graph .c5 { border: 2px solid #000099; background: #0000ff; } +.graph .c6 { border: 2px solid #990099; background: #ff00ff; } + +tr.titlerow th { + + border-bottom: solid black 1px; + margin: 0; + font-size:80%; + text-wrap: none; + +} + +tr.grandtotal td{ + border-top: 1px solid black; +} + +tr.grandtotal th{ + border-top: 1px solid black; +} + +th.label { + align: left; + +} + +table.miniplot th.legend { + font-style: normal; + font-size: 80%; + +} + diff --git a/rt/html/Callbacks/ActivityReports/Search/Results.html/SearchActions b/rt/html/Callbacks/ActivityReports/Search/Results.html/SearchActions new file mode 100644 index 000000000..4775a9af3 --- /dev/null +++ b/rt/html/Callbacks/ActivityReports/Search/Results.html/SearchActions @@ -0,0 +1,7 @@ +<a href="<% $RT::WebPath %>/Reports/Activity/index.html?<% $QueryString %>">Generate reports</a> +<%init> +use YAML; +my %args = $m->caller_args(2); + +my $QueryString = $m->comp('/Elements/QueryString', query => $args{Query}); +</%init>
\ No newline at end of file diff --git a/rt/html/Callbacks/RT-WebCronTool/Elements/Tabs/Default b/rt/html/Callbacks/RT-WebCronTool/Elements/Tabs/Default new file mode 100644 index 000000000..db74ced2d --- /dev/null +++ b/rt/html/Callbacks/RT-WebCronTool/Elements/Tabs/Default @@ -0,0 +1,13 @@ +%# The day after tomorrow is the third day of the rest of your life. +<%INIT> +if ($session{'CurrentUser'}->UserObj->HasRight( + Right => 'SuperUser', + Object => $RT::System, +)) { + $toptabs->{'ZZ-RT-WebCronTool'} = { title =>loc("Web CronTool"), + path => "Developer/CronTool/index.html" }; +} +</%init> +<%args> +$toptabs =>undef +</%args> diff --git a/rt/html/Developer/CronTool/autohandler b/rt/html/Developer/CronTool/autohandler new file mode 100644 index 000000000..7daa09e8d --- /dev/null +++ b/rt/html/Developer/CronTool/autohandler @@ -0,0 +1,9 @@ +%# All theoretical chemistry is really physics; +%# and all theoretical chemists know it. +%# -- Richard P. Feynman +<%INIT> +$m->call_next(%ARGS) if $session{'CurrentUser'}->UserObj->HasRight( + Right => 'SuperUser', + Object => $RT::System, +); +</%INIT> diff --git a/rt/html/Developer/CronTool/index.html b/rt/html/Developer/CronTool/index.html new file mode 100644 index 000000000..67c9e5634 --- /dev/null +++ b/rt/html/Developer/CronTool/index.html @@ -0,0 +1,116 @@ +% if ($@) {
+<P><FONT Color="red"><% $@ %></FONT></P>
+% }
+% if (!$NoUI) {
+<HR>
+<FORM Action="index.html" Method="POST">
+<TABLE>
+% foreach my $class (qw( Search Condition Action )) {
+<TR><TH>
+<% loc($class) %>
+</TH><TD>
+<SELECT NAME="<% $class %>">
+% require File::Find;
+% my @modules;
+% File::Find::find(sub {
+% push @modules, $1 if /^(?!Generic|UserDefined)(\w+)\.pm$/i;
+% }, grep -d, map "$_/RT/$class", @INC);
+<OPTION <% $ARGS{$class} ? '' : 'SELECTED' %>></OPTION>
+% foreach my $module (sort @modules) {
+% my $fullname = "RT::$class\::$module";
+ <OPTION VALUE="<% $fullname %>" <% ($fullname eq $ARGS{$class}) ? 'SELECTED' : '' %>><% $module %></OPTION>
+% }
+</SELECT>
+</TD><TH>
+<&|/l&>Parameter</&>
+</TH><TD>
+<INPUT NAME="<% $class %>Arg" VALUE="<% $ARGS{$class.'Arg'} %>">
+</TD></TR>
+% }
+<TR>
+<TD COLSPAN="4" ALIGN="Right">
+<LABEL>
+<INPUT TYPE="CheckBox" NAME="Verbose" <% $Verbose ? 'CHECKED' : '' %>><&|/l&>Verbose</&>
+</LABEL>
+<INPUT TYPE="Submit" VALUE="<&|/l&>Run</&>">
+</TD>
+</TABLE>
+</FORM>
+<HR>
+% }
+<%INIT>
+$m->print("<H1>", loc("Web CronTool"), "</H1>");
+if ($Search) {
+ my $load_module = sub {
+ my $modname = $_[0];
+ $modname =~ s{::}{/}g;
+ require "$modname.pm" or die (
+ loc( "Failed to load module [_1]. ([_2])", $_[0], $@ ) . "\n"
+ );
+ };
+ $m->print(loc("Starting..."), "<UL>");
+ eval {
+ $load_module->($Search);
+ $load_module->($Action) if $Action;
+ $load_module->($Condition) if $Condition;
+
+ if ($TemplateId and !$TemplateObj) {
+ $TemplateObj = RT::Template->new($RT::Nobody);
+ $TemplateObj->LoadById($TemplateId);
+ }
+
+ my $tickets = RT::Tickets->new($RT::SystemUser);
+ my $search = $Search->new( TicketsObj => $tickets, Argument => $SearchArg );
+ $search->Prepare;
+ my $tickets_found = $search->TicketsObj;
+
+ #for each ticket we've found
+ while ( my $ticket = $tickets_found->Next ) {
+ $m->print("<LI>" . $ticket->Id . ": ") if $Verbose;
+ $m->print(loc("Checking...")) if $Verbose;
+
+ # perform some more advanced check
+ if ($Condition) {
+ my $ConditionObj = $Condition->new(
+ TicketObj => $ticket,
+ Argument => $ConditionArg
+ );
+
+ # if the condition doesn't apply, get out of here
+ next unless ( $ConditionObj->IsApplicable );
+ $m->print(loc("Condition matches...")) if $Verbose;
+ }
+
+ if ($Action) {
+ #prepare our action
+ my $ActionObj = $Action->new(
+ TicketObj => $ticket,
+ TemplateObj => $TemplateObj,
+ Argument => $ActionArg
+ );
+
+ #if our preparation, move onto the next ticket
+ next unless ( $ActionObj->Prepare );
+ $m->print(loc("Action prepared...")) if $Verbose;
+
+ #commit our action.
+ next unless ( $ActionObj->Commit );
+ $m->print(loc("Action committed.")) if $Verbose;
+ }
+ }
+ };
+ $m->print('</UL>', loc("Finished."));
+}
+</%INIT>
+<%ARGS>
+$Search => undef
+$SearchArg => undef
+$Condition => undef
+$ConditionArg => undef
+$Action => undef
+$ActionArg => undef
+$TemplateId => undef
+$TemplateObj => undef
+$Verbose => 1
+$NoUI => 0
+</%ARGS>
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> |
