RTx-Statistics in 2.1 / 3.8
authorivan <ivan>
Sun, 5 Dec 2010 00:06:28 +0000 (00:06 +0000)
committerivan <ivan>
Sun, 5 Dec 2010 00:06:28 +0000 (00:06 +0000)
25 files changed:
rt/share/html/RTx/Statistics/CallsMultiQueue/Elements/Chart [new file with mode: 0755]
rt/share/html/RTx/Statistics/CallsMultiQueue/index.html [new file with mode: 0755]
rt/share/html/RTx/Statistics/CallsQueueDay/Elements/Chart [new file with mode: 0755]
rt/share/html/RTx/Statistics/CallsQueueDay/Results.tsv [new file with mode: 0644]
rt/share/html/RTx/Statistics/CallsQueueDay/index.html [new file with mode: 0755]
rt/share/html/RTx/Statistics/DayOfWeek/Elements/Chart [new file with mode: 0755]
rt/share/html/RTx/Statistics/DayOfWeek/index.html [new file with mode: 0755]
rt/share/html/RTx/Statistics/DurationAsString [new file with mode: 0755]
rt/share/html/RTx/Statistics/Elements/DateSelectRow [new file with mode: 0644]
rt/share/html/RTx/Statistics/Elements/DurationAsString [new file with mode: 0755]
rt/share/html/RTx/Statistics/Elements/GraphBox [new file with mode: 0644]
rt/share/html/RTx/Statistics/Elements/SelectMultiQueue [new file with mode: 0755]
rt/share/html/RTx/Statistics/Elements/StatColumnMap [new file with mode: 0644]
rt/share/html/RTx/Statistics/Elements/Tabs [new file with mode: 0755]
rt/share/html/RTx/Statistics/FAQ/index.html [new file with mode: 0644]
rt/share/html/RTx/Statistics/OpenStalled/Elements/Chart [new file with mode: 0755]
rt/share/html/RTx/Statistics/OpenStalled/Results.tsv [new file with mode: 0644]
rt/share/html/RTx/Statistics/OpenStalled/index.html [new file with mode: 0755]
rt/share/html/RTx/Statistics/Resolution/Elements/Chart [new file with mode: 0755]
rt/share/html/RTx/Statistics/Resolution/index.html [new file with mode: 0644]
rt/share/html/RTx/Statistics/TimeToResolve/Elements/Chart [new file with mode: 0755]
rt/share/html/RTx/Statistics/TimeToResolve/index.html [new file with mode: 0755]
rt/share/html/RTx/Statistics/UserTest/Elements/Chart [new file with mode: 0755]
rt/share/html/RTx/Statistics/UserTest/index.html [new file with mode: 0755]
rt/share/html/RTx/Statistics/index.html [new file with mode: 0755]

