|
| 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 | + |
| 6 | +from libmozdata.bugzilla import BugzillaUser |
| 7 | + |
| 8 | +from bugbot.bzcleaner import BzCleaner |
| 9 | +from bugbot.user_activity import UserActivity, UserStatus |
| 10 | + |
| 11 | + |
| 12 | +class TelemetryAlertInactiveRegression(BzCleaner): |
| 13 | + def __init__(self, nweeks=1): |
| 14 | + super().__init__() |
| 15 | + self.nweeks = nweeks |
| 16 | + self.extra_ni = {"nweeks": self.nweeks} |
| 17 | + |
| 18 | + def description(self): |
| 19 | + return f"Telemetry alerts with {self.nweeks} week(s) of inactivity" |
| 20 | + |
| 21 | + def get_extra_for_needinfo_template(self): |
| 22 | + return self.extra_ni |
| 23 | + |
| 24 | + def get_extra_for_template(self): |
| 25 | + return self.extra_ni |
| 26 | + |
| 27 | + def get_bz_params(self, date): |
| 28 | + start_date, _ = self.get_dates(date) |
| 29 | + |
| 30 | + fields = [ |
| 31 | + "id", |
| 32 | + "history", |
| 33 | + ] |
| 34 | + |
| 35 | + # Find all bugs that have a telemetry-alert keyword, have not changed in the |
| 36 | + # last week, and do not have the backlog-deferred keyword set |
| 37 | + params = { |
| 38 | + "include_fields": fields, |
| 39 | + "f3": "keywords", |
| 40 | + "o3": "allwords", |
| 41 | + "v3": ["telemetry-alert"], |
| 42 | + "f4": "keywords", |
| 43 | + "o4": "nowords", |
| 44 | + "v4": "backlog-deferred", |
| 45 | + "f5": "days_elapsed", |
| 46 | + "o5": "greaterthan", |
| 47 | + "v5": self.nweeks * 7, |
| 48 | + "status": ["UNCONFIRMED", "NEW", "REOPENED"], |
| 49 | + "resolution": ["---"], |
| 50 | + } |
| 51 | + |
| 52 | + return params |
| 53 | + |
| 54 | + def get_probe_owner(self, bug_history): |
| 55 | + probe_owner = "" |
| 56 | + |
| 57 | + for change in bug_history: |
| 58 | + # Skip history changes when they are not from the intermittent bug filer |
| 59 | + # since it's responsible for adding the first needinfo for the probe owner |
| 60 | + if change[ "who"] != "[email protected]": |
| 61 | + continue |
| 62 | + |
| 63 | + # Use the CC field entry to find the email of the person that should |
| 64 | + # be needinfo'ed so we don't need to parse it from the needinfo change |
| 65 | + needinfo_found = False |
| 66 | + for specific_change in change["changes"]: |
| 67 | + if ( |
| 68 | + specific_change["field_name"] == "flagtypes.name" |
| 69 | + and "needinfo" in specific_change["added"] |
| 70 | + ): |
| 71 | + needinfo_found = True |
| 72 | + elif specific_change["field_name"] == "cc": |
| 73 | + probe_owner = specific_change["added"] |
| 74 | + |
| 75 | + if needinfo_found: |
| 76 | + break |
| 77 | + |
| 78 | + return probe_owner |
| 79 | + |
| 80 | + def handle_bug(self, bug, data): |
| 81 | + probe_owner = self.get_probe_owner(bug["history"]) |
| 82 | + if not probe_owner: |
| 83 | + # Could not find a probe owner for some reason |
| 84 | + return |
| 85 | + |
| 86 | + data[str(bug["id"])] = {"probe_owner": probe_owner} |
| 87 | + |
| 88 | + return bug |
| 89 | + |
| 90 | + def get_needinfo_nicks(self, bugs): |
| 91 | + def _user_handler(user, data): |
| 92 | + data[user["name"]] = user["nick"] |
| 93 | + |
| 94 | + authors_to_ni = set() |
| 95 | + for bug_info in bugs.values(): |
| 96 | + authors_to_ni.add(bug_info["probe_owner"]) |
| 97 | + |
| 98 | + if not authors_to_ni: |
| 99 | + return |
| 100 | + |
| 101 | + user_emails_to_names = {} |
| 102 | + BugzillaUser( |
| 103 | + user_names=list(authors_to_ni), |
| 104 | + include_fields=["nick", "name"], |
| 105 | + user_handler=_user_handler, |
| 106 | + user_data=user_emails_to_names, |
| 107 | + ).wait() |
| 108 | + |
| 109 | + for bug_info in bugs.values(): |
| 110 | + bug_info["nickname"] = user_emails_to_names[bug_info["probe_owner"]] |
| 111 | + |
| 112 | + def filter_bugs(self, bugs): |
| 113 | + # Exclude bugs where the regressor author is inactive or blocked needinfo. |
| 114 | + # TODO: We can drop this when https://github.com/mozilla/bugbot/issues/1465 is implemented. |
| 115 | + users_info = UserActivity(include_fields=["groups", "requests"]).check_users( |
| 116 | + set(bug["probe_owner"] for bug in bugs.values()), |
| 117 | + keep_active=True, |
| 118 | + fetch_employee_info=True, |
| 119 | + ) |
| 120 | + |
| 121 | + for bug_id, bug in list(bugs.items()): |
| 122 | + user_info = users_info[bug["probe_owner"]] |
| 123 | + if ( |
| 124 | + user_info["status"] != UserStatus.ACTIVE |
| 125 | + or user_info["requests"]["needinfo"]["blocked"] |
| 126 | + ): |
| 127 | + del bugs[bug_id] |
| 128 | + |
| 129 | + return bugs |
| 130 | + |
| 131 | + def set_autofix(self, bugs): |
| 132 | + for bugid, info in bugs.items(): |
| 133 | + self.add_auto_ni( |
| 134 | + bugid, |
| 135 | + { |
| 136 | + "mail": info["probe_owner"], |
| 137 | + "nickname": info["nickname"], |
| 138 | + }, |
| 139 | + ) |
| 140 | + |
| 141 | + def get_bugs(self, *args, **kwargs): |
| 142 | + bugs = super().get_bugs(*args, **kwargs) |
| 143 | + self.get_needinfo_nicks(bugs) |
| 144 | + self.filter_bugs(bugs) |
| 145 | + self.set_autofix(bugs) |
| 146 | + return bugs |
| 147 | + |
| 148 | + |
| 149 | +if __name__ == "__main__": |
| 150 | + TelemetryAlertInactiveRegression().run() |
0 commit comments