• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mozilla / relman-auto-nag / #4489

pending completion
#4489

push

coveralls-python

suhaibmujahid
[needinfo_regression_author] Fix the `set_needinfo` method

640 of 3219 branches covered (19.88%)

1 of 1 new or added line in 1 file covered. (100.0%)

1817 of 8018 relevant lines covered (22.66%)

0.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/bugbot/rules/needinfo_regression_author.py
1
# This Source Code Form is subject to the terms of the Mozilla Public
2
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
3
# You can obtain one at http://mozilla.org/MPL/2.0/.
4

5
import collections
×
6

7
from libmozdata.bugzilla import Bugzilla
×
8

9
from bugbot import logger, utils
×
10
from bugbot.bzcleaner import BzCleaner
×
11
from bugbot.user_activity import UserActivity, UserStatus
×
12

13

14
class NeedinfoRegressionAuthor(BzCleaner):
×
15
    def __init__(self):
×
16
        super().__init__()
×
17
        self.extra_ni = {}
×
18
        self.private_regressor_ids: set[str] = set()
×
19

20
    def description(self):
×
21
        return "Unassigned regressions with non-empty Regressed By field"
×
22

23
    def handle_bug(self, bug, data):
×
24
        if len(bug["regressed_by"]) != 1:
×
25
            # either we don't have access to the regressor, or there's more than one, either way leave things alone
26
            return
×
27

28
        data[str(bug["id"])] = {
×
29
            "creator": bug["creator"],
30
            "regressor_id": bug["regressed_by"][0],
31
            "severity": bug["severity"],
32
        }
33

34
        return bug
×
35

36
    def get_extra_for_needinfo_template(self):
×
37
        return self.extra_ni
×
38

39
    def get_autofix_change(self):
×
40
        return {
×
41
            "keywords": {"add": ["regression"]},
42
        }
43

44
    def set_autofix(self, bugs):
×
45
        for bugid, info in bugs.items():
×
46
            self.extra_ni[bugid] = {
×
47
                "regressor_id": str(info["regressor_id"]),
48
                "suggest_set_severity": info["suggest_set_severity"],
49
            }
50
            self.add_auto_ni(
×
51
                bugid,
52
                {
53
                    "mail": info["regressor_author_email"],
54
                    "nickname": info["regressor_author_nickname"],
55
                },
56
            )
57

58
    def get_bz_params(self, date):
×
59
        start_date, _ = self.get_dates(date)
×
60

61
        fields = [
×
62
            "id",
63
            "creator",
64
            "regressed_by",
65
            "assigned_to",
66
            "severity",
67
        ]
68

69
        # Find all bugs with regressed_by information which were open after start_date or
70
        # whose regressed_by field was set after start_date.
71
        params = {
×
72
            "include_fields": fields,
73
            "f1": "OP",
74
            "j1": "OR",
75
            "f2": "creation_ts",
76
            "o2": "greaterthan",
77
            "v2": start_date,
78
            "f3": "regressed_by",
79
            "o3": "changedafter",
80
            "v3": start_date,
81
            "f4": "CP",
82
            "f5": "regressed_by",
83
            "o5": "isnotempty",
84
            "n6": 1,
85
            "f6": "longdesc",
86
            "o6": "casesubstring",
87
            "v6": "since you are the author of the regressor",
88
            "f7": "flagtypes.name",
89
            "o7": "notsubstring",
90
            "v7": "needinfo?",
91
            "status": ["UNCONFIRMED", "NEW", "REOPENED"],
92
            "resolution": ["---"],
93
        }
94

95
        utils.get_empty_assignees(params)
×
96

97
        return params
×
98

99
    def retrieve_regressors(self, bugs):
×
100
        regressor_to_bugs = collections.defaultdict(list)
×
101
        for bug in bugs.values():
×
102
            regressor_to_bugs[bug["regressor_id"]].append(bug)
×
103

104
        def bug_handler(regressor_bug):
×
105
            if regressor_bug.get("groups"):
×
106
                regressor_bug_id = str(regressor_bug["id"])
×
107
                self.private_regressor_ids.add(regressor_bug_id)
×
108

109
            for bug in regressor_to_bugs[regressor_bug["id"]]:
×
110
                bug["regressor_author_email"] = regressor_bug["assigned_to"]
×
111
                bug["regressor_author_nickname"] = regressor_bug["assigned_to_detail"][
×
112
                    "nick"
113
                ]
114

115
        Bugzilla(
×
116
            bugids={bug["regressor_id"] for bug in bugs.values()},
117
            bughandler=bug_handler,
118
            include_fields=["id", "assigned_to", "groups"],
119
        ).get_data().wait()
120

121
    def filter_bugs(self, bugs):
×
122
        # Exclude bugs whose regressor author is nobody.
123
        for bug in list(bugs.values()):
×
124
            if utils.is_no_assignee(bug["regressor_author_email"]):
×
125
                logger.warning(
×
126
                    "Bug {}, regressor of bug {}, doesn't have an author".format(
127
                        bug["regressor_id"], bug["id"]
128
                    )
129
                )
130
                del bugs[bug["id"]]
×
131

132
        # Exclude bugs whose creator is the regressor author.
133
        bugs = {
×
134
            bug["id"]: bug
135
            for bug in bugs.values()
136
            if bug["creator"] != bug["regressor_author_email"]
137
        }
138

139
        # Exclude bugs where a commentor is the regressor author.
140
        def comment_handler(bug, bug_id):
×
141
            if any(
×
142
                comment["creator"] == bugs[bug_id]["regressor_author_email"]
143
                for comment in bug["comments"]
144
            ):
145
                del bugs[str(bug_id)]
×
146

147
        # Exclude bugs where the regressor author is inactive or blocked needinfo.
148
        # TODO: We can drop this when https://github.com/mozilla/bugbot/issues/1465 is implemented.
149
        users_info = UserActivity(include_fields=["groups", "requests"]).check_users(
×
150
            set(bug["regressor_author_email"] for bug in bugs.values()),
151
            keep_active=True,
152
            fetch_employee_info=True,
153
        )
154

155
        for bug_id, bug in list(bugs.items()):
×
156
            user_info = users_info[bug["regressor_author_email"]]
×
157
            if (
×
158
                user_info["status"] != UserStatus.ACTIVE
159
                or user_info["requests"]["needinfo"]["blocked"]
160
            ):
161
                del bugs[bug_id]
×
162
            else:
163
                bug["suggest_set_severity"] = bug["severity"] in (
×
164
                    "--",
165
                    "n/a",
166
                ) and user_info.get("is_employee")
167

168
        Bugzilla(
×
169
            bugids=self.get_list_bugs(bugs),
170
            commenthandler=comment_handler,
171
            comment_include_fields=["creator"],
172
        ).get_data().wait()
173

174
        return bugs
×
175

176
    def get_bugs(self, *args, **kwargs):
×
177
        bugs = super().get_bugs(*args, **kwargs)
×
178
        self.retrieve_regressors(bugs)
×
179
        bugs = self.filter_bugs(bugs)
×
180
        self.set_autofix(bugs)
×
181
        return bugs
×
182

183
    def set_needinfo(self):
×
184
        res = super().set_needinfo()
×
185
        for bug_id, needinfo_action in res.items():
×
186
            if bug_id in self.private_regressor_ids:
×
187
                needinfo_action["comment"]["is_private"] = True
×
188

189
        return res
×
190

191

192
if __name__ == "__main__":
×
193
    NeedinfoRegressionAuthor().run()
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc