So, my advice is to first; return all /share/ code to it’s original state.
Then create $home*/local/*html/Tools/Reports directories in order to
copy/create the files mentioned in the “TimeWorkedReport” documentation.
That’s what I did:
Also, I’m in 3.8.7 so I made sure to follow the 3.8 instructions.
Hope this helps.
Hi Ken,
Thanks for the swift response.
I am not sure what you mean by “copy the …/share/… directories/files
to …/local/… directories/files”. Below are the steps I followed:
- edit /usr/share/rt3/html/Tools/Reports/index.html and add the following
lines in the my $tabs section after:
…
C => {
title => loc('Created in a date range'),
path => '/Tools/Reports/CreatedByDates.html',
description => loc('Examine tickets created in a queue between two
dates’),
},
…
++++++++++ ADD THIS SECTION ++++++++++++++++++++++++
D => {
title => loc('Time Worked Report'),
path => '/Tools/Reports/TimeWorkedReport.html',
description => loc('A Time Worked Report'),
},
++++++++++++++++++++++++++++++++++++++++++++++++++++
- edit /usr/share/rt3/html/Tools/Reports/Elements/Tabs and add the
following lines in the my $tabs section after:
…
c => {
title => loc('Created in a date range'),
path => 'Tools/Reports/CreatedByDates.html',
},
…
++++++++++ ADD THIS SECTION ++++++++++++++++++++++++
d => {
title => loc('Time Worked Report'),
path => 'Tools/Reports/TimeWorkedReport.html',
},
++++++++++++++++++++++++++++++++++++++++++++++++++++
- Create the file /usr/share/rt3/html/Elements/SelectMultiQueue, with the
following content:
%# BEGIN BPS TAGGED BLOCK {{{
%#
%# COPYRIGHT:
%#
%# This software is Copyright (c) 1996-2009 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., 51 Franklin Street, Fifth Floor, Boston, MA
%# 02110-1301 or visit their web page on the internet at
%# GNU General Public License v2.0 - GNU Project - Free Software Foundation.
%#
%#
%# 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 }}}
% if ($Lite) {
% my $d = new RT::Queue($session{‘CurrentUser’});
% $d->Load($Default);
% }
% else {
% # $Default will be an arrayref if multiple queues are selected, or a
% # scalar if 0-1 queues are selected. Hence, this ugly processing
logic.
% my %selected;
% if (ref $Default) {
% for (@$Default) {
% $selected{$_} = 1;
% }
% } else {
% $selected{$Default} = 1;
% }
<select name=“<%$Name%>” <% ($OnChange) ? ‘onchange="’.$OnChange.‘"’ : ‘’
|n %> class=" <%$Class%>" MULTIPLE>
% if ($ShowNullOption) {
-
% }
% for my $queue (@{$session{$cache_key}}) {
<option value="<% ($NamedValues ? $queue->{Name} : $queue->{Id}) %>"
%# if ($queue->{Id} eq ($Default||‘’) || $queue->{Name} eq ($Default||‘’))
{
% if($selected{$queue->{Id}}) {
selected=“selected”
% }
<%$queue->{Name}%>
% if ($Verbose and $queue->{Description}) {
(<%$queue->{Description}%>)
% }
% }
% }
<%args>
$CheckQueueRight => ‘CreateTicket’
$ShowNullOption => 1
$ShowAllQueues => 1
$Name => undef
$Verbose => undef
$NamedValues => 0
$Default => 0
$Lite => 0
$OnChange => undef
$Class => ‘select-queue’
</%args>
<%init>
my $cache_key = “SelectQueue—”
. $session{'CurrentUser'}->Id
. "---$CheckQueueRight---$ShowAllQueues";
if (not defined $session{$cache_key} and not $Lite) {
my $q = new RT::Queues($session{'CurrentUser'});
$q->UnLimit;
while (my $queue = $q->Next) {
if ($ShowAllQueues ||
$queue->CurrentUserHasRight($CheckQueueRight)) {
push @{$session{$cache_key}}, {
Id => $queue->Id,
Name => $queue->Name,
Description => $queue->Description,
};
}
}
}
</%init>
- Create the file /usr/share/rt3/html/Tools/Reports/TimeWorkedReport.html,
with the following content:
<%args>
$startdate => undef
$enddate => undef
$queues => undef
$byticket => undef
</%args>
<& /Elements/Header, Title => $title &>
<& /Tools/Reports/Elements/Tabs, current_tab =>
‘Tools/Reports/TimeWorkedReport.html’, Title => $title &>
<%init>
my ($start_date, $end_date, $effective_end_date, $title);
$title = loc(‘Time worked report’);
$start_date = RT::Date->new($session{‘CurrentUser’});
$end_date = RT::Date->new($session{‘CurrentUser’});
If we have a value for start date, parse it into an RT::Date object
if ($startdate) {
$start_date->Set(Format => ‘unknown’, Value => $startdate);
And then get it back as an ISO string for display purposes, in the
form field and
report header
$startdate = $start_date->AsString(Format => ‘ISO’, Timezone =>
‘server’);
}
Same treatment for end date
if ($enddate) {
$end_date->Set(Format => ‘unknown’, Value => $enddate);
$enddate = $end_date->AsString(Format => ‘ISO’, Timezone => ‘server’);
}
</%init>
<&|/l&>Start date</&>:
<& /Elements/SelectDate, Name => ‘startdate’, Default => ($startdate) ?
$start_date->AsString(Format => ‘ISO’, Timezone => ‘server’) : ‘’&>
(report will start from midnight on this day unless you indicate
otherwise)
<&|/l&>End date</&>:
<& /Elements/SelectDate, Name => ‘enddate’, Default => ($enddate) ?
$end_date->AsString(Format => ‘ISO’, Timezone => ‘server’) : ‘’&>
(report will -not- be inclusive of this day unless you change the time
from midnight)
<&|/l&>Queues</&>:
<& /Elements/SelectMultiQueue, Name => ‘queues’, Default => ($queues) ?
$queues : ‘’&>
<& /Elements/Checkbox, Name => ‘byticket’, Default => ($byticket) ?
‘checked’ : ‘’&>
Organize report by ticket instead of by person
<& /Elements/Submit&>
<%perl>
TimeWorkedReport
Version 0.04 2009-09-28
Fran Fabrizio, UAB CIS, fran@cis.uab.edu
use strict;
if we are just getting here and the form values are empty, we are done
if (!$startdate || !$enddate) {
return;
}
get the queue object(s)
my $queuesobj = new RT::Queues($session{CurrentUser});
my ($queuelist, %queuesofinterest);
The user’s choice of queues will come in from the web form in the
$queues variable, which is
mapped to the SELECT field on the web interface for the report.
Unfortunately, if the user
chooses just one queue, $queues will have a scalar value, but if the
user chooses multiple
queues, it will be an arrayref. So we need to check for each case and
process differently.
What we want to construct is the %queuesofinterest simple lookup hash
which defines a key
that is the queue ID for each queue selected, and the $queuelist string,
which is just for
displaying the list of queues in the report header
if (ref $queues) {
multiple queues selected
for (@$queues) {
$queuesobj->Limit(FIELD => "Id", OPERATOR => "=", VALUE => $_,
ENTRYAGGREGATOR => “OR”);
$queuesofinterest{$_} = 1;
}
$queuelist = join ", ", map {$_->Name} @{$queuesobj->ItemsArrayRef};
} else {
my $queue = new RT::Queue;
$queue->Load(Id => $queues);
$queuesofinterest{$queues} = 1;
$queuelist = $queue->Name;
}
hash to hold statistics
%stats will be a multilevel hash - first level keys are the usernames,
second level keys are
the ticket IDs, and for each ticket, we store an anonymous hash with
keys Subject and TimeWorked
(this implies that a single ticket can live under two+ users if they
both worked the ticket)
my %stats;
Get a new transactions object to hold transaction search results for
this ticket
my $trans = new RT::Transactions($session{‘CurrentUser’});
only in the period of interest
$trans->Limit(FIELD => ‘Created’, OPERATOR => ‘>’, VALUE => $startdate);
$trans->Limit(FIELD => ‘Created’, OPERATOR => ‘<’, VALUE => $enddate,
ENTRYAGGREGATOR => ‘AND’);
now start counting all the TimeTaken by examining transactions
associated with this ticket
while (my $tr = $trans->Next) {
did this transaction take any time? RT records this -either- in
TimeTaken column or by
indicating “TimeWorked” in the Field column, depending on how the user
inputted the time.
if (($tr->TimeTaken != 0) || ($tr->Field && $tr->Field eq ‘TimeWorked’))
{
# Got a hot one - what ticket is this?
my $t = new RT::Ticket($session{'CurrentUser'});
$t->Load($tr->ObjectId);
if (!$t) {
# unable to retrieve a ticket for this transaction
# hopefully we don't ever reach here!
next;
} else {
# Is this ticket in a queue we care about?
if (!$queuesofinterest{$t->Queue}) {
next;
}
}
# If this is time logged by user RT_System, it's the result of a ticket
merge
# In order to avoid double-counting minutes in --byticket mode, or the
less serious
# issue of displaying a report for user RT_System in normal mode, we
skip this entirely
if ($tr->CreatorObj->Name eq 'RT_System') {
next;
}
# we've got some time to account for
# is this the first time this person is charging time to this ticket?
# if so, add this ticket subject to the data structure
if (!exists($stats{$tr->CreatorObj->Name}{$t->id}{Subject})) {
$stats{$tr->CreatorObj->Name}{$t->id}{Subject} = $t->Subject;
}
if ($tr->TimeTaken != 0) {
# this was a comment or correspondence where the user also added some
time worked
# value of interest appears in Transaction's TimeTaken column
$stats{$tr->CreatorObj->Name}{$t->id}{TimeWorked} += $tr->TimeTaken;
} else {
# this was a direct update of the time worked field from the Basics
or Jumbo ticket update page
# values of interest appear in Transaction's OldValue and NewValue
columns
# RT does not use the TimeTaken column in this instance.
$stats{$tr->CreatorObj->Name}{$t->id}{TimeWorked} += $tr->NewValue -
$tr->OldValue;
}
}
}
report output starts here
output:
normal user: their own time worked report, most worked ticket to least
worked ticket
superuser: everyone’s time worked report, in username alpha order,
then by most worked to least worked
superuser+byticket: most worked ticket first, with everyone’s
contribution ranked by biggest contribution to smallest
print "
TIME WORKED REPORT FOR QUEUE(S) " . $queuelist . “
”;
print “
Date Range: $startdate TO $enddate
”;
if ($byticket) {
print “
Organized by Ticket
”;
}
print “
”;
if this person is not a superuser, we should only show them the report
for themselves
which means we should remove all keys from %stats except their own
username
if (!($session{‘CurrentUser’}->HasRight(Right => ‘SuperUser’, Object =>
$RT::System))) {
my %tempstats;
$tempstats{$session{CurrentUser}->Name} =
$stats{$session{CurrentUser}->Name};
%stats = %tempstats;
}
if ($byticket) {
if we’re going to organize this by ticket, we need to transform the
data first
HAVE ENTRIES LIKE: $stats{JoeUser}{12345}{TimeWorked} = 150
$stats{JoeUser}{12345}{Subject} = "Fix the Fubar
Widget"
WANT ENTRIES LIKE: $tstats{12345}{TotalTime} = 250
$tstats{12345}{Subject} = “Fix the Fubar Widget”
$tstats{12345}{People}{JoeUser} = 150
$tstats{12345}{People}{JaneDoe} = 100
my %tstats;
for my $person (keys %stats) {
for my $tid (keys %{$stats{$person}}) {
# grab the subject line if you don't have it already
if (!exists($tstats{$tid}{Subject})) {
$tstats{$tid}{Subject} = $stats{$person}{$tid}{Subject};
}
# now increment total time for this ticket
$tstats{$tid}{TotalTime} += $stats{$person}{$tid}{TimeWorked};
# and record this user's contribution to this ticket
$tstats{$tid}{People}{$person} = $stats{$person}{$tid}{TimeWorked};
}
}
Now emit the report
for my $tid (sort {$tstats{$b}{TotalTime} <=> $tstats{$a}{TotalTime}}
keys %tstats) {
my $subject = $tstats{$tid}{Subject};
print "<H3><A TARGET=\"_TimeWorked\"
HREF="/Ticket/Display.html?id=$tid">$tid: $subject";
print "<TABLE BORDER=0 CELLSPACING=5>";
printf("<TR><TH WIDTH=30></TH><TH>%dm</TH><TH>%.1fh</TH><TH>TOTAL
TIME", $tstats{$tid}{TotalTime},($tstats{$tid}{TotalTime} / 60));
for my $person (sort {$tstats{$tid}{People}{$b} <=>
$tstats{$tid}{People}{$a}} keys %{$tstats{$tid}{People}}) {
my $minutes = $tstats{$tid}{People}{$person};
printf(“%dm%.1fh%s”,$minutes,($minutes
/60),$person);
}
print "</TABLE>";
}
} else {
the existing %stats data structure is perfect for the default report,
no data transform needed
for my $person (sort keys %stats) {
# get the person object, so we can get the FriendlyName to use as
header
my $personobj = new RT::User($session{CurrentUser});
$personobj->Load($person);
print "<h3>" . $personobj->FriendlyName . "</h3>";
print "<TABLE BORDER=0 CELLSPACING=5>";
print "<TR><TH>MINUTES</TH><TH>HOURS</TH><TH>TICKET</TH></TR>";
my $totalMinutes = 0;
for my $tid (sort {$stats{$person}{$b}{TimeWorked} <=>
$stats{$person}{$a}{TimeWorked}} keys %{$stats{$person}}) {
my $minutes = $stats{$person}{$tid}{TimeWorked};
my $subject = $stats{$person}{$tid}{Subject};
print "<TR><TD ALIGN=RIGHT>${minutes}m</TD><TD ALIGN=RIGHT>" .
sprintf(“%.1fh”,($minutes/60)) . “” .
"<TD><A TARGET=\"_TimeWorked\"
HREF="/Ticket/Display.html?id=$tid">$tid: $subject";
$totalMinutes += $minutes;
}
print "<TR><TD ALIGN=RIGHT><B>${totalMinutes}m</B></TD><TD
ALIGN=RIGHT>" . sprintf(“%.1fh”,($totalMinutes/60)) .
“TOTALS”;
print "</TABLE>";
}
}
helper functions below
sub form_date_string {
expects seven input params - year, month, day, hour, minute, second,
offset
my $year = $_[0] - 1900;
my $mon = $_[1] - 1;
my $day = $_[2];
my $hour = $[3] ? $[3] : 0;
my $min = $[4] ? $[4] : 0;
my $sec = $[5] ? $[5] : 0;
my $offset = $[6] ? $[6] : 0;
convert to seconds since epoch, then adjust for the $offset, which is
also in seconds
we do this so we don’t have to do fancy date arithmetic - we can just
subtract one seconds
value from the other seconds value
my $starttime = timelocal($sec,$min,$hour,$day,$mon,$year) - $offset;
convert back to component parts now that we’ve adjusted for offset
this gives us the components which represent the GMT time for the local
time that was entered
on the command line
($sec,$min,$hour,$day,$mon,$year) = localtime($starttime);
format the date string, padding with zeros if needed
return sprintf(“%04d-%02d-%02d %02d:%02d:%02d”, ($year+1900), ($mon+1),
$day, $hour, $min, $sec);
}
</%perl>
- Restart the RT Server
Thanking you in anticipation,
Walid
From: rt-users-bounces@lists.bestpractical.com [mailto:
rt-users-bounces@lists.bestpractical.com] *On Behalf Of *Kenneth Crocker
Sent: quinta-feira, 24 de Março de 2011 17:02
To: rt-users@lists.bestpractical.com
Subject: Re: [rt-users] Time Worked Report
Walid,
Did you copy the …/share/… directories/files to …/local/…
directories/files and make your modifications there? That a WAY safer way to
make modifications. You may have forgotten one and that is the problem?
Kenn
LBNL
On Thu, Mar 24, 2011 at 9:51 AM, Walid Haider walid.haider@movensis.com wrote:
Hi,
I am a bit of a newbie with RT, and was wondering if you could help me.
After following the installation your instructions at
http://requesttracker.wikia.com/wiki/TimeWorkedReport, I get the following
errors when clicking on the Time Worked Report link in RT:
Error during compilation of /usr/share/rt3/html/Elements/SelectMultiQueue:
Global symbol “$d” requires explicit package name at
/usr/share/rt3/html/Elements/SelectMultiQueue line 51. Global symbol
“$queue” requires explicit package name at
/usr/share/rt3/html/Elements/SelectMultiQueue line 69. Global symbol
“$queue” requires explicit package name at
/usr/share/rt3/html/Elements/SelectMultiQueue line 69. Global symbol
“$queue” requires explicit package name at
/usr/share/rt3/html/Elements/SelectMultiQueue line 77. Global symbol
“$queue” requires explicit package name at
/usr/share/rt3/html/Elements/SelectMultiQueue line 80.
Although the requirement states RT 3.8.5, I decided to give it a go on
3.8.4. Must I definitely upgrade in order to get this report to work?
I am running RT on the following setup:
Fedora Core 12, Apache 2.2.14, MYSQL 5.1
Thanks in advance.
Regards,
Walid