[Bps-public-commit] rt-extension-timetracking branch, master, updated. 0.20-6-g6547d68

? sunnavy sunnavy at bestpractical.com
Thu Aug 13 01:41:01 EDT 2020


The branch, master has been updated
       via  6547d6834e64cf5352508c7b26719e4f5ef5c4fe (commit)
       via  90cd423066e2e8ab7f0f8b7138d38d963f2ec7c5 (commit)
       via  cc86b8620eb42ff7ab830c8114774cc742a95af9 (commit)
       via  c24ffaa87486d15f2de9bca76304632ef491a0ce (commit)
       via  5034952fd385aeaa304e6501dcf06813fca70d8a (commit)
       via  064902a15839b03d4b3b05634efcf20a6afb630a (commit)
      from  3a3308b8ebca413ed9e60be6912065a2a1cf67ad (commit)

Summary of changes:
 Changes                                            |   3 +
 MANIFEST                                           |   1 +
 META.yml                                           |   2 +-
 .../Elements/Tabs/Privileged                       |   8 +
 html/Tools/TimeSearch.html                         | 242 +++++++++++++++++++++
 lib/RT/Extension/TimeTracking.pm                   |  25 ++-
 static/css/time_tracking.css                       |   4 +
 7 files changed, 283 insertions(+), 2 deletions(-)
 create mode 100644 html/Tools/TimeSearch.html

- Log -----------------------------------------------------------------
commit 064902a15839b03d4b3b05634efcf20a6afb630a
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Wed Jul 11 04:40:02 2018 +0800

    Initial version of time search page
    
    This page makes it easier to get worked time for specified tickets(via
    search query) between 2 dates.

diff --git a/html/Callbacks/RT-Extension-TimeTracking/Elements/Tabs/Privileged b/html/Callbacks/RT-Extension-TimeTracking/Elements/Tabs/Privileged
index 3aeeb1f..9450664 100644
--- a/html/Callbacks/RT-Extension-TimeTracking/Elements/Tabs/Privileged
+++ b/html/Callbacks/RT-Extension-TimeTracking/Elements/Tabs/Privileged
@@ -7,6 +7,14 @@ Menu()->child('tools')->child(
     sort_order  => 2.5,
 );
 
