[Bps-public-commit] rt-extension-automaticassignment branch, master, updated. 053cfce659a4ef7316435fc34e5698c78f081a0b

Shawn Moore shawn at bestpractical.com
Wed Jul 20 18:10:23 EDT 2016


The branch, master has been updated
       via  053cfce659a4ef7316435fc34e5698c78f081a0b (commit)
       via  55de2c29fc8c68a4b1dcd7c8af479830eac590ca (commit)
       via  ae6c5a905c1f33998a6ef90e3a8afdf77bedddf9 (commit)
       via  8335ffd9a6cda8493307a3898b849acbeae24d72 (commit)
      from  54b61624de5a794ee2168cac6c8bc321492400df (commit)

Summary of changes:
 META.yml                                           |   1 +
 README                                             |   8 ++
 etc/initialdata                                    |  13 +++
 lib/RT/Action/AutomaticAssignment.pm               |  31 ++++++
 lib/RT/Action/AutomaticReassignment.pm             |  12 +++
 lib/RT/Extension/AutomaticAssignment.pm            |  69 +++++++++++++
 lib/RT/Extension/AutomaticAssignment/Chooser.pm    |  10 ++
 .../AutomaticAssignment/Chooser/Ownership.pm       | 115 +++++++++++++++++++++
 .../AutomaticAssignment/Chooser/Random.pm          |  14 +++
 9 files changed, 273 insertions(+)
 create mode 100644 etc/initialdata
 create mode 100644 lib/RT/Action/AutomaticAssignment.pm
 create mode 100644 lib/RT/Action/AutomaticReassignment.pm
 create mode 100644 lib/RT/Extension/AutomaticAssignment/Chooser.pm
 create mode 100644 lib/RT/Extension/AutomaticAssignment/Chooser/Ownership.pm
 create mode 100644 lib/RT/Extension/AutomaticAssignment/Chooser/Random.pm

- Log -----------------------------------------------------------------
commit 8335ffd9a6cda8493307a3898b849acbeae24d72
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Wed Jul 20 20:52:42 2016 +0000

    Basic operation with assignment + reassignment actions
    
        For now, selects a random privileged user

diff --git a/META.yml b/META.yml
index c1500cb..0b72455 100644
--- a/META.yml
+++ b/META.yml
@@ -16,6 +16,7 @@ meta-spec:
 name: RT-Extension-AutomaticAssignment
 no_index:
   directory:
+    - etc
     - inc
 requires:
   perl: 5.8.3
diff --git a/etc/initialdata b/etc/initialdata
new file mode 100644
index 0000000..7a969db
--- /dev/null
+++ b/etc/initialdata
@@ -0,0 +1,13 @@
+ at ScripActions = (
+   {
+       Name => 'Automatic Assignment',
+       Description => 'Automatically assign unowned ticket',
+       ExecModule => 'AutomaticAssignment',
+   },
+   {
+       Name => 'Automatic Reassignment',
+       Description => 'Automatically assign unowned or owned ticket',
+       ExecModule => 'AutomaticReassignment',
+   },
+);
+
diff --git a/lib/RT/Action/AutomaticAssignment.pm b/lib/RT/Action/AutomaticAssignment.pm
new file mode 100644
index 0000000..1456510
--- /dev/null
+++ b/lib/RT/Action/AutomaticAssignment.pm
@@ -0,0 +1,31 @@
+package RT::Action::AutomaticAssignment;
+use base 'RT::Action';
+
+# only tickets that are unassigned will be automatically assigned.
+# RT::Action::AutomaticReassignment overrides this to remove this restriction
+sub _PrepareOwner {
+    my $self = shift;
+
+    return $self->TicketObj->Owner == RT->Nobody->id;
+}
+
+sub Prepare {
+    my $self = shift;
+
+    return $self->_PrepareOwner;
+}
+
+sub Commit {
+    my $self = shift;
+    my $ticket = $self->TicketObj;
+
+    my $owner = RT::Extension::AutomaticAssignment->OwnerForTicket($ticket);
+
+    return 0 if !$owner;
+
+    my ($ok, $msg) = $ticket->SetOwner($owner->id);
+    return $ok;
+}
+
+1;
+
diff --git a/lib/RT/Action/AutomaticReassignment.pm b/lib/RT/Action/AutomaticReassignment.pm
new file mode 100644
index 0000000..ce5904c
--- /dev/null
+++ b/lib/RT/Action/AutomaticReassignment.pm
@@ -0,0 +1,12 @@
+package RT::Action::AutomaticReassignment;
+use base 'RT::Action::AutomaticAssignment';
+
+# any owner is fine
+sub _PrepareOwner {
+    my $self = shift;
+
+    return 1;
+}
+
+1;
+
diff --git a/lib/RT/Extension/AutomaticAssignment.pm b/lib/RT/Extension/AutomaticAssignment.pm
index a20f8c1..dac09ee 100644
--- a/lib/RT/Extension/AutomaticAssignment.pm
+++ b/lib/RT/Extension/AutomaticAssignment.pm
@@ -4,6 +4,35 @@ package RT::Extension::AutomaticAssignment;
 
 our $VERSION = '0.01';
 
