File: MilestoneOperation.py

package info (click to toggle)
trac 1.2%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 17,452 kB
  • ctags: 8,602
  • sloc: python: 71,206; makefile: 358; sh: 79; xml: 10
file content (134 lines) | stat: -rw-r--r-- 5,365 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
127
128
129
130
131
132
133
134
# -*- coding: utf-8 -*-
#
# Copyright (C) 2012-2015 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 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]['label']
        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 = None
        if res_ms:
            try:
                Milestone(self.env, milestone)
            except ResourceNotFound:
                pass
            else:
                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(self.env, new_milestone)
                    except ResourceNotFound:
                        add_warning(req, _("Milestone %(name)s does "
                                           "not exist.", name=new_milestone))
                    else:
                        self.log.info('changed milestone from %s to %s',
                                      old_milestone, new_milestone)
                        return {'milestone': 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('->')]
        res_milestone = {}
        if len(transition) == 2:
            resolutions = [y.strip() for y in transition[0].split(',')]
            for res in resolutions:
                res_milestone[res] = transition[1]
        return res_milestone