File: CodeReview.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 (132 lines) | stat: -rw-r--r-- 5,193 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
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2013 Edgewall Software
# Copyright (C) 2007 Eli Carter <retracile@gmail.com>
# 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.perm import IPermissionRequestor
from trac.ticket.api import ITicketActionController
from trac.ticket.default_workflow import ConfigurableTicketWorkflow

revision = "$Rev: 13432 $"
url = "$URL: https://svn.edgewall.org/repos/trac/branches/1.2-stable/sample-plugins/workflow/CodeReview.py $"

class CodeReviewActionController(Component):
    """Support for simple code reviews.

    The action that supports the `code_review` operation will present
    an extra choice for the review decision. Depending on that decision,
    a specific state will be selected.

    Example (from the enterprise-review-workflow.ini):
    {{{
    review = in_review -> *
    review.label = review as
    review.operations = code_review
    review.code_review =
      approve -> in_QA,
      approve as noted -> post_review,
      request changes -> in_work
    }}}
    Don't forget to add the `CodeReviewActionController` to the workflow
    option in the `[ticket]` section in TracIni.
    If there is no other workflow option, the line will look like this:
    {{{
    workflow = ConfigurableTicketWorkflow,CodeReviewActionController
    }}}
    """

    implements(ITicketActionController, IPermissionRequestor)

    # IPermissionRequestor methods

    def get_permission_actions(self):
        return ['TICKET_REVIEW']

    # ITicketActionController methods

    def get_ticket_actions(self, req, ticket):
        # The review action is available in those status where it has been
        # configured, for those users who have the TICKET_REVIEW permission, as
        # long as they are not the owner of the ticket (you can't review your
        # own work!).
        actions_we_handle = []
        if req.authname != ticket['owner'] and \
                    'TICKET_REVIEW' in req.perm(ticket.resource):
            controller = ConfigurableTicketWorkflow(self.env)
            actions_we_handle = controller.get_actions_by_operation_for_req(
                req, ticket, 'code_review')
        self.log.debug('code review handles actions: %r', actions_we_handle)
        return actions_we_handle

    def get_all_status(self):
        all_status = set()
        controller = ConfigurableTicketWorkflow(self.env)
        ouractions = controller.get_actions_by_operation('code_review')
        for weight, action in ouractions:
            status = [status for option, status in
                      self._get_review_options(action)]
            all_status.update(status)
        return all_status

    def render_ticket_action_control(self, req, ticket, action):
        id, grade = self._get_grade(req, action)

        review_options = self._get_review_options(action)
        actions = ConfigurableTicketWorkflow(self.env).actions

        selected_value = grade or review_options[0][0]

        label = actions[action]['label']
        control = tag(["as: ",
                       tag.select([tag.option(option, selected=
                                              (option == selected_value or
                                               None))
                                   for option, status in review_options],
                                  name=id, id=id)])
        if grade:
            new_status = self._get_new_status(req, ticket, action,
                                              review_options)
            hint = "Next status will be '%s'" % new_status
        else:
            hint = "Next status will be one of " + \
                   ', '.join(["'%s'" % status
                              for option, status in review_options])
        return (label, control, hint)

    def get_ticket_changes(self, req, ticket, action):
        new_status = self._get_new_status(req, ticket, action)
        return {'status': new_status or 'new'}

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

    # Internal methods

    def _get_grade(self, req, action):
        id = action + '_code_review_result'
        return id, req.args.get(id)

    def _get_review_options(self, action):
        return [[x.strip() for x in raw_option.split('->')]
                for raw_option in self.config.getlist('ticket-workflow',
                                                      action + '.code_review')]

    def _get_new_status(self, req, ticket, action, review_options=None):
        id, grade = self._get_grade(req, action)
        if not review_options:
            review_options = self._get_review_options(action)
        for option, status in review_options:
            if grade == option:
                return status