+sub AvailableOwnersForTicket {
+    my $class  = shift;
+    my $ticket = shift;
+
+    my $users = RT::Users->new(RT->SystemUser);
+    $users->LimitToPrivileged;
+    return $users->ItemsArrayRef;
+}
+
+sub ChooseOwnerForTicket {
+    my $class  = shift;
+    my $ticket = shift;
+    my $users  = shift;
+
+    return $users->[rand @$users];
+}
+
+sub OwnerForTicket {
+    my $class  = shift;
+    my $ticket = shift;
+
+    my $users = $class->AvailableOwnersForTicket($ticket);
+    return if !$users;
+
+    my $user = $class->ChooseOwnerForTicket($ticket, $users);
+
+    return $user;
+}
+
 =head1 NAME
 
 RT-Extension-AutomaticAssignment - 

commit ae6c5a905c1f33998a6ef90e3a8afdf77bedddf9
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Wed Jul 20 22:05:47 2016 +0000

    Document creating scrips for automatic assignment

diff --git a/README b/README
index 24fe415..f5d4375 100644
--- a/README
+++ b/README
@@ -18,6 +18,14 @@ INSTALLATION
             rm -rf /opt/rt4/var/mason_data/obj
 
     Restart your webserver
+    Create scrips
+        This lets you control which circumstances automatic assignment
+        should take place. For example, perhaps you want an On Create,
+        Automatic Assignment scrip on some of your queues. Any tickets
+        explicitly created with an owner will retain that owner. You may
+        also want an On Queue Change, Automatic Reassignment scrip. For
+        Automatic Reassignment, the automatic assignment will happen even if
+        the ticket has an owner already.
 
 AUTHOR
     Best Practical Solutions, LLC <modules at bestpractical.com>
diff --git a/lib/RT/Extension/AutomaticAssignment.pm b/lib/RT/Extension/AutomaticAssignment.pm
index dac09ee..8a42248 100644
--- a/lib/RT/Extension/AutomaticAssignment.pm
+++ b/lib/RT/Extension/AutomaticAssignment.pm
@@ -63,6 +63,15 @@ Add this line:
 
 =item Restart your webserver
 
+=item Create scrips
+
+This lets you control which circumstances automatic assignment should take
+place. For example, perhaps you want an On Create, Automatic Assignment
+scrip on some of your queues. Any tickets explicitly created with an owner
+will retain that owner. You may also want an On Queue Change, Automatic
+Reassignment scrip. For Automatic Reassignment, the automatic assignment
+will happen even if the ticket has an owner already.
+
 =back
 
 =head1 AUTHOR

commit 55de2c29fc8c68a4b1dcd7c8af479830eac590ca
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Wed Jul 20 22:06:13 2016 +0000

    Implement a Chooser abstraction, starting with Random