+Menu()->child('tools')->child(
+    'time_search',
+    title       => loc("Time Search"),
+    description => loc("Find time worked for specified tickets between dates"),
+    path        => '/Tools/TimeSearch.html',
+    sort_order  => 2.6,
+);
+
 my $request_path = $HTML::Mason::Commands::r->path_info;
 $request_path =~ s!/{2,}!/!g;
 if ( $request_path =~ m{^/Tools/MyWeek.html} && $DECODED_ARGS->{'User'} && $DECODED_ARGS->{'User'} != $session{CurrentUser}->id ) {
diff --git a/html/Tools/TimeSearch.html b/html/Tools/TimeSearch.html
new file mode 100644
index 0000000..b8693b3
--- /dev/null
+++ b/html/Tools/TimeSearch.html
@@ -0,0 +1,232 @@
+<& /Elements/Header, Title => loc("Time Search") &>
+
+<& /Elements/Tabs &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+<div class="time_tracking time_search">
+
+<form>
+<table>
+<tr>
+    <td class="label"><&|/l&>Ticket Query</&>:</td>
+    <td class="value"><textarea name="Query" rows="8" cols="72"><% $Query %></textarea></td>
+</tr>
+<tr>
+    <td class="label"><&|/l&>Start Date</&>:</td>
+    <td class="value"><& /Elements/SelectDate, ShowTime => 0, Name => 'StartDate', Default => $start_date->Date(Format=>'ISO', Timezone => 'user') &></td>
+</tr>
+<tr>
+    <td class="label"><&|/l&>End Date</&>:</td>
+    <td class="value"><& /Elements/SelectDate, ShowTime => 0, Name => 'EndDate', Default => $end_date->Date(Format=>'ISO', Timezone => 'user') &></td>
+</tr>
+</table>
+<& /Elements/Submit, Name => 'DoSearch', Label => loc("Search") &>
+</form>
+
+% if ( $ARGS{DoSearch} ) {
+
+<div>
+
+% if ( %ticket_worked ) {
+<table class="ticket-list collection-as-table">
+    <tr class="collection-as-table">
+        <th class="collection-as-table"><&|/l&>id</&></th>
+        <th class="collection-as-table"><&|/l&>Subject</&></th>
+        <th class="collection-as-table"><&|/l&>Queue</&></th>
+        <th class="collection-as-table"><&|/l&>Status</&></th>
+        <th class="collection-as-table"><&|/l&>Owner</&></th>
+% if ( $display_cf ){
+        <th class="collection-as-table"><% $display_cf %></th>
+% }
+        <th class="collection-as-table"><&|/l&>Time Worked</&></th>
+        <th class="collection-as-table"><&|/l&>Time Estimated</&></th>
+    </tr>
+% my $i = 1;
+% for my $ticket_id ( sort { $a <=> $b } keys %ticket_worked ) {
+% my $entry = $ticket_worked{$ticket_id};
+% my $ticket = $entry->{ticket};
+    <tr class="<% $i++ % 2 ? 'oddline' : 'evenline' %>">
+        <td class="collection-as-table">
+        <a href="<% RT->Config->Get('WebPath') %>/Ticket/Display.html?id=<% $ticket->id %>"><% $ticket->id %></a>
+        </td>
+        <td class="collection-as-table">
+        <a href="<% RT->Config->Get('WebPath') %>/Ticket/Display.html?id=<% $ticket->id %>"><% $ticket->Subject %></a>
+        </td>
+        <td class="collection-as-table"><% $ticket->QueueObj->Name %></td>
+        <td class="collection-as-table"><% $ticket->Status %></td>
+        <td class="collection-as-table"><% $ticket->OwnerObj->Name %></td>
+% if ( $display_cf ){
+        <td class="collection-as-table"><% $ticket->FirstCustomFieldValue($display_cf) %></td>
+% }
+        <td class="collection-as-table"><& /Ticket/Elements/ShowTime, minutes => $entry->{time_worked} &></td>
+        <td class="collection-as-table"><& /Ticket/Elements/ShowTime, minutes => $ticket->TimeEstimated &></td>
+    </tr>
+% }
+</table>
+% }
+
+</div>
+
+<hr />
+<div class="time_worked">
+<span class="label"><&|/l&>Total Time Worked</&>:</span> <span class="value"><& /Ticket/Elements/ShowTime, minutes => $total_time_worked &></span>
+</div>
+% }
+
+% $m->callback( CallbackName => 'End', Query => $Query, StartDate => $start_date, EndDate => $end_date, TicketWorked => \%ticket_worked );
+
+</div>
+<%INIT>
+
+my @results;
+my $user = $session{CurrentUser};
+
+my $start_date = RT::Date->new( $user );
+if ( $StartDate ) {
+    $start_date->Set( Value => $StartDate, Format => 'unknown' );
+}
+else {
+    $start_date->SetToNow;
+    my ( $ret, $week_start_date ) =
+      RT::Extension::TimeTracking::WeekStartDate( $user, $start_date, RT->Config->Get( 'TimeTrackingFirstDayOfWeek' ) );
+    $start_date = $week_start_date if $ret;
+}
+$start_date->SetToMidnight( Timezone => 'user' );
+
+my $end_date = RT::Date->new( $user );
+if ( $EndDate ) {
+    $end_date->Set( Value => $EndDate, Format => 'unknown' );
+}
+else {
+    $end_date->SetToNow;
+}
+$end_date->SetToMidnight( Timezone => 'user' );
+
+my $exclusive_end_date = RT::Date->new( $user );
+$exclusive_end_date->Set( Value => $end_date->Unix, Format => 'Unix' );
+$exclusive_end_date->AddDays( 1 );
+
+my %ticket_worked;
+my $total_time_worked = 0;
+
+# Do we need to load a CF for display?
+my $display_cf;
+if ( $display_cf = RT->Config->Get( 'TimeTrackingDisplayCF' ) ) {
+    my $confirm_cf = RT::CustomField->new( RT->SystemUser );
+    my ( $ret, $msg ) = $confirm_cf->Load( $display_cf );
+
+    if ( not $ret ) {
+        RT::Logger->error(
+            "Unable to load custom field $display_cf " . "defined via config option TimeTrackingDisplayCF: $msg" );
+        undef $display_cf;
+    }
+}
+
+if ( $ARGS{DoSearch} ) {
+    my $tickets = RT::Tickets->new( $user );
+    if ( $Query ) {
+        my ( $ret, $msg ) = $tickets->FromSQL( $Query );
+        push @results, $msg unless $ret;
+    }
+    else {
+        push @results, loc( 'No query' );
+    }
+    MaybeRedirectForResults(
+        Actions   => \@results,
+        Arguments => { StartDate => $StartDate, EndDate => $EndDate, Query => $Query },
+    );
+    my @ticket_ids = map { $_->id } @{ $tickets->ItemsArrayRef };
+
+    my $txns = RT::Transactions->new( $user );
+    $txns->Limit(
+        FIELD => 'ObjectType',
+        VALUE => 'RT::Ticket',
+    );
+    $txns->Limit(
+        FIELD    => 'ObjectID',
+        VALUE    => \@ticket_ids,
+        OPERATOR => 'IN',
+    );
+
+    $txns->Limit(
+        FIELD    => 'TimeTaken',
+        VALUE    => 0,
+        OPERATOR => '!=',
+    );
+
+    $txns->Limit(
+        FIELD    => 'Created',
+        VALUE    => $start_date->ISO(),
+        OPERATOR => '>=',
+    );
+    $txns->Limit(
+        FIELD           => 'Created',
+        VALUE           => $exclusive_end_date->ISO(),
+        OPERATOR        => '<',
+        ENTRYAGGREGATOR => 'AND',
+    );
+
+    while ( my $txn = $txns->Next ) {
+        my $ticket = $txn->Object;
+        next if $txn->FirstCustomFieldValue( 'Worked Date' );    # we handle this in the next part
+        $ticket_worked{ $ticket->id } ||= { ticket => $ticket, };
+        $ticket_worked{ $ticket->id }{time_worked} += $txn->TimeTaken;
+        $total_time_worked += $txn->TimeTaken;
+    }
+
+    $txns = RT::Transactions->new( $user );
+    $txns->Limit(
+        FIELD => 'ObjectType',
+        VALUE => 'RT::Ticket',
+    );
+    $txns->Limit(
+        FIELD    => 'ObjectID',
+        VALUE    => \@ticket_ids,
+        OPERATOR => 'IN',
+    );
+
+    $txns->Limit(
+        FIELD    => 'TimeTaken',
+        VALUE    => 0,
+        OPERATOR => '!=',
+    );
+
+    my $cf = RT::CustomField->new( $user );
+    $cf->Load( 'Worked Date' );
+    my $cf_alias = $txns->Join(
+        ALIAS1 => 'main',
+        FIELD1 => 'id',
+        TABLE2 => 'ObjectCustomFieldValues',
+        FIELD2 => 'ObjectId'
+    );
+    $txns->Limit( ALIAS => $cf_alias, FIELD => 'CustomField', VALUE => $cf->id );
+    $txns->Limit( ALIAS => $cf_alias, FIELD => 'ObjectType',  VALUE => 'RT::Transaction' );
+    $txns->Limit(
+        ALIAS    => $cf_alias,
+        FIELD    => 'Content',
+        VALUE    => $start_date->ISO( Time => 0, Timezone => 'user' ),
+        OPERATOR => '>='
+    );
+    $txns->Limit(
+        ALIAS           => $cf_alias,
+        FIELD           => 'Content',
+        VALUE           => $exclusive_end_date->ISO( Time => 0, Timezone => 'user' ),
+        OPERATOR        => '<',
+        ENTRYAGGREGATOR => 'AND',
+    );
+
+    while ( my $txn = $txns->Next ) {
+        my $ticket = $txn->Object;
+        $ticket_worked{ $ticket->id } ||= { ticket => $ticket, };
+        $ticket_worked{ $ticket->id }{time_worked} += $txn->TimeTaken;
+        $total_time_worked += $txn->TimeTaken;
+    }
+}
+</%INIT>
+
+<%ARGS>
+$Query => undef
+$StartDate => undef
+$EndDate => undef
+</%ARGS>
diff --git a/static/css/time_tracking.css b/static/css/time_tracking.css
index 7e70767..5a6dba1 100644
--- a/static/css/time_tracking.css
+++ b/static/css/time_tracking.css
@@ -24,6 +24,7 @@ div.time_worked_day {
     right: 5px;
 }
 
+div.time_search div.time_worked,
 div.time_worked_week {
     text-align: right;
 }

commit 5034952fd385aeaa304e6501dcf06813fca70d8a
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Tue Aug 21 05:14:58 2018 +0800

    Add an option to automatically search all the children on TimeSearch page

diff --git a/html/Tools/TimeSearch.html b/html/Tools/TimeSearch.html
index b8693b3..1ff4bb0 100644
--- a/html/Tools/TimeSearch.html
+++ b/html/Tools/TimeSearch.html
@@ -20,6 +20,10 @@
     <td class="label"><&|/l&>End Date</&>:</td>
     <td class="value"><& /Elements/SelectDate, ShowTime => 0, Name => 'EndDate', Default => $end_date->Date(Format=>'ISO', Timezone => 'user') &></td>
 </tr>
+<tr>
+    <td class="label"><&|/l&>Include All Children?</&></td>
+    <td class="value"><& /Widgets/Form/Boolean:InputOnly, Name => 'IncludeChildren', CurrentValue => $IncludeChildren &></td>
+</tr>
 </table>
 <& /Elements/Submit, Name => 'DoSearch', Label => loc("Search") &>
 </form>
@@ -136,7 +140,10 @@ if ( $ARGS{DoSearch} ) {
         Actions   => \@results,
         Arguments => { StartDate => $StartDate, EndDate => $EndDate, Query => $Query },
     );
-    my @ticket_ids = map { $_->id } @{ $tickets->ItemsArrayRef };
+
+    my @tickets = map { $_, $IncludeChildren ? ( RT::Extension::TimeTracking->AllChildren( $_ ) ) : () }
+      @{ $tickets->ItemsArrayRef };
+    my @ticket_ids = List::MoreUtils::uniq( map { $_->id } @tickets );
 
     my $txns = RT::Transactions->new( $user );
     $txns->Limit(
@@ -229,4 +236,5 @@ if ( $ARGS{DoSearch} ) {
 $Query => undef
 $StartDate => undef
 $EndDate => undef
+$IncludeChildren => 1
 </%ARGS>
diff --git a/lib/RT/Extension/TimeTracking.pm b/lib/RT/Extension/TimeTracking.pm
index 830c8fb..fd3a70d 100644
--- a/lib/RT/Extension/TimeTracking.pm
+++ b/lib/RT/Extension/TimeTracking.pm
@@ -30,6 +30,29 @@ our %WEEK_INDEX = (
     Saturday  => 6,
 );
 
+sub AllChildren {
+    my $self = shift;
+    my $ticket = shift or return;
+
+    my @tickets;
+    my %seen;
+
+    my $find_children;
+    $find_children = sub {
+        my $parent = shift;
+        my $links  = $parent->Members;
+        while ( my $link = $links->Next ) {
+            my $obj = $link->BaseObj;
+            next unless $obj && UNIVERSAL::isa( $obj, 'RT::Ticket' );
+            next if $seen{ $obj->id }++;
+            push @tickets, $obj;
+            $find_children->( $obj );
+        }
+    };
+    $find_children->( $ticket );
+    return @tickets;
+}
+
 =head1 NAME
 
 RT-Extension-TimeTracking - Time Tracking Extension

commit c24ffaa87486d15f2de9bca76304632ef491a0ce
Author: craig kaiser <craig at bestpractical.com>
Date:   Mon Aug 10 13:55:53 2020 -0400

    Add 'table' Boostrap4 class to search results table

diff --git a/html/Tools/TimeSearch.html b/html/Tools/TimeSearch.html
index 1ff4bb0..7ce37b8 100644
--- a/html/Tools/TimeSearch.html
+++ b/html/Tools/TimeSearch.html
@@ -33,7 +33,7 @@
 <div>
 
 % if ( %ticket_worked ) {
-<table class="ticket-list collection-as-table">
+<table class="table ticket-list collection-as-table">
     <tr class="collection-as-table">
         <th class="collection-as-table"><&|/l&>id</&></th>
         <th class="collection-as-table"><&|/l&>Subject</&></th>

commit cc86b8620eb42ff7ab830c8114774cc742a95af9
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Thu Aug 13 13:23:03 2020 +0800

    Migrate TimeSearch page to elevator themes

diff --git a/html/Tools/TimeSearch.html b/html/Tools/TimeSearch.html
index 7ce37b8..3d14aea 100644
--- a/html/Tools/TimeSearch.html
+++ b/html/Tools/TimeSearch.html
@@ -7,25 +7,27 @@
 <div class="time_tracking time_search">
 
 <form>
-<table>
-<tr>
-    <td class="label"><&|/l&>Ticket Query</&>:</td>
-    <td class="value"><textarea name="Query" rows="8" cols="72"><% $Query %></textarea></td>
-</tr>
-<tr>
-    <td class="label"><&|/l&>Start Date</&>:</td>
-    <td class="value"><& /Elements/SelectDate, ShowTime => 0, Name => 'StartDate', Default => $start_date->Date(Format=>'ISO', Timezone => 'user') &></td>
-</tr>
-<tr>
-    <td class="label"><&|/l&>End Date</&>:</td>
-    <td class="value"><& /Elements/SelectDate, ShowTime => 0, Name => 'EndDate', Default => $end_date->Date(Format=>'ISO', Timezone => 'user') &></td>
-</tr>
-<tr>
-    <td class="label"><&|/l&>Include All Children?</&></td>
-    <td class="value"><& /Widgets/Form/Boolean:InputOnly, Name => 'IncludeChildren', CurrentValue => $IncludeChildren &></td>
-</tr>
-</table>
-<& /Elements/Submit, Name => 'DoSearch', Label => loc("Search") &>
+  <div class="form-row">
+    <div class="col-3 label"><&|/l&>Ticket Query</&>:</div>
+    <div class="col-9 value"><textarea name="Query" rows="8" cols="72"><% $Query %></textarea></div>
+  </div>
+  <div class="form-row">
+      <div class="col-3 label"><&|/l&>Start Date</&>:</div>
+      <div class="col-9 value"><& /Elements/SelectDate, ShowTime => 0, Name => 'StartDate', Default => $start_date->Date(Format=>'ISO', Timezone => 'user') &></div>
+  </div>
+  <div class="form-row">
+      <div class="col-3 label"><&|/l&>End Date</&>:</div>
+      <div class="col-9 value"><& /Elements/SelectDate, ShowTime => 0, Name => 'EndDate', Default => $end_date->Date(Format=>'ISO', Timezone => 'user') &></div>
+  </div>
+  <div class="form-row include-children">
+      <div class="col-3 label"><&|/l&>Include All Children?</&></div>
+      <div class="col-9 value"><& /Widgets/Form/Boolean:InputOnly, Name => 'IncludeChildren', CurrentValue => $IncludeChildren &></div>
+  </div>
+  <div class="form-row">
+    <div class="col-12">
+      <& /Elements/Submit, Name => 'DoSearch', Label => loc("Search") &>
+    </div>
+  </div>
 </form>
 
 % if ( $ARGS{DoSearch} ) {
diff --git a/static/css/time_tracking.css b/static/css/time_tracking.css
index 5a6dba1..6627c70 100644
--- a/static/css/time_tracking.css
+++ b/static/css/time_tracking.css
@@ -38,3 +38,6 @@ div.time_tracking h2 {
     margin-top: 30px;
 }
 
+div.time_tracking div.include-children div.value {
+    margin-top: 5px;
+}

commit 90cd423066e2e8ab7f0f8b7138d38d963f2ec7c5
Merge: 3a3308b cc86b86
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Thu Aug 13 13:23:24 2020 +0800

    Merge branch 'time-search'


commit 6547d6834e64cf5352508c7b26719e4f5ef5c4fe
Author: sunnavy <sunnavy at bestpractical.com>
Date:   Thu Aug 13 13:25:14 2020 +0800

    Prep 0.21

diff --git a/Changes b/Changes
index bc5fede..7e1d120 100644
--- a/Changes
+++ b/Changes
@@ -1,5 +1,8 @@
 Revision history for RT-Extension-TimeTracking
 
+0.21 2020-08-13
+ - Add "Time Search" page
+
 0.20 2020-07-24
  - Add RT 5 support and drop RT 4 support
 
diff --git a/MANIFEST b/MANIFEST
index a08ccc4..1ebaf08 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -10,6 +10,7 @@ html/Callbacks/RT-Extension-TimeTracking/Ticket/Elements/EditTransactionCustomFi
 html/Callbacks/RT-Extension-TimeTracking/Ticket/Elements/ShowBasics/EndOfList
 html/Elements/ShowCustomFieldActor
 html/Tools/MyWeek.html
+html/Tools/TimeSearch.html
 inc/Module/AutoInstall.pm
 inc/Module/Install.pm
 inc/Module/Install/AutoInstall.pm
diff --git a/META.yml b/META.yml
index 1c1841d..9830380 100644
--- a/META.yml
+++ b/META.yml
@@ -26,6 +26,6 @@ requires:
 resources:
   license: http://opensource.org/licenses/gpl-license.php
   repository: https://github.com/bestpractical/rt-extension-timetracking
-version: '0.20'
+version: '0.21'
 x_module_install_rtx_version: '0.42'
 x_requires_rt: 5.0.0
diff --git a/lib/RT/Extension/TimeTracking.pm b/lib/RT/Extension/TimeTracking.pm
index fd3a70d..a34ba2e 100644
--- a/lib/RT/Extension/TimeTracking.pm
+++ b/lib/RT/Extension/TimeTracking.pm
@@ -2,7 +2,7 @@ use strict;
 use warnings;
 package RT::Extension::TimeTracking;
 
-our $VERSION = '0.20';
+our $VERSION = '0.21';
 
 RT->AddStyleSheets("time_tracking.css");
 RT->AddJavaScript("time_tracking.js");

-----------------------------------------------------------------------


More information about the Bps-public-commit mailing list