File: MilestoneOperation.py

package info (click to toggle)
trac 1.0.2%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 16,244 kB
  • ctags: 6,848
  • sloc: python: 56,954; makefile: 350; sh: 79; xml: 10
file content (126 lines) | stat: -rw-r--r-- 5,063 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# -*- coding: utf-8 -*-
#
# Copyright (C) 2002-2013 Edgewall Software
# Copyright (C) 2012 Franz Mayer <franz.mayer@gefasoft.de>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.com/license.html.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/.

from genshi.builder import tag

from trac.core import Component, implements
from trac.resource import ResourceNotFound
from trac.ticket.api import ITicketActionController
from trac.ticket.default_workflow import ConfigurableTicketWorkflow
from trac.ticket.model import Milestone
from trac.util.translation import _
from trac.web.chrome import add_warning

revision = "$Rev$"
url = "$URL$"

class MilestoneOperation(Component):
    """Sets milestone for specific status.

=== Example ===
{{{
[ticket-workflow]
resolve.operations = set_resolution,set_milestone
resolve.milestone = invalid,wontfix,duplicate,worksforme->rejected
}}}

When setting status to `duplicate` the milestone will automatically change
to `rejected`.

'''Note:''' if user has changed milestone manually, this workflow operation
has ''no effect''!

=== Configuration ===
Don't forget to add `MilestoneOperation` to the workflow option
in `[ticket]` section. If there is no workflow option, the line will look
like this:
{{{
[ticket]
workflow = ConfigurableTicketWorkflow,MilestoneOperation
}}}
"""

    implements(ITicketActionController)

    def get_ticket_actions(self, req, ticket):
        actions_we_handle = []
        if req.authname != 'anonymous' and \
                    'TICKET_MODIFY' in req.perm(ticket.resource):
            controller = ConfigurableTicketWorkflow(self.env)
            actions_we_handle = controller.get_actions_by_operation_for_req(
                req, ticket, 'set_milestone')
        self.log.debug('set_milestone handles actions: %r' % actions_we_handle)
        return actions_we_handle

    def get_all_status(self):
        return []

    def render_ticket_action_control(self, req, ticket, action):
        actions = ConfigurableTicketWorkflow(self.env).actions
        label = actions[action]['name']
        res_ms = self.__get_resolution_milestone_dict(ticket, action)
        resolutions = ''
        milestone = None
        for i, resolution in enumerate(res_ms):
            if i > 0:
                resolutions = "%s, '%s'" % (resolutions, resolution)
            else:
                resolutions = "'%s'" % resolution
                milestone = res_ms[resolution]
        hint = _("For resolution %(resolutions)s the milestone will be "
                 "set to '%(milestone)s'.",
                 resolutions=resolutions, milestone=milestone)
        return (label, None, hint)

    def get_ticket_changes(self, req, ticket, action):
        if action == 'resolve' and \
                req.args and 'action_resolve_resolve_resolution' in req.args:
            old_milestone = ticket._old.get('milestone') or None
            user_milestone = ticket['milestone'] or None
            # If there's no user defined milestone, we try to set it
            # using the defined resolution -> milestone mapping.
            if old_milestone is None:
                new_status = req.args['action_resolve_resolve_resolution']
                new_milestone = self.__get_new_milestone(ticket, action,
                                                         new_status)
                # ... but we don't reset it to None unless it was None
                if new_milestone is not None or user_milestone is None:
                    try:
                        milestone = Milestone(self.env, new_milestone)
                        self.log.info('changed milestone from %s to %s' %
                                      (old_milestone, new_milestone) )
                        return {'milestone': new_milestone}
                    except ResourceNotFound:
                        add_warning(req, _("Milestone %(name)s does not exist.",
                                           name=new_milestone))
        return {}

    def apply_action_side_effects(self, req, ticket, action):
        pass

    def __get_new_milestone(self, ticket, action, new_status):
        """Determines the new status"""
        if new_status:
            res_ms = self.__get_resolution_milestone_dict(ticket, action)
            return res_ms.get(new_status)

    def __get_resolution_milestone_dict(self, ticket, action):
        transitions = self.config.get('ticket-workflow',
                                      action + '.milestone').strip()
        transition = [x.strip() for x in transitions.split('->')]
        resolutions = [y.strip() for y in transition[0].split(',')]
        res_milestone = {}
        for res in resolutions:
            res_milestone[res] = transition[1]
        return res_milestone