File: CodeReview.py

package info (click to toggle)
trac 1.6-2~bpo12%2B1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-backports
  • size: 26,624 kB
  • sloc: python: 81,903; javascript: 2,219; makefile: 561; sh: 92; xml: 12
file content (128 lines) | stat: -rw-r--r-- 4,908 bytes parent folder | download | duplicates (2)
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
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2023 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 https://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at https://trac.edgewall.org/.

from collections import OrderedDict

from trac.core import Component, implements
from trac.perm import IPermissionRequestor
from trac.ticket.api import ITicketActionController
from trac.ticket.default_workflow import ConfigurableTicketWorkflow
from trac.util import to_list
from trac.util.html import tag

revision = "$Rev: 17657 $"
url = "$URL: https://svn.edgewall.org/repos/trac/trunk/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):
    {{{#!ini
    request_review = in_work -> in_review
    review = in_review -> *
    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. When added to the default
    value of `workflow`, the line will look like this:
    {{{#!ini
    workflow = ConfigurableTicketWorkflow,CodeReviewActionController
    }}}
    """

    implements(IPermissionRequestor, ITicketActionController)

    # 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)
        for weight, action \
                in controller.get_actions_by_operation('code_review'):
            review_options = self._get_review_options(action)
            all_status.update(iter(review_options.values()))
        return all_status

    def render_ticket_action_control(self, req, ticket, action):
        id, selected = self._get_selected(req, action)

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

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

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

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

    # Internal methods

    def _get_selected(self, req, action):
        id = 'action_%s_code_review' % action
        return id, req.args.get(id)

    def _get_review_options(self, action):
        values = self.config.getlist('ticket-workflow', action + '.code_review')
        return OrderedDict(to_list(v, sep='->') for v in values)

    def _get_new_status(self, req, action, review_options=None):
        selected = self._get_selected(req, action)[1]
        if not review_options:
            review_options = self._get_review_options(action)
        return review_options[selected]