diff --git a/rt/share/html/RTx/Statistics/CallsMultiQueue/Elements/Chart b/rt/share/html/RTx/Statistics/CallsMultiQueue/Elements/Chart
new file mode 100755 (executable)
index 0000000..02a183b
--- /dev/null
@@ -0,0 +1,39 @@
+<%perl>
+$r->content_type("image/$format");
+print $graph->plot(\@data)->$format();
+$m->abort();
+</%perl>
+<em><&|/l, $#data+1&>[_1] Plot Elements</&></em><p>
+% foreach my $value (@data) {
+<% $value %><p>
+% }
+<em><&|/l&>x_labels</&>:</em><p>
+<% $ARGS{x_labels} %>
+<p>
+<em><&|/l&>legend</&>:</em><p>
+<% $ARGS{set_legend} %>
+<p>
+<em><&|/l, (keys %ARGS) - 2&>[_1] data sets</&>:</em><p>
+
+% for (1..(scalar keys %ARGS)-2) {
+<% $_ %> <% $ARGS{"data$_"} %><p>
+% }
+
+<%INIT>
+use GD::Graph::lines;
+
+my @data;
+my $graph = GD::Graph::lines->new($Statistics::GraphWidth,$Statistics::GraphHeight);
+$graph->set(export_format => "png",
+            x_label       => 'Day of Week',
+            y_label       => 'Tickets per day');
+$graph->set_legend(split /,/ , $ARGS{set_legend});
+my $format = $graph->export_format;
+push @data, [split /,/ , $ARGS{x_labels}];
+for (1..((scalar keys %ARGS)-2)) {
+  push @data, [split /,/  , $ARGS{"data".$_}];
+}
+
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/share/html/RTx/Statistics/CallsMultiQueue/index.html b/rt/share/html/RTx/Statistics/CallsMultiQueue/index.html
new file mode 100755 (executable)
index 0000000..abf8aa7
--- /dev/null
@@ -0,0 +1,330 @@
+<& /Elements/Header, Title => loc('Tickets per day in Multiple queues') &>
+<& /RTx/Statistics/Elements/Tabs, Title => loc('Tickets per day in Multiple Queues by status') &>
+
+<h3>Description</h3>
+<p>This chart shows details of tickets per day by their status. You can select multiple queues to display at the same time, but only one status. You can chose any of the defined status values. 
+There is also the option to display all available queues at the same time.
+The default display shows tickets resolved in your default queue (General unless altered locally).
+The line chart below shows the same information in a graphical form.
+
+<br />
+
+<form method="POST" action="index.html">
+
+%# Build Legend
+% my @legend;
+% for (sort keys %queues_to_show) {
+%   push @legend, $_;
+% }
+
+%my $title = "Tickets with Status $status in " . join(', ', @queues) . ", per day from " .
+%        Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[0]) . " through " .
+%        Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$#dates-1]);
+
+<& /Elements/TitleBoxStart, title => $title, title_href => "/RTx/Statistics/OpenStalled/index.html?$QueryString"&>
+<TABLE BORDER=0 cellspacing=0 cellpadding=1 WIDTH="100%">
+% if ($ShowHeader) {
+<& /RTx/Statistics/Elements/CollectionAsTable/Header, 
+    Format => \@RowFormat, 
+    FormatString => $RowFormat,
+    AllowSorting => $AllowSorting, 
+    Order => $Order, 
+    Query => undef,
+    Rows => $Rows,
+    Page => $Page,
+    OrderBy => $OrderBy , 
+    BaseURL => $BaseURL,
+    maxitems => $maxitems &> 
+% }
+% my $line = 0;
+% LINE: for my $d (0..$#dates) {
+%   if ($d == $#dates ){
+%     next LINE;
+%   }
+%   $line++;
+%   my $x = 1;
+%   $values{Statistics_Date} = Statistics::FormatDate($dateformat, $dates[$d]);
+%   my $row_total=0;
+%   foreach my $q (sort keys %queues_to_show) {
+%     my $tix = new RT::Tickets($session{'CurrentUser'});
+%     if ($status eq "resolved") {
+%       $tix->LimitStatus(VALUE => $status);
+%       $tix->LimitResolved(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+%       if ($dates[$d+1]) {
+%         $tix->LimitResolved(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+%       }
+%     } 
+%     elsif ($status eq "new") {
+%       $tix->LimitCreated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+%       if ($dates[$d+1]) {
+%         $tix->LimitCreated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+%       }
+%     } 
+%     elsif ($status eq "deleted") {
+%       $tix->LimitStatus(VALUE => $status);
+%       $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+%       if ($dates[$d+1]) {
+%         $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+%       }
+%     }
+%     elsif ($status eq "stalled") {
+%       $tix->LimitStatus(VALUE => $status);
+%       $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+%       if ($dates[$d+1]) {
+%         $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+%       }
+%     }
+%     elsif ($status eq "open") {
+%       $tix->LimitStatus(VALUE => $status);
+%       $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+%       if ($dates[$d+1]) {
+%         $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+%       }
+%     }
+%     elsif ($status eq "rejected") {
+%       $tix->LimitStatus(VALUE => $status);
+%       $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+%       if ($dates[$d+1]) {
+%         $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+%       }
+%     }
+%     $tix->LimitQueue (VALUE => $q);
+%     $values{$q} = $tix->Count;
+%     $row_total += $tix->Count;
+%     $data[$x++][$d] = $tix->Count;
+%   }
+%   $values{Statistics_Totals} = $row_total;
+<&  /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@RowFormat, i => $line, record => $record, maxitems => $maxitems &>
+% }
+</table>
+<& /Elements/TitleBoxEnd&>
+
+<hr>
+
+<BR />
+<BR />
+
+<!--    <td>Show:</td>
+    <td COLSPAN=2><SELECT NAME="status">
+% for (qw(resolved new deleted stalled rejected open)) {
+    <OPTION VALUE="<% $_ %>" <% $_ eq $status && "SELECTED" %>>
+    <% loc($_) %></OPTION>
+% }
+--!>
+
+<%perl>
+# Create the graph URL
+my $url = 'Elements/Chart?x_labels=';
+#$url .= join ",", @{ shift @data } . "&";
+for (0..$max) {
+     $url .=  $m->interp->apply_escapes($data[0][$_],'u') . ",";
+}
+chop $url;
+$url .= "&";
+shift @data;
+$url .=  'set_legend='.(join ",", @legend)."&";
+for (0..$#data) {
+  $url .= "data".(1+$_)."=". (join ",", @{$data[$_]})."&";
+}
+chop $url;
+</%perl>
+
+<& /RTx/Statistics/Elements/GraphBox, GraphURL => $url &>
+
+<& /RTx/Statistics/Elements/ControlsAsTable/ControlBox, 
+         Title => "Change Status, Queues or Dates", 
+         ShowDates => 1, sMonth => \$sMonth, sDay => \$sDay, sYear => \$sYear,
+                         eMonth => \$eMonth, eDay => \$eDay, eYear => \$eYear,
+                         weekends => $weekends,
+         ShowMultiQueues => 1, queues_ref => \@queues,
+        ShowStatus => 1, Status => $status
+ &>
+
+</form>
+
+<a href="<%$RT::WebPath%>/RTx/Statistics/CallsMultiQueue/index.html?<% $QueryString %>"><&|/l&>Bookmarkable link</&></a>
+%# | <a href="<%$RT::WebPath%>/RTx/Statistics/CallsMultiQueue/Results.tsv?<%$QueryString%>"><&|/l&>spreadsheet</&></a>
+<BR>
+<BR>
+
+<%ARGS>
+$status => $Statistics::MultiQueueStatus
+$max => $Statistics::MultiQueueMaxRows
+@queues => @Statistics::MultiQueueQueueList
+$weekends => $Statistics::PerDayWeekends;
+$sMonth=>undef
+$sDay=>undef
+$sYear=>undef
+$eMonth=>undef
+$eDay=>undef
+$eYear=>undef
+$days=>undef
+$dateformat => $Statistics::MultiQueueDateFormat
+$currentMonth=>undef
+
+$AllowSorting => undef
+$Order => undef
+$OrderBy => undef
+$ShowNavigation => 1
+$ShowHeader => 1
+$Rows => 50
+$Page => 1
+$BaseURL => undef
+$AddAllCheck => undef
+</%ARGS>
+
+<%INIT>
+
+use RTx::Statistics;
+use Time::Local;
+my $n = 0;
+my @data = ([]);
+my @dates;
+my @msgs;
+my $selected;
+my $diff;
+my %queues_to_show;
+my $secsPerDay=86400;
+my $sEpoch;
+my $eEpoch;
+my $QueryString;
+my $maxitems;
+my $RowFormat;
+my $BoldRowFormat;
+my %record;
+my %values;
+my $record = \%record;
+
+$record{values} = \%values;
+
+Statistics::DebugClear();
+Statistics::DebugLog("CallsQueueDay/index.html ARGS:\n");
+for my $key (keys %ARGS) {
+  Statistics::DebugLog("ARG{ $key }=" . $ARGS{$key} . "\n");
+}
+
+
+  # Handle the Add All Checkbox
+  if($AddAllCheck eq "on") {
+    $AddAllCheck = undef;
+    undef (@queues);
+    my $q=new RT::Queues($session{'CurrentUser'});
+    $q->UnLimit;
+    while (my $queue=$q->Next) {
+      next if !$queue->CurrentUserHasRight('SeeQueue');
+      push @queues, $queue->Name;
+    }
+  }
+
+  # If the user has the right to see the queue, put it into the map
+  for my $q (@queues) {
+      my $Queueobj = new RT::Queue($session{'CurrentUser'});
+      $Queueobj->Load($q);
+      next if !$Queueobj->CurrentUserHasRight('SeeQueue');
+      $queues_to_show{$q} = 1;
+  }
+
+  $maxitems = (scalar @queues) + 2;
+
+  # Build the format strings
+  $RowFormat = "'__Statistics_Date__'";
+  $BoldRowFormat = "'<B>__Statistics_Date__</B>'";
+  for my $q (@queues) {
+      $RowFormat .= ",'__Statistics_Dynamic__/KEY:$q/TITLE:$q/STYLE:text-align:right;'";
+      $BoldRowFormat .= ",'<B>__Statistics_Dynamic__</B>/KEY:$q/TITLE:$q/STYLE:text-align:right;'";
+  }
+  $RowFormat .= ",'<B>__Statistics_Totals__</B>/STYLE:text-align:right;'";
+  $BoldRowFormat .= ",'<B>__Statistics_Totals__</B>/STYLE:text-align:right;'";
+  # Parse the formats into structures.
+  my (@RowFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $RowFormat);
+  my (@BoldRowFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $BoldRowFormat);
+
+if ($sDay > $Statistics::monthsMaxDay{$sMonth}) {
+  $sDay = $Statistics::monthsMaxDay{$sMonth};
+}
+
+if ($eDay > $Statistics::monthsMaxDay{$eMonth}) {
+  $eDay = $Statistics::monthsMaxDay{$eMonth};
+}
+
+if ($sYear){
+       $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear-1900);
+}
+if ($eYear){
+Statistics::DebugLog("eMonth = " . $eMonth . "\n");
+       $eEpoch = timelocal(0, 0, 0, $eDay, $eMonth, $eYear-1900);
+} else {
+        # This case happens when the page is first loaded
+       my @local = localtime(time);
+       ($eDay, $eMonth, $eYear) = ($local[3], $local[4], $local[5]);
+       $eYear += 1900; 
+       $eEpoch = timelocal(0, 0, 0, $local[3], $local[4], $local[5], $local[6], $local[7], $local[8]);
+Statistics::DebugLog("Setting eEpoch=$eEpoch from current time.\n");
+}
+
+if (($eEpoch < $sEpoch) || ($sEpoch == 0)) {
+    # We have an end, but not a start, or, overlapping.
+    
+    # if $currentMonth is set, just set the day to 1
+    if($currentMonth) {
+      # set start vars from end, but with day set to 1
+      (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($eEpoch);
+      $sDay=1;
+      $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear);
+    } else {
+      # If the user has specified how many days back to go, use that,
+      # If not, set start to configured default period before end
+      if(defined $days) {
+        $sEpoch = $eEpoch - ($days * $Statistics::secsPerDay);
+      } else {
+        $sEpoch = $eEpoch - ($Statistics::PerDayPeriod * $Statistics::secsPerDay);
+      }
+      (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($sEpoch);
+    }
+    $sYear += 1900;
+}
+
+# Compute days to chart.
+# The +1 is because we need to generate one more date. If the user
+# selected a 10 day range, we need to generate 11 days.
+$diff = int(($eEpoch - $sEpoch + $Statistics::secsPerDay - 1) / $Statistics::secsPerDay)+1;
+Statistics::DebugLog("Setting diff=$diff\n");
+
+Statistics::DebugLog("sEpoch=$sEpoch, components=" . join(',', localtime($sEpoch)) . "\n");
+Statistics::DebugLog("eEpoch=$eEpoch, components=" . join(',', localtime($eEpoch)) . "\n");
+
+# Build the new query string
+$QueryString = "queues=" . join("&queues=", @queues);
+$QueryString .= "&sDay=$sDay&sMonth=$sMonth&sYear=$sYear&eDay=$eDay&eMonth=$eMonth&eYear=$eYear&weekends=$weekends";
+
+
+
+
+# Set up the end date to be midnight(morning) of the date after the one the user wanted.
+my $endRange = $eEpoch + $Statistics::secsPerDay;
+$n = 0;
+until ($#dates == $diff) {
+    my $date = new RT::Date($session{CurrentUser});
+    $date->Set(Value=>$endRange - $n, Format => 'unix');
+    # Note: we used to adjust the time to local midnight, but
+    # none of the other date entry fields in RT seem to adjust, so we've stopped.
+    #Statistics::DebugLog("Before adjust to midnight date " . Statistics::FormatDate("%c", $date) . "\n");
+    $n+= $Statistics::secsPerDay;
+    # If we aren't showing weekends and this is one, decrement the number
+    # of days to show and skip to the next date.
+    if(!$weekends and Statistics::RTDateIsWeekend($date)) {$diff--; next;}
+    unshift @dates, $date;
+Statistics::DebugLog("pushing date " . Statistics::FormatDate("%c", $date) . "\n");
+    unshift @{ $data[0] }, Statistics::FormatDate($Statistics::PerDayLabelDateFormat, $date);
+}
+
+# We put an extra day into the lists to cover up till midnight of the next day,
+# But we don't want that to appear in the labels, so pop it off.
+pop( @{ $data[0] } );
+
+my $queue = new RT::Queues($session{CurrentUser});
+$queue->UnLimit;
+
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($queue);
+</%INIT>
diff --git a/rt/share/html/RTx/Statistics/CallsQueueDay/Elements/Chart b/rt/share/html/RTx/Statistics/CallsQueueDay/Elements/Chart
new file mode 100755 (executable)
index 0000000..9a3a505
--- /dev/null
@@ -0,0 +1,29 @@
+<%perl>
+$r->content_type("image/$format");
+print $graph->plot(\@data)->$format();
+$m->abort();
+print $#data+1 . " Elements:<p>";
+for (0..$#data) {
+print $data[$_];
+print "<p>";
+}
+</%perl>
+<%INIT>
+use GD::Graph::lines;
+
+my @data;
+my $graph = GD::Graph::lines->new($Statistics::GraphWidth,$Statistics::GraphHeight);
+$graph->set(export_format => "png",
+            x_label       => 'Day of Week',
+            y_label       => 'Tickets per Day',
+           x_labels_vertical => 1,
+       );
+my $format = $graph->export_format;
+$graph->set_legend(split /,/ , $ARGS{set_legend});
+push @data, [split /,/ , $ARGS{x_labels}];
+push @data, [split /,/ , $ARGS{data1}];
+push @data, [split /,/ , $ARGS{data2}];
+push @data, [split /,/ , $ARGS{data3}];
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/share/html/RTx/Statistics/CallsQueueDay/Results.tsv b/rt/share/html/RTx/Statistics/CallsQueueDay/Results.tsv
new file mode 100644 (file)
index 0000000..23f0c69
--- /dev/null
@@ -0,0 +1,191 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%# 
+%# COPYRIGHT:
+%#  
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC 
+%#                                          <jesse@bestpractical.com>
+%# 
+%# (Except where explicitly superseded by other copyright notices)
+%# 
+%# 
+%# LICENSE:
+%# 
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%# 
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%# 
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%# 
+%# 
+%# CONTRIBUTION SUBMISSION POLICY:
+%# 
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%# 
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%# 
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$Queue => undef
+$weekends => $Statistics::PerDayWeekends;
+$sMonth=>undef
+$sDay=>undef
+$sYear=>undef
+$eMonth=>undef
+$eDay=>undef
+$eYear=>undef
+$days=>undef
+$currentMonth=>undef
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+use Time::Local;
+my @dates;
+my $n = 0;
+my %Totals;
+my $now = new RT::Date($session{CurrentUser});
+my $sEpoch;
+my $eEpoch;
+
+if (!defined $Queue) {
+  $Queue = $Statistics::PerDayQueue;
+}
+
+if ($sDay > $Statistics::monthsMaxDay{$sMonth}) {
+  $sDay = $Statistics::monthsMaxDay{$sMonth};
+}
+
+if ($eDay > $Statistics::monthsMaxDay{$eMonth}) {
+  $eDay = $Statistics::monthsMaxDay{$eMonth};
+}
+
+if ($sYear){
+       $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear-1900);
+}
+if ($eYear){
+Statistics::DebugLog("eMonth = " . $eMonth . "\n");
+       $eEpoch = timelocal(0, 0, 0, $eDay, $eMonth, $eYear-1900);
+} else {
+        # This case happens when the page is first loaded
+       my @local = localtime(time);
+       ($eDay, $eMonth, $eYear) = ($local[3], $local[4], $local[5]);
+       $eYear += 1900; 
+       $eEpoch = timelocal(0, 0, 0, $local[3], $local[4], $local[5], $local[6], $local[7], $local[8]);
+Statistics::DebugLog("Setting eEpoch=$eEpoch from current time.\n");
+}
+
+if (($eEpoch < $sEpoch) || ($sEpoch == 0)) {
+    # We have an end, but not a start, or, overlapping.
+    
+    # if $currentMonth is set, just set the day to 1
+    if($currentMonth) {
+      # set start vars from end, but with day set to 1
+      (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($eEpoch);
+      $sDay=1;
+      $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear);
+    } else {
+      # If the user has specified how many days back to go, use that,
+      # If not, set start to configured default period before end
+      if(defined $days) {
+        $sEpoch = $eEpoch - ($days * $Statistics::secsPerDay);
+      } else {
+        $sEpoch = $eEpoch - ($Statistics::PerDayPeriod * $Statistics::secsPerDay);
+      }
+      (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($sEpoch);
+    }
+    $sYear += 1900;
+}
+
+# set content type
+$r->content_type('application/vnd.ms-excel');
+
+# Put out some data about the generation of this file
+$m->out("Tickets per day for Queue:\t" . $Queue . "\tGenerated at:\t" . Statistics::FormatDate("%x %X", $now). "\n\n");
+
+
+# Compute days to chart.
+# The +1 is because we need to generate one more date. If the user
+# selected a 10 day range, we need to generate 11 days.
+my $diff = int(($eEpoch - $sEpoch + $Statistics::secsPerDay - 1) / $Statistics::secsPerDay)+1;
+
+# Build array of dates
+my $endRange = $eEpoch + $Statistics::secsPerDay;
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($Queue);
+until ($#dates == $diff) {
+    my $date = new RT::Date($session{CurrentUser});
+    $date->Set(Value=>$endRange - $n, Format => 'unix');
+    # Note: we used to adjust the time to local midnight, but
+    # none of the other date entry fields in RT seem to adjust, so we've stopped.
+    #Statistics::DebugLog("Before adjust to midnight date " . Statistics::FormatDate("%c", $date) . "\n");
+    $n+= $Statistics::secsPerDay;
+    # If we aren't showing weekends and this is one, decrement the number
+    # of days to show and skip to the next date.
+    if(!$weekends and Statistics::RTDateIsWeekend($date)) {$diff--; next;}
+    unshift @dates, $date;
+}
+
+# Output header row
+$m->out("Date\tcreate\tresolved\tdeleted\n");
+
+
+LINE: for my $d (0..$#dates) {
+  if ($d == $#dates){
+    next LINE;
+  }
+  my $x = 1;
+  # Output the date for this row
+  $m->out(Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$d]));
+  
+  # output the 3 columns for this row
+  for my $status (qw(created resolved deleted)) {
+    my $tix = new RT::Tickets($session{'CurrentUser'});
+    if ($status eq "created") {
+      $tix->LimitCreated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+      if ($dates[$d+1]) {
+        $tix->LimitCreated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+      }
+    } elsif ($status eq "resolved") {
+      $tix->LimitStatus(VALUE => $status);
+      $tix->LimitResolved(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+      if ($dates[$d+1]) {
+         $tix->LimitResolved(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+      }
+    } elsif ($status eq "deleted") {
+      $tix->LimitStatus(VALUE => $status);
+      $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+      if ($dates[$d+1]) {
+        $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+      }
+    }
+    $tix->LimitQueue (VALUE => $Queue);
+    $m->out( "\t" . $tix->Count ); 
+    $Totals{$status} += $tix->Count;
+  }
+  $m->out("\n");
+}
+
+# Output the totals
+$m->out("Totals\t$Totals{created}\t$Totals{resolved}\t$Totals{deleted}\n");
+
+$m->abort();
+</%INIT>
diff --git a/rt/share/html/RTx/Statistics/CallsQueueDay/index.html b/rt/share/html/RTx/Statistics/CallsQueueDay/index.html
new file mode 100755 (executable)
index 0000000..06fc484
--- /dev/null
@@ -0,0 +1,275 @@
+<& /Elements/Header, Title => loc("Tickets per day in Queue:" . $QueueObj->Name()) &>
+<& /RTx/Statistics/Elements/Tabs,  Title => loc("Tickets by status per day in Queue:" . $QueueObj->Name()) &>
+
+<h3>Description</h3>
+<p>This page displays details about tickets in the selected queue over the date range chosen. It shows how many tickets were created on
+each day in the chosen range, and how many of those were either Resolved or Deleted.</p>
+<p>To always show the current month to date, bookmark this <a href="<%$RT::WebPath%>/RTx/Statistics/CallsQueueDay/index.html?currentMonth=1">link</a>, or 
+for a spreadsheet, use this <a href="<%$RT::WebPath%>/RTx/Statistics/CallsQueueDay/Results.tsv?currentMonth=1">link</a>.</p>
+
+<form method="POST" action="index.html">
+
+% Statistics::DebugLog("queue name=" . $QueueObj->Name() . "\n");
+
+%my $title = "Ticket counts in " . $QueueObj->Name() . " by status per day from " . 
+%        Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[0]) . " through " .
+%        Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$#dates-1]);
+<&|/Elements/TitleBox, 
+       title => $title,
+       title_href => "/RTx/Statistics/CallsQueueDay/index.html?$QueryString" &>
+<TABLE BORDER=0 cellspacing=0 cellpadding=1 WIDTH=100%>
+% if ($ShowHeader) {
+<& /RTx/Statistics/Elements/CollectionAsTable/Header, 
+    Format => \@Format, 
+    FormatString => $Format,
+    AllowSorting => $AllowSorting, 
+    Order => $Order, 
+    Query => undef,
+    Rows => $Rows,
+    Page => $Page,
+    OrderBy => $OrderBy , 
+    BaseURL => $BaseURL,
+    maxitems => $maxitems &> 
+% }
+% my $line = 1;
+% LINE: for my $d (0..$#dates) {
+% if ($d == $#dates){
+%       next LINE;
+% }
+%     my $x = 1;
+%     $values{Statistics_Date} = Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$d]);
+%# NOTE need to handle all status values here....
+%     for my $status (qw(created resolved deleted)) {
+%         my $tix = new RT::Tickets($session{'CurrentUser'});
+%         $tix->LimitQueue (VALUE => $Queue);
+%         if ($status eq "created") {
+%             $tix->LimitCreated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+%             if ($dates[$d+1]) {
+%                 $tix->LimitCreated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+%            }
+%            $values{Statistics_Created_Count} = $tix->Count;
+%            $Totals{Statistics_Created_Count} += $tix->Count;
+%         }
+%         elsif ($status eq "resolved") {
+%             $tix->LimitStatus(VALUE => $status);
+%             $tix->LimitResolved(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+%             if ($dates[$d+1]) {
+%                 $tix->LimitResolved(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+%             }
+%            $values{Statistics_Resolved_Count} = $tix->Count;
+%            $Totals{Statistics_Resolved_Count} += $tix->Count;
+%         } 
+%         elsif ($status eq "deleted") {
+%             $tix->LimitStatus(VALUE => $status);
+%             $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+%             if ($dates[$d+1]) {
+%                 $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+%             }
+%            $values{Statistics_Deleted_Count} = $tix->Count;
+%            $Totals{Statistics_Deleted_Count} += $tix->Count;
+%         }
+%         $data[$x++][$d] = $tix->Count;
+%     }
+<&   /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@Format, i => $line, record => $record, maxitems => $maxitems &>
+%    $line++;
+% }
+% $values {Statistics_Date} = "Totals";
+% $values {Statistics_Created_Count} = $Totals{Statistics_Created_Count};
+% $values {Statistics_Resolved_Count} = $Totals{Statistics_Resolved_Count};
+% $values {Statistics_Deleted_Count} = $Totals{Statistics_Deleted_Count};
+<&   /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@BoldFormat, i => $line, record => $record, maxitems => $maxitems &>
+</table>
+</&>
+
+<hr>
+
+<BR />
+<BR />
+
+<%perl>
+# Create the graph URL
+my $url= 'Elements/Chart?x_labels=';
+for (1..$diff) {
+    $url .= $data[0][$_] . ",";
+}
+chop $url;
+$url .= "&";
+shift @data;
+for (0..$#data) {
+    $url .= "data".(1+$_)."=".(join ",", @{$data[$_]})."&";
+}
+chop $url;
+$url .= "&set_legend=Created,Resolved,Deleted";
+</%perl>
+
+<& /RTx/Statistics/Elements/GraphBox, GraphURL => $url &>
+
+<& /RTx/Statistics/Elements/ControlsAsTable/ControlBox, 
+         Title => "Change Queue or Dates", 
+         ShowDates => 1, sMonth => \$sMonth, sDay => \$sDay, sYear => \$sYear,
+                         eMonth => \$eMonth, eDay => \$eDay, eYear => \$eYear,
+                         weekends => $weekends,
+         ShowSingleQueue => 1, Queue => $Queue
+ &>
+
+</form>
+
+<a href="<%$RT::WebPath%>/RTx/Statistics/CallsQueueDay/index.html?<% $QueryString %>"><&|/l&>Bookmarkable link</&></a> |
+<a href="<%$RT::WebPath%>/RTx/Statistics/CallsQueueDay/Results.tsv?<%$QueryString%>"><&|/l&>spreadsheet</&></a>
+<BR>
+<BR>
+
+
+% Statistics::DebugLog("ref of eMonth is " . ref($eMonth) . "\n");
+% Statistics::DebugInit( $m );
+
+<%ARGS>
+$Queue => undef
+$weekends => $Statistics::PerDayWeekends;
+$sMonth=>undef
+$sDay=>undef
+$sYear=>undef
+$eMonth=>undef
+$eDay=>undef
+$eYear=>undef
+$days=>undef
+$currentMonth=>undef
+
+$AllowSorting => undef
+$Order => undef
+$OrderBy => undef
+$ShowNavigation => 1
+$ShowHeader => 1
+$Rows => 50
+$Page => 1
+$BaseURL => undef
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+use Time::Local;
+my $selected;
+my $n = 0;
+my @data = ([]);
+my @dates;
+my @msgs;
+my $diff;
+my $sEpoch=0;
+my $eEpoch=0;
+my %Totals;
+my $QueryString;
+my $maxitems = 4;
+my %record;
+my %values;
+my $record = \%record;
+
+$record{values} = \%values;
+
+
+# If debugging, set things up and display all the args
+Statistics::DebugClear();
+Statistics::DebugLog("CallsQueueDay/index.html ARGS:\n");
+for my $key (keys %ARGS) {
+  Statistics::DebugLog("ARG{ $key }=" . $ARGS{$key} . "\n");
+}
+
+my $Format = qq{ Statistics_Date, 
+                 '__Statistics_Created_Count__/STYLE:text-align:right;', 
+                 '__Statistics_Resolved_Count__/STYLE:text-align:right;', 
+                '__Statistics_Deleted_Count__/STYLE:text-align:right;' };
+my $BoldFormat = qq{ '<B>__Statistics_Date__</B>', 
+                     '<B>__Statistics_Created_Count__</B>/STYLE:text-align:right;',
+                     '<B>__Statistics_Resolved_Count__</B>/STYLE:text-align:right;',
+                    '<B>__Statistics_Deleted_Count__</B>/STYLE:text-align:right;' };
+my (@Format) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $Format);
+my (@BoldFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $BoldFormat);
+Statistics::DebugLog("CallsQueueDay/index.html Format array=" . join(',', @Format) . "\n");
+
+if (!defined $Queue) {
+  my $QueueObj = new RT::Queue($session{'CurrentUser'});
+  $QueueObj->Load($Statistics::PerDayQueue);
+  $Queue = $QueueObj->Id();
+}
+
+if ($sDay > $Statistics::monthsMaxDay{$sMonth}) {
+  $sDay = $Statistics::monthsMaxDay{$sMonth};
+}
+
+if ($eDay > $Statistics::monthsMaxDay{$eMonth}) {
+  $eDay = $Statistics::monthsMaxDay{$eMonth};
+}
+
+if ($sYear){
+       $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear-1900);
+}
+if ($eYear){
+Statistics::DebugLog("eMonth = " . $eMonth . "\n");
+       $eEpoch = timelocal(0, 0, 0, $eDay, $eMonth, $eYear-1900);
+} else {
+        # This case happens when the page is first loaded
+       my @local = localtime(time);
+       ($eDay, $eMonth, $eYear) = ($local[3], $local[4], $local[5]);
+       $eYear += 1900; 
+       $eEpoch = timelocal(0, 0, 0, $local[3], $local[4], $local[5], $local[6], $local[7], $local[8]);
+Statistics::DebugLog("Setting eEpoch=$eEpoch from current time.\n");
+}
+
+if (($eEpoch < $sEpoch) || ($sEpoch == 0)) {
+    # We have an end, but not a start, or, overlapping.
+    
+    # if $currentMonth is set, just set the day to 1
+    if($currentMonth) {
+      # set start vars from end, but with day set to 1
+      (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($eEpoch);
+      $sDay=1;
+      $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear);
+    } else {
+      # If the user has specified how many days back to go, use that,
+      # If not, set start to configured default period before end
+      if(defined $days) {
+        $sEpoch = $eEpoch - ($days * $Statistics::secsPerDay);
+      } else {
+        $sEpoch = $eEpoch - ($Statistics::PerDayPeriod * $Statistics::secsPerDay);
+      }
+      (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($sEpoch);
+    }
+    $sYear += 1900;
+}
+
+# Compute days to chart.
+# The +1 is because we need to generate one more date. If the user
+# selected a 10 day range, we need to generate 11 days.
+$diff = int(($eEpoch - $sEpoch + $Statistics::secsPerDay - 1) / $Statistics::secsPerDay)+1;
+Statistics::DebugLog("Setting diff=$diff\n");
+
+Statistics::DebugLog("sEpoch=$sEpoch, components=" . join(',', localtime($sEpoch)) . "\n");
+Statistics::DebugLog("eEpoch=$eEpoch, components=" . join(',', localtime($eEpoch)) . "\n");
+
+# Set up the string for the current query for bookmarkable link
+$QueryString = "sDay=$sDay&sMonth=$sMonth&sYear=$sYear&eDay=$eDay&eMonth=$eMonth&eYear=$eYear&weekends=$weekends&Queue=$Queue";
+
+# Set up the end date to be midnight(morning) of the date after the one the user wanted.
+my $endRange = $eEpoch + $Statistics::secsPerDay;
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($Queue);
+$n = 0;
+until ($#dates == $diff) {
+    my $date = new RT::Date($session{CurrentUser});
+    $date->Set(Value=>$endRange - $n, Format => 'unix');
+    # Note: we used to adjust the time to local midnight, but
+    # none of the other date entry fields in RT seem to adjust, so we've stopped.
+    #Statistics::DebugLog("Before adjust to midnight date " . Statistics::FormatDate("%c", $date) . "\n");
+    $n+= $Statistics::secsPerDay;
+    # If we aren't showing weekends and this is one, decrement the number
+    # of days to show and skip to the next date.
+    if(!$weekends and Statistics::RTDateIsWeekend($date)) {$diff--; next;}
+    unshift @dates, $date;
+Statistics::DebugLog("pushing date " . Statistics::FormatDate("%c", $date) . "\n");
+    unshift @{ $data[0] }, Statistics::FormatDate($Statistics::PerDayLabelDateFormat, $date);
+}
+
+# We put an extra day into the lists to cover up till midnight of the next day,
+# But we don't want that to appear in the labels, so pop it off.
+pop( @{ $data[0] } );
+
+</%INIT>
diff --git a/rt/share/html/RTx/Statistics/DayOfWeek/Elements/Chart b/rt/share/html/RTx/Statistics/DayOfWeek/Elements/Chart
new file mode 100755 (executable)
index 0000000..239c095
--- /dev/null
@@ -0,0 +1,26 @@
+% $r->content_type("image/$format");
+% $m->print($graph->plot(\@data)->$format());
+% $m->abort();
+<&|/l, $#data+1&>[_1] Elements</&>:<p>
+% for (0..$#data) {
+<% $data[$_] %><p>
+% }
+<%INIT>
+use GD::Graph::bars;
+
+my @data;
+my $graph = GD::Graph::bars->new($Statistics::GraphWidth,$Statistics::GraphHeight);
+$graph->set(export_format => "png",
+             x_label      => 'Day of Week',
+             y_label      => 'Ticket actions per Day by type');
+$graph->set_legend(split /,/ , $ARGS{set_legend});
+push @data, [split /,/ , $ARGS{x_labels}];
+push @data, [split /,/ , $ARGS{data1}];
+push @data, [split /,/ , $ARGS{data2}];
+push @data, [split /,/ , $ARGS{data3}];
+
+my $format = $graph->export_format;
+$r->content_type("image/$format");
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/share/html/RTx/Statistics/DayOfWeek/index.html b/rt/share/html/RTx/Statistics/DayOfWeek/index.html
new file mode 100755 (executable)
index 0000000..2e82b9c
--- /dev/null
@@ -0,0 +1,155 @@
+<& /Elements/Header, Title =>loc('Tickets by Day Of Week in Queue:' . $QueueObj->Name()) &>
+<& /RTx/Statistics/Elements/Tabs, Title =>loc('Trends in ticket status by Day Of Week in Queue:' . $QueueObj->Name()) &>
+
+<h3>Description</h3>
+<p>The purpose of this page is to show historical trends for each day of the week. 
+It displays details of number of tickets created in your
+selected queue for each day. It also hows how many of those created tickets were Resolved or Deleted</p>
+
+<form method="POST" action="index.html">
+
+
+%my $title = "Ticket counts by day of week in " . $QueueObj->Name();
+<&|/Elements/TitleBox, 
+       title => $title,
+       title_href => "/RTx/Statistics/DayOfWeek/index.html?$QueryString" &>
+<TABLE BORDER=0 cellspacing=0 cellpadding=1 WIDTH=100%>
+% if ($ShowHeader) {
+<& /RTx/Statistics/Elements/CollectionAsTable/Header, 
+    Format => \@Format, 
+    FormatString => $Format,
+    AllowSorting => $AllowSorting, 
+    Order => $Order, 
+    Query => undef,
+    Rows => $Rows,
+    Page => $Page,
+    OrderBy => $OrderBy , 
+    BaseURL => $BaseURL,
+    maxitems => $maxitems &> 
+% }
+% my $line = 1;
+% for my $d (0..$#days) {
+%     my $x = 1;
+%     $values{Statistics_Date} = $days[$d];
+%# NOTE Show all status values???
+%     $values{Statistics_Created_Count} = $counts[$d]{new};
+%     $values{Statistics_Resolved_Count} = $counts[$d]{resolved};
+%     $values{Statistics_Deleted_Count} = $counts[$d]{deleted};
+<&   /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@Format, i => $line, record => $record, maxitems => $maxitems &>
+%    $line++;
+% }
+% $values {Statistics_Date} = "Totals";
+% $values {Statistics_Created_Count} = $Totals{new};
+% $values {Statistics_Resolved_Count} = $Totals{resolved};
+% $values {Statistics_Deleted_Count} = $Totals{deleted};
+<&   /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@BoldFormat, i => $line, record => $record, maxitems => $maxitems &>
+</table>
+</&>
+
+<hr>
+
+<BR />
+<BR />
+
+<%perl>
+my $url = 'Elements/Chart?&x_labels=';
+for (0..$#days) {
+  $url .= $days[$_] . "," ;
+}
+chop $url;
+$url .= "&";
+
+my @things = qw(new resolved deleted);
+for my $th (0..$#things) {
+  $url .= "data".(1+$th)."=".(join ",", map { $counts[$_]{$things[$th]} } (0..6))."&";
+}
+chop $url;
+$url .= '&set_legend=Created,Resolved,Deleted';
+</%perl>
+
+<& /RTx/Statistics/Elements/GraphBox, GraphURL => $url &>
+
+% Statistics::DebugLog("queue name=" . $QueueObj->Id() . "\n");
+
+<& /RTx/Statistics/Elements/ControlsAsTable/ControlBox, 
+         Title => "Change Queue", 
+         ShowSingleQueue => 1, Queue => $QueueObj->Id()
+ &>
+
+</form>
+
+% Statistics::DebugInit( $m );
+
+<%ARGS>
+$Queue => $Statistics::DayOfWeekQueue
+
+$AllowSorting => undef
+$Order => undef
+$OrderBy => undef
+$ShowNavigation => 1
+$ShowHeader => 1
+$Rows => 50
+$Page => 1
+$BaseURL => undef
+</%ARGS>
+
+<%INIT>
+use GD::Graph;
+use RTx::Statistics;
+my @days = qw(Sun Mon Tue Wed Thu Fri Sat);
+my $n = 0;
+my @data = ([]);
+my @msgs;
+my @counts;
+my %Totals = (
+  resolved => 0,
+  deleted => 0,
+  new => 0
+);
+my $QueryString = "Queue=$Queue";
+my $maxitems = 4;
+my %record;
+my %values;
+my $record = \%record;
+
+$record{values} = \%values;
+
+my $Format = qq{ Statistics_Date, 
+                 '__Statistics_Created_Count__/STYLE:text-align:right;', 
+                 '__Statistics_Resolved_Count__/STYLE:text-align:right;', 
+                '__Statistics_Deleted_Count__/STYLE:text-align:right;' };
+my $BoldFormat = qq{ '<B>__Statistics_Date__</B>', 
+                     '<B>__Statistics_Created_Count__</B>/STYLE:text-align:right;',
+                     '<B>__Statistics_Resolved_Count__</B>/STYLE:text-align:right;',
+                    '<B>__Statistics_Deleted_Count__</B>/STYLE:text-align:right;' };
+my (@Format) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $Format);
+my (@BoldFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $BoldFormat);
+
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($Queue);
+$RT::Logger->warning("Loaded queue $Queue, name=". $QueueObj->Name());
+
+my $tix = new RT::Tickets($session{'CurrentUser'});
+$tix->LimitQueue (VALUE => $Queue);
+$tix->UnLimit;
+if ($tix->Count) {
+    # Initialize the counters to zero, so that all the cells show up
+    foreach my $day (0..@days) {
+        $counts[$day]{resolved} = 0;
+        $counts[$day]{deleted} = 0;
+        $counts[$day]{new} = 0;
+    }
+    while (my $t = $tix->RT::SearchBuilder::Next) {  # BLOODY HACK
+        if($t->Status eq "resolved") {
+          $counts[(localtime($t->ResolvedObj->Unix))[6]]{resolved}++;
+         $Totals{resolved}++;
+       }
+       if($t->Status eq "deleted") {
+         $counts[(localtime($t->LastUpdatedObj->Unix))[6]]{deleted}++;
+         $Totals{deleted}++;
+        }
+        $counts[(localtime($t->CreatedObj->Unix))[6]]{new}++;
+       $Totals{new}++;
+    }
+}
+</%INIT>
diff --git a/rt/share/html/RTx/Statistics/DurationAsString b/rt/share/html/RTx/Statistics/DurationAsString
new file mode 100755 (executable)
index 0000000..c0b4d9a
--- /dev/null
@@ -0,0 +1,18 @@
+<%$days|'00'%> days <%$hours|'00'%>:<%$minutes|'00'%> 
+<%INIT>
+
+my $MINUTE = 60;
+my $HOUR =  $MINUTE*60;
+my $DAY = $HOUR * 24;
+my $WEEK = $DAY * 7;
+my $days = int($Duration / $DAY);
+$Duration = $Duration % $DAY;
+my $hours = int($Duration / $HOUR);
+$hours = sprintf("%02d", $hours);
+$Duration = $Duration % $HOUR;
+my $minutes = int($Duration/$MINUTE);
+$minutes = sprintf("%02d", $minutes);
+</%INIT>
+<%ARGS>
+$Duration => undef
+</%ARGS>
diff --git a/rt/share/html/RTx/Statistics/Elements/DateSelectRow b/rt/share/html/RTx/Statistics/Elements/DateSelectRow
new file mode 100644 (file)
index 0000000..325e168
--- /dev/null
@@ -0,0 +1,55 @@
+    <td class="collection-as-table" style="text-align:left;"><% $Label %></td>
+    <td class="collection-as-table" style="text-align:left;">
+      <select name=<% $nameMonth %> >
+% for ($n=0;$n<=$#Statistics::months;$n++){ 
+%      if ($$refMonth eq $n){  
+%              $selected ="selected";
+%      }else {
+%              $selected ="";
+%      }
+         <option  value=<% $n %> <% $selected %> ><% $Statistics::months[$n] %></option>
+%}
+      </select>
+    </td>
+    <td class="collection-as-table" style="text-align:left;">
+      <select name=<% $nameDay %> >
+% for ($n=1;$n<=31;$n++){
+%      if ($$refDay == $n ){
+%              $selected ="selected";
+%      }else {
+%              $selected ="";
+%      }
+           <option  value=<% $n %> <% $selected %> ><% $n  %></option>
+% }
+      </select>
+    </td>
+    <td class="collection-as-table" style="text-align:left;">
+         <select name=<% $nameYear %> >
+% 
+% for ($n=0;$n <= scalar @Statistics::years-1;$n++){
+%      if ($Statistics::years[$n] == $$refYear){
+%              $selected ="selected";
+%      }else{
+%              $selected ="";
+%      }
+         <option value=<% $Statistics::years[$n] %> <% $selected %> ><% $Statistics::years[$n] %></option>
+% }      
+         </select>
+    </td>
+
+
+<%args>
+$Label => undef
+$refMonth => undef
+$nameMonth => undef
+$refDay => undef
+$nameDay => undef
+$refYear => undef
+$nameYear => undef
+</%args>
+<%init>
+use RTx::Statistics;
+my $n;
+my $selected;
+
+</%init>
diff --git a/rt/share/html/RTx/Statistics/Elements/DurationAsString b/rt/share/html/RTx/Statistics/Elements/DurationAsString
new file mode 100755 (executable)
index 0000000..c0b4d9a
--- /dev/null
@@ -0,0 +1,18 @@
+<%$days|'00'%> days <%$hours|'00'%>:<%$minutes|'00'%> 
+<%INIT>
+
+my $MINUTE = 60;
+my $HOUR =  $MINUTE*60;
+my $DAY = $HOUR * 24;
+my $WEEK = $DAY * 7;
+my $days = int($Duration / $DAY);
+$Duration = $Duration % $DAY;
+my $hours = int($Duration / $HOUR);
+$hours = sprintf("%02d", $hours);
+$Duration = $Duration % $HOUR;
+my $minutes = int($Duration/$MINUTE);
+$minutes = sprintf("%02d", $minutes);
+</%INIT>
+<%ARGS>
+$Duration => undef
+</%ARGS>
diff --git a/rt/share/html/RTx/Statistics/Elements/GraphBox b/rt/share/html/RTx/Statistics/Elements/GraphBox
new file mode 100644 (file)
index 0000000..3dc0697
--- /dev/null
@@ -0,0 +1,27 @@
+<div style="float:left; padding-right:30px;">
+<table class="box" bgcolor="#336699" style="border-style:none solid solid solid;border-width:1px;border-color:#2E2E8C;" cellpadding="0" cellspacing="0">
+  <tbody><tr>
+    <th style="color: rgb(51, 102, 153);" class="titlebox">
+      <span class="titleboxclose">
+        <a href="#" onclick="hideshow('stats_chart')">X</a></span>&nbsp;
+
+      <span class="titleboxtitle">
+        <b><a href="<% $GraphURL %>">Download Chart as Image</a></b>
+      </span>
+    </th>
+    <th style="color: rgb(51, 102, 153);" class="titleboxright">
+      <span class="titleboxright">&nbsp;</span>
+    </th>
+  </tr>
+
+  <tr id="element-stats_chart">
+    <td colspan="3" class="" bgcolor="#dddddd">
+       <img src="<% $GraphURL %>" ALT="Result Graph" >
+     </td>
+  </tr>
+  </tbody>
+</table>
+</div>
+<%args>
+$GraphURL => undef
+</%args>
diff --git a/rt/share/html/RTx/Statistics/Elements/SelectMultiQueue b/rt/share/html/RTx/Statistics/Elements/SelectMultiQueue
new file mode 100755 (executable)
index 0000000..637f6dc
--- /dev/null
@@ -0,0 +1,81 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%# 
+%# COPYRIGHT:
+%#  
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC 
+%#                                          <jesse@bestpractical.com>
+%# 
+%# (Except where explicitly superseded by other copyright notices)
+%# 
+%# 
+%# LICENSE:
+%# 
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%# 
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%# 
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%# 
+%# 
+%# CONTRIBUTION SUBMISSION POLICY:
+%# 
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%# 
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%# 
+%# END BPS TAGGED BLOCK }}}
+<SELECT NAME ="<%$Name%>" multiple size="<% $Size %>">
+% if ($ShowNullOption) {
+<OPTION VALUE="">-</OPTION>
+% }
+% while (my $queue=$q->Next) {
+% if ($ShowAllQueues || $queue->CurrentUserHasRight($CheckQueueRight)) {
+%  my $targ="," . $queue->Name . ",";
+<OPTION VALUE="<%($NamedValues ? $queue->Name : $queue->Id) %>" <%( ($sel_list =~ m/$targ/) ? 'SELECTED' : '')%>><%$queue->Name%>
+%   if (($Verbose) and ($queue->Description) ){
+(<%$queue->Description%>)
+%  }
+</OPTION>
+% }
+% }
+</SELECT>
+<%ARGS>
+$CheckQueueRight => 'CreateTicket'
+$ShowNullOption => 1
+$ShowAllQueues => 1
+$Name => undef
+$Verbose => undef
+$NamedValues => 0
+$Selected => undef  # ref to array containing selected queue names
+$Lite => 0
+$Size => 5
+</%ARGS>
+
+<%INIT>
+
+# put list of queue names into string, starting and ending with commas
+my $sel_list = "," . join(",", @$Selected) . ",";
+
+my $q=new RT::Queues($session{'CurrentUser'});
+$q->UnLimit;
+
+</%INIT>
diff --git a/rt/share/html/RTx/Statistics/Elements/StatColumnMap b/rt/share/html/RTx/Statistics/Elements/StatColumnMap
new file mode 100644 (file)
index 0000000..aef9e2f
--- /dev/null
@@ -0,0 +1,173 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%# 
+%# COPYRIGHT:
+%#  
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC 
+%#                                          <jesse@bestpractical.com>
+%# 
+%# (Except where explicitly superseded by other copyright notices)
+%# 
+%# 
+%# LICENSE:
+%# 
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%# 
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%# 
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%# 
+%# 
+%# CONTRIBUTION SUBMISSION POLICY:
+%# 
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%# 
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%# 
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$Name => undef
+$Attr => undef
+</%ARGS>
+
+
+<%ONCE>
+our ( $STAT_COLUMN_MAP );
+
+sub StatColumnMap {
+    my $name = shift;
+    my $attr = shift;
+
+    # First deal with the simple things from the map
+    if ( $STAT_COLUMN_MAP->{$name} ) {
+        return ( $STAT_COLUMN_MAP->{$name}->{$attr} );
+    }
+
+    # now, let's deal with harder things, like Custom Fields
+
+    elsif ( $name =~ /^(?:CF|CustomField)\.\{(.+)\}$/ ) {
+        my $field = $1;
+
+        if ( $attr eq 'attribute' ) {
+            return (undef);
+        }
+        elsif ( $attr eq 'title' ) {
+            return ( $field );
+        }
+        elsif ( $attr eq 'value' ) {
+           # Display custom field contents, separated by newlines.
+            # For Image custom fields we also show a thumbnail here.
+            return sub {
+                my $values = $_[0]->CustomFieldValues($field);
+                return map {
+                    (
+                        ($_->CustomFieldObj->Type eq 'Image')
+                            ? \($m->scomp( '/Elements/ShowCustomFieldImage', Object => $_ ))
+                            : $_->Content
+                    ),
+                    \'<br>',
+                } @{ $values->ItemsArrayRef }
+           };
+        }
+    }
+}
+
+sub LinkCallback {
+    my $method = shift;
+
+    my $mode            = $RT::Ticket::LINKTYPEMAP{$method}{Mode};
+    my $type            = $RT::Ticket::LINKTYPEMAP{$method}{Type};
+    my $mode_uri        = $mode.'URI';
+    my $local_type      = 'Local'.$mode;
+
+    return sub {
+        map {
+            \'<A HREF="',
+            $_->$mode_uri->Resolver->HREF,
+            \'">',
+            ( $_->$mode_uri->IsLocal ? $_->$local_type : $_->$mode ),
+            \'</A><BR>',
+        } @{ $_[0]->Links($mode,$type)->ItemsArrayRef }
+    }
+}
+
+$STAT_COLUMN_MAP = {
+    LastUpdated => {
+        attribute => 'LastUpdated',
+        title     => 'Last Updated',
+        value     => sub { return $_[0]->LastUpdatedObj->AsString }
+    },
+
+    Statistics_Date => {
+       title => 'Date',
+       value => sub { return $_[0]{values}{Statistics_Date} }
+    },
+
+    Statistics_Created_Count => {
+       title => 'Created',
+       value => sub { return $_[0]{values}{Statistics_Created_Count} }
+    },
+
+    Statistics_Resolved_Count => {
+       title => 'Resolved',
+       value => sub { return $_[0]{values}{Statistics_Resolved_Count} }
+    },
+
+    Statistics_Deleted_Count => {
+       title => 'Deleted',
+       value => sub { return $_[0]{values}{Statistics_Deleted_Count} }
+    },
+
+    Statistics_Totals => {
+       title => 'Totals',
+       value => sub { return $_[0]{values}{Statistics_Totals} }
+    },
+
+    Statistics_Status => {
+       title => 'Status',
+       value => sub { return $_[0]{values}{Statistics_Status} }
+    },
+
+    Statistics_Dynamic => {
+       # Depends on having a KEY as second param
+       value => sub { 
+           my $record = shift;
+           my $line = shift;
+           my $key = shift;
+           return $$record{values}{$key} 
+       }
+    },
+
+    # Everything from LINKTYPEMAP
+    (map {
+        $_ => { value => LinkCallback( $_ ) }
+    } keys %RT::Ticket::LINKTYPEMAP),
+
+    '_CLASS' => {
+        value => sub { return $_[1] % 2 ? 'oddline' : 'evenline' }
+    },
+
+};
+</%ONCE>
+<%init>
+$m->comp( '/Elements/Callback', STAT_COLUMN_MAP    => $STAT_COLUMN_MAP, _CallbackName => 'StatColumnMap');
+return StatColumnMap($Name, $Attr);
+</%init>
diff --git a/rt/share/html/RTx/Statistics/Elements/Tabs b/rt/share/html/RTx/Statistics/Elements/Tabs
new file mode 100755 (executable)
index 0000000..4fde113
--- /dev/null
@@ -0,0 +1,72 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+%# 
+%# (Except where explictly superceded by other copyright notices)
+%# 
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%# 
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%# 
+%# Unless otherwise specified, all modifications, corrections or
+%# extensions to this work which alter its source code become the
+%# property of Best Practical Solutions, LLC when submitted for
+%# inclusion in the work.
+%# 
+%# 
+%# END LICENSE BLOCK
+<& /Elements/Tabs, 
+    tabs => $tabs, 
+    current_toptab => 'RTx/Statistics/index.html', 
+    current_tab => $current_tab, 
+    Title => $Title &>
+
+<%INIT>
+  my $tabs = { A => { title => loc('Tickets per Day'),
+                         path => 'RTx/Statistics/CallsQueueDay/index.html',
+                       },
+              B => { title => loc('Tickets by status'),
+                          path => 'RTx/Statistics/OpenStalled/index.html',
+                        },
+              C => { title => loc('Multiple Queues'),
+                          path => 'RTx/Statistics/CallsMultiQueue/index.html',
+                        },
+              D => { title => loc('Ticket Trends by Day'),
+                          path => 'RTx/Statistics/DayOfWeek/index.html',
+                        },
+              E => { 'title' => loc('Time to Resolve'),
+                          path => 'RTx/Statistics/Resolution/index.html',
+                        },
+              F => { 'title' => loc('Resolve Time Graph'),
+                          path => 'RTx/Statistics/TimeToResolve/index.html',
+                        },
+              Z => { 'title' => loc('FAQ'),
+                          path => 'RTx/Statistics/FAQ/index.html',
+                        },
+            };
+
+  # Now let callbacks add their extra tabs
+  $m->comp('/Elements/Callback', tabs => $tabs, %ARGS);
+
+  foreach my $tab (sort keys %{$tabs}) {
+    if ($tabs->{$tab}->{'path'} eq $current_tab) {
+      $tabs->{$tab}->{"subtabs"} = $subtabs;
+      $tabs->{$tab}->{"current_subtab"} = $current_subtab;
+    }
+  }
+
+</%INIT>
+
+
+<%ARGS>
+$subtabs => undef
+$current_tab => undef
+$current_subtab => undef
+$Title => undef
+</%ARGS>
diff --git a/rt/share/html/RTx/Statistics/FAQ/index.html b/rt/share/html/RTx/Statistics/FAQ/index.html
new file mode 100644 (file)
index 0000000..e7839ea
--- /dev/null
@@ -0,0 +1,23 @@
+<& /Elements/Header, Title => 'FAQ and known issues' &>
+<& /RTx/Statistics/Elements/Tabs, Title => loc("FAQ and Known Issues") &>
+<hr noshade size="1">
+<p>This page will be used to contain known issues and FAQ`s for the Statistics
+package<br />
+This will also be used to clarify limitations of the package as they stand.</p>
+
+<p><strong>What Version of the Statistics package is this?</strong></p>
+<p>0.1.8</p>
+
+<p><strong>What time zone are the charts set to?</strong></p>
+<p>Because of the new programming method of the date functions, the charts are currently built in GMT(UTC). This may once again be
+customisable in a future release.</p>
+
+<p><strong>What is the default date period and queue?</strong></p>
+<p>The default date period is the previous 10 days, except where the chart is over a fixed 7 day period. The default queue is either
+General, or another queue set in your local configuration.</p>
+
+<p><strong>What are the limitations of the date function?</strong></p>
+<p>It has few, but it will not let you chose less than one day. you cannot select an end date before the start date and it is not
+recommended to select a date in the future or an illegal date, such at 30th February. Code has been put in place to trap these, but it may
+not be fool proof.</p>
+<hr size="1" noshade>
diff --git a/rt/share/html/RTx/Statistics/OpenStalled/Elements/Chart b/rt/share/html/RTx/Statistics/OpenStalled/Elements/Chart
new file mode 100755 (executable)
index 0000000..9505881
--- /dev/null
@@ -0,0 +1,27 @@
+<%perl>
+$r->content_type("image/$format");
+print $graph->plot(\@data)->$format();
+$m->abort();
+print $#data+1 . " Elements:<p>";
+for (0..$#data) {
+print $data[$_];
+print "<p>";
+}
+</%perl>
+<%INIT>
+use GD::Graph::bars;
+
+my @data;
+my $graph = GD::Graph::bars->new($Statistics::GraphWidth,$Statistics::GraphHeight);
+$graph->set(export_format => "png",
+            x_label       => 'Queue name',
+            y_label       => 'Total per queue by status');
+my $format = $graph->export_format;
+$graph->set_legend(split /,/ , $ARGS{set_legend});
+push @data, [split /,/ , $ARGS{x_labels}];
+push @data, [split /,/ , $ARGS{data1}];
+push @data, [split /,/ , $ARGS{data2}];
+push @data, [split /,/ , $ARGS{data3}];
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/share/html/RTx/Statistics/OpenStalled/Results.tsv b/rt/share/html/RTx/Statistics/OpenStalled/Results.tsv
new file mode 100644 (file)
index 0000000..2ec1e0c
--- /dev/null
@@ -0,0 +1,114 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%# 
+%# COPYRIGHT:
+%#  
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC 
+%#                                          <jesse@bestpractical.com>
+%# 
+%# (Except where explicitly superseded by other copyright notices)
+%# 
+%# 
+%# LICENSE:
+%# 
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%# 
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%# 
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%# 
+%# 
+%# CONTRIBUTION SUBMISSION POLICY:
+%# 
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%# 
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%# 
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+@queues => @Statistics::OpenStalledQueueList
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+use Time::Local;
+
+  my $n = 0;
+  my @data; 
+  my @msgs;
+  my %totals;
+  my $QueryString;
+  my $now = new RT::Date($session{CurrentUser});
+  my $tix = new RT::Tickets($session{'CurrentUser'});
+
+  my %queues = map { 
+    $_ => 1;
+  } (@queues);
+
+  # set content type
+  $r->content_type('application/vnd.ms-excel');
+
+  $QueryString = "queues=" . join("&queues=", @queues);
+
+  my $queue = new RT::Queues($session{CurrentUser});
+  $queue->UnLimit;
+
+  my $QueueObj = new RT::Queue($session{'CurrentUser'});
+  $QueueObj->Load($queue);
+
+  # Put out some data about the generation of this file
+  $m->out("Tickets by Status by Queue for Queues:\t" . join(',', @queues) . "\tGenerated at:\t" . Statistics::FormatDate("%x %X", $now). "\n\n");
+
+  # basically the same as index.html
+
+  # Output header row
+  $m->out("Status");
+  for ( sort keys %queues) {
+      push @data, $_;
+      my $Queueobj = new RT::Queue($session{'CurrentUser'});
+      $Queueobj->Load($_);
+      next if !$Queueobj->CurrentUserHasRight('SeeQueue');
+      $m->out("\t" . $_);
+  }
+  $m->out("\tTotals\n");
+
+  foreach my $s (qw(new open stalled)) {
+    $m->out("$s");
+    my $total=0;
+    foreach my $q (sort keys %queues)  {
+      $tix = new RT::Tickets($session{'CurrentUser'});
+      $tix->LimitQueue(VALUE => "$q");
+      $tix->LimitStatus(VALUE => "$s");
+      $totals{$q} += $tix->Count; # Add up columns for each queue
+      $m->out("\t" . $tix->Count);
+      $total += $tix->Count;
+    }
+    $m->out("\t$total\n");
+    $totals{"Totals"} += $total;
+  }
+  $m->out("Totals");
+  foreach my $q (sort keys %queues) {
+    $m->out("\t" . $totals{$q});
+  }
+  $m->out("\t" . $totals{"Totals"} . "\n");
+
+  $m->abort();
+</%INIT>
diff --git a/rt/share/html/RTx/Statistics/OpenStalled/index.html b/rt/share/html/RTx/Statistics/OpenStalled/index.html
new file mode 100755 (executable)
index 0000000..d0cd9f1
--- /dev/null
@@ -0,0 +1,188 @@
+<& /Elements/Header, Title => loc('New, Open and Stalled tickets by Queue') &>
+<& /RTx/Statistics/Elements/Tabs, Title => loc('New, Open and Stalled tickets by Queue') &>
+
+<h3>Description</h3>
+<p>The purpose of this page is to show a snapshot of the current status of tickets by Queue. You can multi select Queues from the dropdown
+list or simply show all available queues. This will indicate how many tickets have not yet been viewed (New), how many have been at least
+viewed once (Open) and how many have had their status changed to stalled.</p>
+
+<form method="POST" action="index.html">
+
+%my $tix = new RT::Tickets($session{'CurrentUser'});
+%if ($queue) {
+%        $tix->LimitQueue (VALUE => $queue);
+%}
+
+
+%my $title = "New, Open and Stalled Tickets in " . join(', ', @queues);
+<& /Elements/TitleBoxStart, title => $title, title_href => "/RTx/Statistics/OpenStalled/index.html?$QueryString"&>
+<TABLE BORDER=0 cellspacing=0 cellpadding=1 WIDTH="100%">
+% if ($ShowHeader) {
+<& /RTx/Statistics/Elements/CollectionAsTable/Header, 
+    Format => \@RowFormat, 
+    FormatString => $RowFormat,
+    AllowSorting => $AllowSorting, 
+    Order => $Order, 
+    Query => undef,
+    Rows => $Rows,
+    Page => $Page,
+    OrderBy => $OrderBy , 
+    BaseURL => $BaseURL,
+    maxitems => $maxitems &> 
+% }
+
+%    for ( sort keys %queues_to_show) {
+%        push @data, $_;
+%    }
+%    my @legend;
+%    my $total = 0;
+%    my $line = 0;
+%# NOTE need to handle all status values (see share/html/Elements/SelectStatus).
+%    foreach my $s (qw(new open stalled)) {
+%      $line++;
+%      push @legend, $s;
+%      $total=0;
+%      foreach my $q (sort keys %queues_to_show)  {
+%        $tix = new RT::Tickets($session{'CurrentUser'});
+%        $tix->LimitQueue(VALUE => "$q");
+%        $tix->LimitStatus(VALUE => "$s");
+%        push @data, $tix->Count;
+%        $totals{$q} += $tix->Count; # Add up columns for each queue
+%        $total += $tix->Count;
+%        $values{$q} = $tix->Count;
+%      }
+%      $totals{"Totals"} += $total;
+%      $values{Statistics_Status} = $s;
+%      $values{Statistics_Totals} = $total;
+<&     /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@RowFormat, i => $line, record => $record, maxitems => $maxitems &>
+%    }
+%    $values{Statistics_Status} = "Totals";
+%    foreach my $q (sort keys %queues_to_show) {
+%      $values{$q} = $totals{$q};
+%    }
+%    $values{Statistics_Totals} = $totals{"Totals"};
+<&   /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@BoldRowFormat, i => $line+1, record => $record, maxitems => $maxitems &>
+</table>
+<& /Elements/TitleBoxEnd&>
+
+<hr>
+
+<BR />
+<BR />
+
+% use Data::Dumper;
+% Statistics::DebugLog("Dump of data array is " . Dumper(@data) . "\n");
+%  my $url = 'Elements/Chart?x_labels=';
+%  for (1..(scalar keys %queues_to_show)) {
+%    $url .=  $m->interp->apply_escapes((shift @data),'u')  . ',';
+%  }
+%  chop $url;
+%  $url .= '&data1=' ;
+%  for (1..(scalar keys %queues_to_show)) {
+%    $url .=  $m->interp->apply_escapes((shift @data),'u') . ',';
+%  }
+%  chop $url;
+%  $url .= '&data2=' ;
+%  for (1..(scalar keys %queues_to_show)) {
+%    $url .=  $m->interp->apply_escapes((shift @data),'u') . ',';
+%  }
+%  chop $url;
+%  $url .= '&data3=' ;
+%  for (1..(scalar keys %queues_to_show)) {
+%    $url .=  $m->interp->apply_escapes((shift @data),'u') . ',';
+%  }
+%  $url .= '&set_legend='.(join ",", @legend);
+
+
+<& /RTx/Statistics/Elements/GraphBox, GraphURL => $url &>
+
+<& /RTx/Statistics/Elements/ControlsAsTable/ControlBox, Title => "Select Queues", ShowMultiQueues => 1, queues_ref => \@queues &>
+
+<a href="<%$RT::WebPath%>/RTx/Statistics/OpenStalled/index.html?<% $QueryString %>"><&|/l&>Bookmarkable link</&></a>
+%# | <a href="<%$RT::WebPath%>/RTx/Statistics/OpenStalled/Results.tsv?<%$QueryString%>"><&|/l&>spreadsheet</&></a>
+<BR>
+<BR>
+
+</FORM>
+
+% Statistics::DebugInit( $m );
+
+<%ARGS>
+@queues => @Statistics::OpenStalledQueueList
+$AllowSorting => undef
+$Order => undef
+$OrderBy => undef
+$ShowNavigation => 1
+$ShowHeader => 1
+$Rows => 50
+$Page => 1
+$BaseURL => undef
+$AddAllCheck => undef
+</%ARGS>
+
+<%INIT>
+  use RTx::Statistics;
+
+  my $n = 0;
+  my @data; 
+  my @msgs;
+  my %totals;
+  my $QueryString;
+  my %queues_to_show;
+  my $maxitems;
+  my $RowFormat;
+  my $BoldRowFormat;
+  my %record;
+  my %values;
+  my $record = \%record;
+
+  $record{values} = \%values;
+
+  Statistics::DebugClear();
+
+  # Handle the Add All Checkbox
+  if($AddAllCheck eq "on") {
+    $AddAllCheck = undef;
+    undef (@queues);
+    my $q=new RT::Queues($session{'CurrentUser'});
+    $q->UnLimit;
+    while (my $queue=$q->Next) {
+      next if !$queue->CurrentUserHasRight('SeeQueue');
+      push @queues, $queue->Name;
+    }
+  }
+
+  # If the user has the right to see the queue, put it into the map
+  for my $q (@queues) {
+      my $Queueobj = new RT::Queue($session{'CurrentUser'});
+      $Queueobj->Load($q);
+      next if !$Queueobj->CurrentUserHasRight('SeeQueue');
+      $queues_to_show{$q} = 1;
+  }
+
+  $maxitems = (scalar @queues) + 2;
+
+  # Build the new query string
+  $QueryString = "queues=" . join("&queues=", @queues);
+
+  # Build the format strings
+  $RowFormat = "'__Statistics_Status__'";
+  $BoldRowFormat = "'<B>__Statistics_Status__</B>'";
+  for my $q (@queues) {
+      $RowFormat .= ",'__Statistics_Dynamic__/KEY:$q/TITLE:$q/STYLE:text-align:right;'";
+      $BoldRowFormat .= ",'<B>__Statistics_Dynamic__</B>/KEY:$q/TITLE:$q/STYLE:text-align:right;'";
+  }
+  $RowFormat .= ",'<B>__Statistics_Totals__</B>/STYLE:text-align:right;'";
+  $BoldRowFormat .= ",'<B>__Statistics_Totals__</B>/STYLE:text-align:right;'";
+  # Parse the formats into structures.
+  my (@RowFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $RowFormat);
+  my (@BoldRowFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $BoldRowFormat);
+
+
+  my $queue = new RT::Queues($session{CurrentUser});
+  $queue->UnLimit;
+
+  my $QueueObj = new RT::Queue($session{'CurrentUser'});
+  $QueueObj->Load($queue);
+
+</%INIT>
diff --git a/rt/share/html/RTx/Statistics/Resolution/Elements/Chart b/rt/share/html/RTx/Statistics/Resolution/Elements/Chart
new file mode 100755 (executable)
index 0000000..fa0ac55
--- /dev/null
@@ -0,0 +1,29 @@
+<%perl>
+$r->content_type("image/$format");
+print $graph->plot(\@data)->$format();
+$m->abort();
+print $#data+1 . " Elements:<p>";
+for (0..$#data) {
+print $data[$_];
+print "<p>";
+}
+</%perl>
+<%INIT>
+use GD::Graph::lines;
+
+my @data;
+my $graph = GD::Graph::lines->new($Statistics::GraphWidth,$Statistics::GraphHeight);
+$graph->set(export_format => "png",
+            x_label           => 'Days',
+            y_label           => 'Average time in Days');
+
+push @data, [split /,/ , $ARGS{x_labels}];
+push @data, [split /,/ , $ARGS{data1}];
+push @data, [split /,/ , $ARGS{data2}];
+push @data, [split /,/ , $ARGS{data3}];
+
+my $format = $graph->export_format;
+#$r->content_type("image/$format");
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/share/html/RTx/Statistics/Resolution/index.html b/rt/share/html/RTx/Statistics/Resolution/index.html
new file mode 100644 (file)
index 0000000..d9885b0
--- /dev/null
@@ -0,0 +1,269 @@
+<& /Elements/Header, Title => 'Time to Resolution' &>
+<& /RTx/Statistics/Elements/Tabs, Title => loc("Time To Resolve tickets by Queue for : " .$QueueObj->Name()) &>
+<h3>Description</h3>
+<p>This page shows details of resolution of tickets in the selected queue. It displays tickets created on each day in your selected date
+range. Of those tickets created on that day, how many have been resolved and the total time it has taken for all tickets created on that
+day to be resolved.</p>
+<p>At the bottom of the chart is shows total time taken to resolve all tickets
+in the selected date range and the average time per ticket to
+resolve.</p>
+
+<form method="POST" action="index.html">
+
+%my $title = "Time to resolve in " . $QueueObj->Name() . " per day from " . 
+%        Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[0]) . " through " .
+%        Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$#dates-1]);
+<&|/Elements/TitleBox, 
+       title => $title,
+       title_href => "/RTx/Statistics/Resolution/index.html?$QueryString" &>
+<TABLE BORDER=0 cellspacing=0 cellpadding=1 WIDTH=100%>
+% if ($ShowHeader) {
+<& /RTx/Statistics/Elements/CollectionAsTable/Header, 
+    Format => \@Format, 
+    FormatString => $Format,
+    AllowSorting => $AllowSorting, 
+    Order => $Order, 
+    Query => undef,
+    Rows => $Rows,
+    Page => $Page,
+    OrderBy => $OrderBy , 
+    BaseURL => $BaseURL,
+    maxitems => $maxitems &> 
+% }
+% my $line = 1;
+% LINE: for my $d (0..$#dates ) {
+%      if ($d == $#dates ){
+%              next LINE;
+%      }
+%    my $x = 1;
+%    $values{Statistics_Date} = Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$d]);
+%    my $tix = new RT::Tickets($session{'CurrentUser'});
+%    $tix->LimitCreated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+%    if ($dates[$d+1]) {
+%        $tix->LimitCreated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+%    }         
+%    if ($Queue) {
+%        $tix->LimitQueue (VALUE => $Queue);
+%    }
+%    $values{Statistics_Created_Count} = $tix->Count;
+%    $tix->LimitStatus(VALUE => "resolved");
+%    $values{Statistics_Resolved_Count} = $tix->Count;
+%    if ($tix->Count) {
+%       my @tix = @{$tix->ItemsArrayRef};
+%       my $total;
+%       $total += ($_->ResolvedObj->Unix - $_->CreatedObj->Unix) for @tix;
+%              $size+= ($#tix +1);
+%              $grandtotal += $total;                                                     
+%       $values{Duration} = Statistics::DurationAsString($total);
+%      $data[$x++][$d] =  int ($total );
+%    } else {
+%       $values{Duration} = "N/A";
+%    }
+<&   /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@Format, i => $line, record => $record, maxitems => $maxitems &>
+%    $line++;
+%}
+%    $size =1 if $size==0;
+%    $values{text} = "Average time to resolve = " . Statistics::DurationAsString($grandtotal /  $size);
+<&   /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@OneCellFormat, i => $line, record => $record, maxitems => $maxitems &>
+%    $line++;
+%    $values{text} = "Total time to resolve = " . Statistics::DurationAsString( $grandtotal );
+<&   /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@OneCellFormat, i => $line, record => $record, maxitems => $maxitems &>
+%    $line++;
+</table>
+</&>
+
+<hr>
+
+<BR />
+<BR />
+
+<%perl>
+# Create the graph URL
+
+# change the total time to resolve to a floating point number of days
+foreach my $dat(@{$data[1]} ){
+  $dat = ($dat / $Statistics::secsPerDay);
+  $dat = sprintf("%0.4f", $dat); 
+}
+
+my $url = 'Elements/Chart?x_labels=';
+for (0..$diff-1) {
+  $url .= $data[0][$_] . ",";
+}
+chop $url;
+shift @data;
+$url .= "&data1=";
+for(0..$diff-1) {
+  $data[0][$_] = 0 if !$data[0][$_];
+  $url .= $data[0][$_] . ",";
+}
+</%perl>
+
+<& /RTx/Statistics/Elements/GraphBox, GraphURL => $url &>
+
+<& /RTx/Statistics/Elements/ControlsAsTable/ControlBox, 
+         Title => "Change Queue or Dates", 
+         ShowDates => 1, sMonth => \$sMonth, sDay => \$sDay, sYear => \$sYear,
+                         eMonth => \$eMonth, eDay => \$eDay, eYear => \$eYear,
+                         weekends => $weekends,
+         ShowSingleQueue => 1, Queue => $Queue
+ &>
+
+</form>
+
+<%ARGS>
+$max => $Statistics::TimeToResolveMaxRows
+$Queue => undef
+$weekends =>$Statistics::TimeToResolveWeekends
+$sMonth=>undef
+$sDay=>undef
+$sYear=>undef
+$eMonth=>undef
+$eDay=>undef
+$eYear=>undef
+$days=>undef
+$currentMonth=>undef
+
+$AllowSorting => undef
+$Order => undef
+$OrderBy => undef
+$ShowNavigation => 1
+$ShowHeader => 1
+$Rows => 50
+$Page => 1
+$BaseURL => undef
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+use Time::Local;
+my $n = 0;
+my @data = ([]);
+my @dates;
+my @msgs;
+my $size;
+my $selected;
+my $grandtotal = 0; 
+my $diff;
+my $sEpoch=0;
+my $eEpoch=0;
+my $QueryString;
+
+my $maxitems = 4;
+my %record;
+my %values;
+my $record = \%record;
+
+$record{values} = \%values;
+
+
+# If debugging, set things up and display all the args
+Statistics::DebugClear();
+Statistics::DebugLog("CallsQueueDay/index.html ARGS:\n");
+for my $key (keys %ARGS) {
+  Statistics::DebugLog("ARG{ $key }=" . $ARGS{$key} . "\n");
+}
+
+my $Format = qq{ Statistics_Date, 
+                 '__Statistics_Created_Count__/STYLE:text-align:right;', 
+                 '__Statistics_Resolved_Count__/STYLE:text-align:right;', 
+                '__Statistics_Dynamic__/KEY:Duration/TITLE:Time To Resolve/STYLE:text-align:right;' };
+my $BoldFormat = qq{ '<B>__Statistics_Date__</B>', 
+                     '<B>__Statistics_Created_Count__</B>/STYLE:text-align:right;',
+                     '<B>__Statistics_Resolved_Count__</B>/STYLE:text-align:right;',
+                    '<B>__Statistics_Dynamic__</B>/KEY:Duration/TITLE:Time To Resolve/STYLE:text-align:right;' };
+
+# TODO need way to make this cell do colspan
+my $OneCellFormat = qq{ '<B>__Statistics_Dynamic__</B>/KEY:text/STYLE:text-align:left;','','','' };
+
+my (@Format) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $Format);
+my (@BoldFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $BoldFormat);
+my (@OneCellFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $OneCellFormat);
+
+Statistics::DebugLog("CallsQueueDay/index.html Format array=" . join(',', @Format) . "\n");
+
+if ($sDay > $Statistics::monthsMaxDay{$sMonth}) {
+  $sDay = $Statistics::monthsMaxDay{$sMonth};
+}
+
+if ($eDay > $Statistics::monthsMaxDay{$eMonth}) {
+  $eDay = $Statistics::monthsMaxDay{$eMonth};
+}
+
+if ($sYear){
+       $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear-1900);
+}
+if ($eYear){
+Statistics::DebugLog("eMonth = " . $eMonth . "\n");
+       $eEpoch = timelocal(0, 0, 0, $eDay, $eMonth, $eYear-1900);
+} else {
+        # This case happens when the page is first loaded
+       my @local = localtime(time);
+       ($eDay, $eMonth, $eYear) = ($local[3], $local[4], $local[5]);
+       $eYear += 1900; 
+       $eEpoch = timelocal(0, 0, 0, $local[3], $local[4], $local[5], $local[6], $local[7], $local[8]);
+Statistics::DebugLog("Setting eEpoch=$eEpoch from current time.\n");
+}
+
+if (($eEpoch < $sEpoch) || ($sEpoch == 0)) {
+    # We have an end, but not a start, or, overlapping.
+    
+    # if $currentMonth is set, just set the day to 1
+    if($currentMonth) {
+      # set start vars from end, but with day set to 1
+      (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($eEpoch);
+      $sDay=1;
+      $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear);
+    } else {
+      # If the user has specified how many days back to go, use that,
+      # If not, set start to configured default period before end
+      if(defined $days) {
+        $sEpoch = $eEpoch - ($days * $Statistics::secsPerDay);
+      } else {
+        $sEpoch = $eEpoch - ($Statistics::PerDayPeriod * $Statistics::secsPerDay);
+      }
+      (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($sEpoch);
+    }
+    $sYear += 1900;
+}
+
+# Compute days to chart.
+# The +1 is because we need to generate one more date. If the user
+# selected a 10 day range, we need to generate 11 days.
+$diff = int(($eEpoch - $sEpoch + $Statistics::secsPerDay - 1) / $Statistics::secsPerDay)+1;
+Statistics::DebugLog("Setting diff=$diff\n");
+
+Statistics::DebugLog("sEpoch=$sEpoch, components=" . join(',', localtime($sEpoch)) . "\n");
+Statistics::DebugLog("eEpoch=$eEpoch, components=" . join(',', localtime($eEpoch)) . "\n");
+
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+if (!defined $Queue) {
+  $QueueObj->Load($Statistics::TimeToResolveQueue);
+  $Queue = $QueueObj->Id();
+}
+
+# Set up the string for the current query for bookmarkable link
+$QueryString = "sDay=$sDay&sMonth=$sMonth&sYear=$sYear&eDay=$eDay&eMonth=$eMonth&eYear=$eYear&weekends=$weekends&Queue=$Queue";
+
+# Set up the end date to be midnight(morning) of the date after the one the user wanted.
+my $endRange = $eEpoch + $Statistics::secsPerDay;
+$QueueObj->Load($Queue);
+# NOTE: list loop starts at the end of the date range, unshifting dates onto 
+# the arrays, so that they end up in start to finish order.
+$eEpoch += $Statistics::secsPerDay;
+$n = 0;
+until ($#dates == $diff ) {    
+    my $date = new RT::Date($session{CurrentUser});
+    $date->Set(Value=>$endRange - $n, Format => 'unix');
+    # Note: we used to adjust the time to local midnight, but
+    # none of the other date entry fields in RT seem to adjust, so we've stopped.
+    #Statistics::DebugLog("Before adjust to midnight date " . Statistics::FormatDate("%c", $date) . "\n");
+    $n+= $Statistics::secsPerDay;
+    # If we aren't showing weekends and this is one, decrement the number
+    # of days to show and skip to the next date.
+    if(!$weekends and Statistics::RTDateIsWeekend($date)) {$diff--; next;}
+    unshift @dates, $date;
+Statistics::DebugLog("pushing date " . Statistics::FormatDate("%c", $date) . "\n");
+    unshift @{ $data[0] }, Statistics::FormatDate($Statistics::PerDayLabelDateFormat, $date);
+}
+</%INIT>
diff --git a/rt/share/html/RTx/Statistics/TimeToResolve/Elements/Chart b/rt/share/html/RTx/Statistics/TimeToResolve/Elements/Chart
new file mode 100755 (executable)
index 0000000..a069a7b
--- /dev/null
@@ -0,0 +1,23 @@
+<%perl>
+print $graph->plot(\@data)->$format();
+$m->abort();
+</%perl>
+<%INIT>
+use GD::Graph::points;
+
+my @data;
+my $graph = GD::Graph::points->new(400,300);
+$graph->set(export_format => "png",
+            marker_size   => $ARGS{marker_size},
+            x_label       => 'Average time to resolve (Days)',
+            y_label       => 'Number of tickets resolved' );
+#$r->content_type("image/$format");
+my $format = $graph->export_format; 
+push @data, [split /,/ , $ARGS{x_labels}];
+for (1..((scalar keys %ARGS)-2)) {
+  push @data, [split /,/  , $ARGS{"data".$_}];
+}
+
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/share/html/RTx/Statistics/TimeToResolve/index.html b/rt/share/html/RTx/Statistics/TimeToResolve/index.html
new file mode 100755 (executable)
index 0000000..2124b53
--- /dev/null
@@ -0,0 +1,75 @@
+<& /Elements/Header, Title => 'Time to Resolve in Queue' &>
+<& /RTx/Statistics/Elements/Tabs,  Title => 'Time to Resolve, by ticket in Queue:' . $QueueObj->Name() &>
+
+<h3>Description</h3>
+<p>This page displays the same information as the Time to Resolve chart, but in a scattergraph format and only for the previous 7 calendar
+days. It only displays data for tickets which have been resolved. Each division on the Days axis is one day and the granularity of this chart
+is 30 minutes.</p>
+<form method="POST">
+
+<table>
+  <tr>
+  <td>Show Queue:</td>
+  <td COLSPAN=3><& /Elements/SelectQueue, Name=>"queue", Default=>$queue ,ShowNullOption=>0, 
+            CheckQueueRight=>'SeeQueue' &></td>
+  </tr>
+</table>
+<INPUT TYPE="submit" VALUE="Update Page"</INPUT>
+</form>
+
+<BR>
+% my $url = 'Elements/Chart?x_labels=';
+% my $i;
+% $url .= join ",", (map {(int($_/2) == $_/2 && (++$i)%2) ? $_/2 : ""} grep {$counts[$_]} 0..($#counts-1)), "longer";
+% $url .= '&';
+% $url .= "marker_size=1&";
+% $url .= "data1=".(join ",", map { $_ || () } @counts)."&";
+% chop $url;
+<IMG SRC="<% $url %>">
+
+<BR>
+
+%Statistics::DebugInit($m);
+
+<%ARGS>
+$queue => undef
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+
+my @days = qw(Sun Mon Tue Wed Thu Fri Sat);
+my $n = 0;
+my @data = ([]);
+my @msgs;
+my @counts;
+
+Statistics::DebugClear();
+Statistics::DebugLog("TimeToResolve/index.html ARGS:\n");
+for my $key (keys %ARGS) {
+  Statistics::DebugLog("ARG{ $key }=" . $ARGS{$key} . "\n");
+}
+
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+if (!defined $queue) {
+  $QueueObj->Load($Statistics::TimeToResolveGraphQueue);
+  $queue = $QueueObj->Id();
+} else {
+  $QueueObj->Load($queue);
+}
+
+
+my $tix = new RT::Tickets($session{'CurrentUser'});
+$tix->LimitQueue (VALUE => $queue) if $queue;
+$tix->LimitStatus(VALUE => "resolved");
+$tix->UnLimit;
+if ($tix->Count) {
+    while (my $t = $tix->RT::SearchBuilder::Next) {  # BLOODY HACK
+        my $when = $t->ResolvedObj->Unix - $t->CreatedObj->Unix;
+        next unless $when > 0; # Doubly bloody hack
+        my $max = (60*60*24*2) / 1800;
+        my $x = int($when / 1800);
+        $counts[$x > $max ? $max : $x]++;
+    }
+}
+</%INIT>
diff --git a/rt/share/html/RTx/Statistics/UserTest/Elements/Chart b/rt/share/html/RTx/Statistics/UserTest/Elements/Chart
new file mode 100755 (executable)
index 0000000..99eb2a2
--- /dev/null
@@ -0,0 +1,28 @@
+<%perl>
+print $graph->plot(\@data)->$format();
+$m->abort();
+print $#data+1 . " Elements:<p>";
+for (0..$#data) {
+print $data[$_];
+print "<p>";
+}
+</%perl>
+<%INIT>
+use GD::Graph::lines;
+
+my @data;
+my $graph = GD::Graph::lines->new(640,480);
+$graph->set(export_format => "png",
+            x_label           => 'Days',
+            y_label           => 'Average time in Days');
+
+push @data, [split /,/ , $ARGS{x_labels}];
+push @data, [split /,/ , $ARGS{data1}];
+push @data, [split /,/ , $ARGS{data2}];
+push @data, [split /,/ , $ARGS{data3}];
+
+my $format = $graph->export_format;
+#$r->content_type("image/$format");
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/share/html/RTx/Statistics/UserTest/index.html b/rt/share/html/RTx/Statistics/UserTest/index.html
new file mode 100755 (executable)
index 0000000..7bc25da
--- /dev/null
@@ -0,0 +1,54 @@
+<& /Elements/Header, Title => 'Time to Resolve in Queue' &>
+<& /RTx/Statistics/Elements/Tabs,  Title => 'Time to Resolve, by ticket in Queue:' . $QueueObj->Name() &>
+
+
+<form method="POST">
+
+See Queue:<BR>
+<& /Elements/SelectQueue, Name=>"queue", Default => "$queue" &>
+<BR>
+<INPUT TYPE="submit" VALUE="Go!"</INPUT>
+</form>
+
+<BR>
+% my $url = 'Elements/Chart?x_labels=';
+% my $i;
+% $url .= join ",", (map {(int($_/2) == $_/2 && (++$i)%2) ? $_/2 : ""} grep {$counts[$_]} 0..($#counts-1)), "longer";
+% $url .= '&';
+% $url .= "marker_size=1&";
+% $url .= "data1=".(join ",", map { $_ || () } @counts)."&";
+% chop $url;
+<IMG SRC="<% $url %>">
+
+<BR>
+
+<%ARGS>
+$queue => $Statistics::TimeToResolveGraphQueue;
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+
+my @days = qw(Sun Mon Tue Wed Thu Fri Sat);
+my $n = 0;
+my @data = ([]);
+my @msgs;
+my @counts;
+
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($queue);
+
+my $tix = new RT::Tickets($session{'CurrentUser'});
+$tix->LimitQueue (VALUE => $queue) if $queue;
+$tix->LimitStatus(VALUE => "resolved");
+$tix->UnLimit;
+if ($tix->Count) {
+    while (my $t = $tix->RT::SearchBuilder::Next) {  # BLOODY HACK
+        my $when = $t->ResolvedObj->Unix - $t->CreatedObj->Unix;
+        next unless $when > 0; # Doubly bloody hack
+        my $max = (60*60*24*2) / 1800;
+        my $x = int($when / 1800);
+        $counts[$x > $max ? $max : $x]++;
+    }
+}
+</%INIT>
diff --git a/rt/share/html/RTx/Statistics/index.html b/rt/share/html/RTx/Statistics/index.html
new file mode 100755 (executable)
index 0000000..41490de
--- /dev/null
@@ -0,0 +1,59 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+%# 
+%# (Except where explictly superceded by other copyright notices)
+%# 
+%# Copyright this file (c) 2003 Harald Wagener <hwagener@hamburg.fcb.com>
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%# 
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%# 
+%# Unless otherwise specified, all modifications, corrections or
+%# extensions to this work which alter its source code become the
+%# property of Best Practical Solutions, LLC when submitted for
+%# inclusion in the work.
+%# 
+%# 
+%# END LICENSE BLOCK
+<& /Elements/Header, Title => loc('RT Statistics') &>
+<& /RTx/Statistics/Elements/Tabs,  Title => loc('RT Statistics')  &>
+
+<&|/l&><h2>Description</h2>
+<p>These 6 options below enable you to display management data from the RT Database in table and graphical forms, enabling trends, bottlenecks, load problems etc to be identified.
+Each contains a description of how the data is displayed and describes the options available to you.</p></&>
+<ul>
+<li><strong><a href="CallsQueueDay/index.html">
+<&|/l&>Tickets per day per Queue</&></a></strong><br />
+<&|/l&>View the number of tickets created, resolved or deleted in a<br /> specific Queue, over the requested period of days</&>
+</li>
+<li><strong><a href="OpenStalled/index.html">
+<&|/l&>Tickets status by Queue</&></a></strong><br>
+<&|/l&>View numbers of new, open and stalled tickets in a selected Queue</&>
+</li>
+<li><strong><a href="CallsMultiQueue/index.html">
+<&|/l&>Tickets per Day in Multiple Queues</&>
+</a></strong><br>
+<&|/l&>View tickets created, resolved or deleted on in one or more Queues<br /> over a specified time period</&>
+</li>
+<li><strong><a href="DayOfWeek/index.html">
+<&|/l&>Tickets per Day of Week (absolute)</&></a></strong><br>
+<&|/l&>View trends showing when tickets are created, resolved or deleted</&>
+</li>
+<li><strong><a href="Resolution/index.html">
+<&|/l&>Time to Resolve</&></a></strong><br>
+<&|/l&>View how long tickets take to be resolved by Queue</&>
+</li>
+</li>
+<li><strong><a href="TimeToResolve/index.html">
+<&|/l&>Time to Resolve (scatter graph)</&></a></strong><br>
+<&|/l&>View a detailed scatter graph of time to resolve tickets by Queue</&>
+</li>
+</ul>