diff --git a/lib/RT/Extension/AutomaticAssignment.pm b/lib/RT/Extension/AutomaticAssignment.pm
index 8a42248..7d549ef 100644
--- a/lib/RT/Extension/AutomaticAssignment.pm
+++ b/lib/RT/Extension/AutomaticAssignment.pm
@@ -18,7 +18,32 @@ sub ChooseOwnerForTicket {
     my $ticket = shift;
     my $users  = shift;
 
-    return $users->[rand @$users];
+    my $queue = $ticket->QueueObj->Name;
+    my $choosers = RT->Config->Get('AutomaticAssignment_Choosers');
+    if (!$choosers) {
+        RT->Logger->error("No AutomaticAssignment_Choosers defined; automatic assignment cannot occur.");
+        return;
+    }
+
+    my $config = $choosers->{QueueDefault}{ $queue } || $choosers->{Default};
+    my $chooser_name;
+
+    if (!$config) {
+        RT->Logger->error("No AutomaticAssignment_Choosers Default or QueueDefault for queue '$queue' defined; automatic assignment cannot occur.");
+        return;
+    }
+
+    if (ref($config)) {
+        $chooser_name = $config->{class};
+    }
+    else {
+        $chooser_name = $config;
+        $config = {};
+    }
+
+    my $chooser_class = $chooser_name =~ /::/ ? $chooser_name : "RT::Extension::AutomaticAssignment::Chooser::$chooser_name";
+    $chooser_class->require or die $UNIVERSAL::require::ERROR;
+    return $chooser_class->ChooseOwnerForTicket($ticket, $users, $config);
 }
 
 sub OwnerForTicket {
@@ -72,6 +97,12 @@ will retain that owner. You may also want an On Queue Change, Automatic
 Reassignment scrip. For Automatic Reassignment, the automatic assignment
 will happen even if the ticket has an owner already.
 
+=item Configure automatic assignment policies
+
+    Set(%AutomaticAssignment_Choosers, (
+        Default => 'Random',
+    ));
+
 =back
 
 =head1 AUTHOR
diff --git a/lib/RT/Extension/AutomaticAssignment/Chooser.pm b/lib/RT/Extension/AutomaticAssignment/Chooser.pm
new file mode 100644
index 0000000..39735dd
--- /dev/null
+++ b/lib/RT/Extension/AutomaticAssignment/Chooser.pm
@@ -0,0 +1,10 @@
+package RT::Extension::AutomaticAssignment::Chooser;
+use base 'RT::Base';
+
+sub ChooseOwnerForTicket {
+    my $self = shift;
+    die "Subclass " . ref($self) . " of " . __PACKAGE__ . " does not implement required method ChooseOwnerForTicket";
+}
+
+1;
+
diff --git a/lib/RT/Extension/AutomaticAssignment/Chooser/Random.pm b/lib/RT/Extension/AutomaticAssignment/Chooser/Random.pm
new file mode 100644
index 0000000..bdcf518
--- /dev/null
+++ b/lib/RT/Extension/AutomaticAssignment/Chooser/Random.pm
@@ -0,0 +1,14 @@
+package RT::Extension::AutomaticAssignment::Chooser::Random;
+use base 'RT::Extension::AutomaticAssignment::Chooser';
+
+sub ChooseOwnerForTicket {
+    my $class  = shift;
+    my $ticket = shift;
+    my $users  = shift;
+    my $config = shift;
+
+    return $users->[rand @$users];
+}
+
+1;
+

commit 053cfce659a4ef7316435fc34e5698c78f081a0b
Author: Shawn M Moore <shawn at bestpractical.com>
Date:   Wed Jul 20 22:06:28 2016 +0000

    Add Ownership chooser

diff --git a/lib/RT/Extension/AutomaticAssignment/Chooser/Ownership.pm b/lib/RT/Extension/AutomaticAssignment/Chooser/Ownership.pm
new file mode 100644
index 0000000..d27cb24
--- /dev/null
+++ b/lib/RT/Extension/AutomaticAssignment/Chooser/Ownership.pm
@@ -0,0 +1,115 @@
+package RT::Extension::AutomaticAssignment::Chooser::Ownership;
+use strict;
+use warnings;
+use base 'RT::Extension::AutomaticAssignment::Chooser';
+
+sub ChooseOwnerForTicket {
+    my $class  = shift;
+    my $ticket = shift;
+    my @users  = @{ shift(@_) };
+    my $config = shift;
+
+    # for Ownership we only consider tickets in the same queue
+    my $tickets = RT::Tickets->new($ticket->CurrentUser);
+    $tickets->LimitQueue(VALUE => $ticket->Queue);
+
+    my @all_statuses; # for trimming down the tickets to consider
+    my %primary_status; # for coalescing multiple statuses at the same level
+    my @rounds; # for tracking which statuses are more important than others
+
+    # ties => [ ['new', 'open'], 'stalled' ]
+    # this says find the user with the fewest number of new or open tickets.
+    # if there are multiple such users, then select the one with the fewest
+    # number of stalled tickets. if there are multiple again, then we pick
+    # one at random
+    # in this scenario, "open" is folded into "new" like so:
+    #     @all_statuses = ('new', 'open', 'stalled')
+    #     %primary_status = (new => 'new', open => 'new', stalled => 'stalled')
+    #     @rounds = ('new', 'stalled')
+
+    if ($config->{ties}) {
+        for my $statuses (@{ $config->{ties} }) {
+            if (ref($statuses) {
+                # multiple statuses at the same round (like new/open above)
+                # arbitrarily map to the first status in the list (new)
+                my $primary = $statuses->[0];
+                push @all_statuses, @$statuses;
+                push @rounds, $primary;
+                $primary_status{$_} = $primary for @$statuses;
+            }
+            else {
+                # just a single status in this round (like stalled above)
+                my $status = $statuses;
+                push @all_statuses, $status;
+                push @rounds, $status;
+                $primary_status{$status} = $status;
+            }
+        }
+    }
+    else {
+        # if the config does not specify status tiebreakers,
+        # then simplify by selecting the user with the fewest
+        # active-status tickets. we map everything to a single
+        # "active" round
+        @statuses = $queue->ActiveStatusArray;
+        $primary_status{$_} = 'active' for @statuses;
+        @rounds = 'active';
+    }
+
+    # limit to all the statuses we've seen thus far
+    # we certainly don't want to look at all resolved tickets (unless
+    # directed to)
+    for my $status (@statuses) {
+        $tickets->LimitStatus(VALUE => $status);
+    }
+
+    # track how many tickets are in each primary status for
+    # each owner except for nobody
+    my %status_by_owner;
+    while (my $ticket = $tickets->Next) {
+        next if $ticket->Owner = RT->Nobody->id;
+        my $primary_status = $primary_status{ $ticket->Status };
+        $status_by_owner{ $ticket->Owner }{ $primary_status }++;
+    }
+
+    # for each round, find the users with the least number of tickets
+    # in that status. if we find just one user, we return that new owner.
+    # if we find multiple, we continue on to the next round with just
+    # the users who tied for the fewest number of tickets
+    my @fewest;
+    for my $status (@rounds) {
+        @fewest = ();
+        my $fewest_ticket_count;
+
+        for my $user (@users) {
+            my $count = $status_by_owner{ $user->Id }{ $status };
+
+            # either the first user we've seen, or this user
+            # has fewer tickets than anyone else we've seen this round
+            if (!defined($fewest_ticket_count) || $count < $fewest_ticket_count) {
+                @fewest = $user;
+                $fewest_ticket_count = $count;
+            }
+            elsif ($count == $fewest_ticket_count) {
+                push @fewest, $user;
+            }
+        }
+
+        # found exactly one user, so return this user as the owner
+        if (@fewest == 1) {
+            return $fewest[0];
+        }
+        # otherwise, continue to the next round with the users who tied for
+        # fewest tickets
+        else {
+            @users = @fewest;
+        }
+    }
+
+    # all remaining users have the exact same number of tickets by status, so
+    # pick a random one
+    return $fewest[rand @fewest];
+}
+
+1;
+

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


More information about the Bps-public-commit mailing list