]>
Commit | Line | Data |
---|---|---|
84fb5b46 MKG |
1 | # BEGIN BPS TAGGED BLOCK {{{ |
2 | # | |
3 | # COPYRIGHT: | |
4 | # | |
3ffc5f4f | 5 | # This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC |
84fb5b46 MKG |
6 | # <sales@bestpractical.com> |
7 | # | |
8 | # (Except where explicitly superseded by other copyright notices) | |
9 | # | |
10 | # | |
11 | # LICENSE: | |
12 | # | |
13 | # This work is made available to you under the terms of Version 2 of | |
14 | # the GNU General Public License. A copy of that license should have | |
15 | # been provided with this software, but in any event can be snarfed | |
16 | # from www.gnu.org. | |
17 | # | |
18 | # This work is distributed in the hope that it will be useful, but | |
19 | # WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
21 | # General Public License for more details. | |
22 | # | |
23 | # You should have received a copy of the GNU General Public License | |
24 | # along with this program; if not, write to the Free Software | |
25 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
26 | # 02110-1301 or visit their web page on the internet at | |
27 | # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. | |
28 | # | |
29 | # | |
30 | # CONTRIBUTION SUBMISSION POLICY: | |
31 | # | |
32 | # (The following paragraph is not intended to limit the rights granted | |
33 | # to you to modify and distribute this software under the terms of | |
34 | # the GNU General Public License and is only of importance to you if | |
35 | # you choose to contribute your changes and enhancements to the | |
36 | # community by submitting them to Best Practical Solutions, LLC.) | |
37 | # | |
38 | # By intentionally submitting any modifications, corrections or | |
39 | # derivatives to this work, or any other work intended for use with | |
40 | # Request Tracker, to Best Practical Solutions, LLC, you confirm that | |
41 | # you are the copyright holder for those contributions and you grant | |
42 | # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, | |
43 | # royalty-free, perpetual, license to use, copy, create derivative | |
44 | # works based on those contributions, and sublicense and distribute | |
45 | # those contributions and any derivatives thereof. | |
46 | # | |
47 | # END BPS TAGGED BLOCK }}} | |
48 | ||
49 | =head1 NAME | |
50 | ||
51 | RT::Action::LinearEscalate - will move a ticket's priority toward its final priority. | |
52 | ||
53 | =head1 This vs. RT::Action::EscalatePriority | |
54 | ||
55 | This action doesn't change priority if due date is not set. | |
56 | ||
57 | This action honor the Starts date. | |
58 | ||
59 | This action can apply changes silently. | |
60 | ||
61 | This action can replace EscalatePriority completly. If you want to tickets | |
62 | that have been created without Due date then you can add scrip that sets | |
63 | default due date. For example a week then priorities of your tickets will | |
64 | escalate linearly during the week from intial value towards final. | |
65 | ||
66 | =head1 This vs. LinearEscalate from the CPAN | |
67 | ||
68 | This action is an integration of the module from the CPAN into RT's core | |
69 | that's happened in RT 3.8. If you're upgrading from 3.6 and have been using | |
70 | module from the CPAN with old version of RT then you should uninstall it | |
71 | and use this one. | |
72 | ||
73 | However, this action doesn't support control over config. Read </CONFIGURATION> | |
74 | to find out ways to deal with it. | |
75 | ||
76 | =head1 DESCRIPTION | |
77 | ||
78 | LinearEscalate is a ScripAction that will move a ticket's priority | |
79 | from its initial priority to its final priority linearly as | |
80 | the ticket approaches its due date. | |
81 | ||
82 | It's intended to be called by an RT escalation tool. One such tool is called | |
83 | rt-crontool and is located in $RTHOME/bin (see C<rt-crontool -h> for more details). | |
84 | ||
85 | =head1 USAGE | |
86 | ||
87 | Once the ScripAction is installed, the following script in "cron" | |
88 | will get tickets to where they need to be: | |
89 | ||
90 | rt-crontool --search RT::Search::FromSQL --search-arg \ | |
91 | "(Status='new' OR Status='open' OR Status = 'stalled')" \ | |
92 | --action RT::Action::LinearEscalate | |
93 | ||
94 | The Starts date is associated with intial ticket's priority or | |
95 | the Created field if the former is not set. End of interval is | |
96 | the Due date. Tickets without due date B<are not updated>. | |
97 | ||
98 | =head1 CONFIGURATION | |
99 | ||
100 | Initial and Final priorities are controlled by queue's options | |
3ffc5f4f | 101 | and can be defined using the web UI via Admin tab. This |
84fb5b46 MKG |
102 | action should handle correctly situations when initial priority |
103 | is greater than final. | |
104 | ||
105 | LinearEscalate's behavior can be controlled by two options: | |
106 | ||
107 | =over 4 | |
108 | ||
109 | =item RecordTransaction - defaults to false and if option is true then | |
110 | causes the tool to create a transaction on the ticket when it is escalated. | |
111 | ||
112 | =item UpdateLastUpdated - which defaults to true and updates the LastUpdated | |
113 | field when the ticket is escalated, otherwise don't touch anything. | |
114 | ||
115 | =back | |
116 | ||
117 | You cannot set "UpdateLastUpdated" to false unless "RecordTransaction" | |
118 | is also false. Well, you can, but we'll just ignore you. | |
119 | ||
120 | You can set this options using either in F<RT_SiteConfig.pm>, as action | |
121 | argument in call to the rt-crontool or in DB if you want to use the action | |
122 | in scrips. | |
123 | ||
124 | From a shell you can use the following command: | |
125 | ||
126 | rt-crontool --search RT::Search::FromSQL --search-arg \ | |
127 | "(Status='new' OR Status='open' OR Status = 'stalled')" \ | |
128 | --action RT::Action::LinearEscalate \ | |
129 | --action-arg "RecordTransaction: 1" | |
130 | ||
131 | This ScripAction uses RT's internal _Set or __Set calls to set ticket | |
132 | priority without running scrips or recording a transaction on each | |
133 | update, if it's been said to. | |
134 | ||
135 | =cut | |
136 | ||
137 | package RT::Action::LinearEscalate; | |
138 | ||
139 | use strict; | |
140 | use warnings; | |
141 | use base qw(RT::Action); | |
142 | ||
84fb5b46 MKG |
143 | #Do what we need to do and send it out. |
144 | ||
145 | #What does this type of Action does | |
146 | ||
147 | sub Describe { | |
148 | my $self = shift; | |
149 | my $class = ref($self) || $self; | |
150 | return "$class will move a ticket's priority toward its final priority."; | |
151 | } | |
152 | ||
153 | sub Prepare { | |
154 | my $self = shift; | |
155 | ||
156 | my $ticket = $self->TicketObj; | |
157 | ||
3ffc5f4f | 158 | unless ( $ticket->DueObj->IsSet ) { |
84fb5b46 MKG |
159 | $RT::Logger->debug('Due is not set. Not escalating.'); |
160 | return 1; | |
161 | } | |
162 | ||
163 | my $priority_range = ($ticket->FinalPriority ||0) - ($ticket->InitialPriority ||0); | |
164 | unless ( $priority_range ) { | |
165 | $RT::Logger->debug('Final and Initial priorities are equal. Not escalating.'); | |
166 | return 1; | |
167 | } | |
168 | ||
169 | if ( $ticket->Priority >= $ticket->FinalPriority && $priority_range > 0 ) { | |
170 | $RT::Logger->debug('Current priority is greater than final. Not escalating.'); | |
171 | return 1; | |
172 | } | |
173 | elsif ( $ticket->Priority <= $ticket->FinalPriority && $priority_range < 0 ) { | |
174 | $RT::Logger->debug('Current priority is lower than final. Not escalating.'); | |
175 | return 1; | |
176 | } | |
177 | ||
178 | # TODO: compute the number of business days until the ticket is due | |
179 | ||
180 | # now we know we have a due date. for every day that passes, | |
181 | # increment priority according to the formula | |
182 | ||
3ffc5f4f MKG |
183 | my $starts = $ticket->StartsObj->IsSet ? $ticket->StartsObj->Unix : $ticket->CreatedObj->Unix; |
184 | my $now = time; | |
84fb5b46 MKG |
185 | |
186 | # do nothing if we didn't reach starts or created date | |
187 | if ( $starts > $now ) { | |
188 | $RT::Logger->debug('Starts(Created) is in future. Not escalating.'); | |
189 | return 1; | |
190 | } | |
191 | ||
3ffc5f4f | 192 | my $due = $ticket->DueObj->Unix; |
84fb5b46 MKG |
193 | $due = $starts + 1 if $due <= $starts; # +1 to avoid div by zero |
194 | ||
195 | my $percent_complete = ($now-$starts)/($due - $starts); | |
196 | ||
197 | my $new_priority = int($percent_complete * $priority_range) + ($ticket->InitialPriority || 0); | |
3ffc5f4f | 198 | $new_priority = $ticket->FinalPriority if $new_priority > $ticket->FinalPriority; |
84fb5b46 MKG |
199 | $self->{'new_priority'} = $new_priority; |
200 | ||
201 | return 1; | |
202 | } | |
203 | ||
204 | sub Commit { | |
205 | my $self = shift; | |
206 | ||
207 | my $new_value = $self->{'new_priority'}; | |
208 | return 1 unless defined $new_value; | |
209 | ||
210 | my $ticket = $self->TicketObj; | |
211 | # if the priority hasn't changed do nothing | |
212 | return 1 if $ticket->Priority == $new_value; | |
213 | ||
214 | # override defaults from argument | |
215 | my ($record, $update) = (0, 1); | |
216 | { | |
217 | my $arg = $self->Argument || ''; | |
218 | if ( $arg =~ /RecordTransaction:\s*(\d+)/i ) { | |
219 | $record = $1; | |
220 | $RT::Logger->debug("Overrode RecordTransaction: $record"); | |
221 | } | |
222 | if ( $arg =~ /UpdateLastUpdated:\s*(\d+)/i ) { | |
223 | $update = $1; | |
224 | $RT::Logger->debug("Overrode UpdateLastUpdated: $update"); | |
225 | } | |
226 | $update = 1 if $record; | |
227 | } | |
228 | ||
229 | $RT::Logger->debug( | |
230 | 'Linearly escalating priority of ticket #'. $ticket->Id | |
231 | .' from '. $ticket->Priority .' to '. $new_value | |
232 | .' and'. ($record? '': ' do not') .' record a transaction' | |
233 | .' and'. ($update? '': ' do not') .' touch last updated field' | |
234 | ); | |
235 | ||
236 | my ( $val, $msg ); | |
237 | unless ( $record ) { | |
238 | unless ( $update ) { | |
239 | ( $val, $msg ) = $ticket->__Set( | |
240 | Field => 'Priority', | |
241 | Value => $new_value, | |
242 | ); | |
243 | } | |
244 | else { | |
245 | ( $val, $msg ) = $ticket->_Set( | |
246 | Field => 'Priority', | |
247 | Value => $new_value, | |
248 | RecordTransaction => 0, | |
249 | ); | |
250 | } | |
251 | } | |
252 | else { | |
253 | ( $val, $msg ) = $ticket->SetPriority( $new_value ); | |
254 | } | |
255 | ||
256 | unless ($val) { | |
257 | $RT::Logger->error( "Couldn't set new priority value: $msg" ); | |
258 | return (0, $msg); | |
259 | } | |
260 | return 1; | |
261 | } | |
262 | ||
263 | RT::Base->_ImportOverlays(); | |
264 | ||
265 | 1; | |
266 | ||
267 | =head1 AUTHORS | |
268 | ||
269 | Kevin Riggle E<lt>kevinr@bestpractical.comE<gt> | |
270 | ||
271 | Ruslan Zakirov E<lt>ruz@bestpractical.comE<gt> | |
272 | ||
273 | =cut |