bugbot/rules/regression_without_regressed_by.py (173 lines of code) (raw):
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import dateutil.parser
from libmozdata.bugzilla import Bugzilla, BugzillaUser
from bugbot import utils
from bugbot.bzcleaner import BzCleaner
class RegressionWithoutRegressedBy(BzCleaner):
def description(self):
return "Regressions without regressed_by and some dependencies"
def handle_bug(self, bug, data):
bugid = bug["id"]
deps = set(bug["blocks"]) | set(bug["depends_on"])
assignee = bug["assigned_to_detail"]
if utils.is_no_assignee(assignee["email"]):
assignee = None
data[str(bugid)] = {
"deps": deps,
"assignee": assignee,
"creator": bug["creator_detail"],
"creation": dateutil.parser.parse(bug["creation_time"]),
}
return bug
def filter_bugs(self, bugs):
all_deps = set()
deps_to_max_allowed_date = {}
for bugid, info in bugs.items():
bugid = int(bugid)
# we just keep bugs from dependencies created before bugid
# since the others cannot be a regresser !
info["deps"] = deps = set(x for x in info["deps"] if x < bugid)
if deps:
all_deps |= deps
for dep in deps:
deps_to_max_allowed_date[dep] = info["creation"]
def bug_handler(bug, data):
if "meta" in bug["keywords"] or not bug["cf_last_resolved"]:
data.add(bug["id"])
def history_handler(bug, data):
bugid = bug["id"]
treated.add(bugid)
resolved_before = False
for h in bug["history"]:
if resolved_before:
break
for change in h["changes"]:
if change["field_name"] == "cf_last_resolved" and change["added"]:
date = dateutil.parser.parse(change["added"])
if date < deps_to_max_allowed_date[bugid]:
resolved_before = True
break
if not resolved_before:
data.add(bugid)
invalids = set()
treated = set()
Bugzilla(
bugids=list(all_deps),
include_fields=["id", "keywords", "cf_last_resolved"],
bughandler=bug_handler,
bugdata=invalids,
historyhandler=history_handler,
historydata=invalids,
).get_data().wait()
# Some bugs aren't accessible so they won't appear in treated (all_deps - treated)
# Since we don't have any info about them, then we consider them as invalid
invalids |= all_deps - treated
for bugid, info in bugs.items():
info["deps"] -= invalids
bugs = {bugid: info for bugid, info in bugs.items() if info["deps"]}
return bugs
def set_autofix(self, bugs):
def history_handler(bug, data):
bugid = str(bug["id"])
deps = data[bugid]["deps"]
stats = {}
for h in bug["history"]:
for change in h["changes"]:
if (
change["field_name"] in {"blocks", "depends_on"}
and change["added"] in deps
):
who = h["who"]
stats[who] = stats.get(who, 0) + 1
data[bugid]["winner"] = (
max(stats.items(), key=lambda p: p[1])[0] if stats else None
)
no_assignee = [bugid for bugid, info in bugs.items() if not info["assignee"]]
Bugzilla(
bugids=no_assignee, historyhandler=history_handler, historydata=bugs
).get_data().wait()
no_nick = {}
for bugid, info in bugs.items():
if info["assignee"]:
winner = {
"mail": info["assignee"]["email"],
"nickname": info["assignee"]["nick"],
}
self.add_auto_ni(bugid, winner)
elif info["winner"]:
winner = info["winner"]
if winner not in no_nick:
no_nick[winner] = []
no_nick[winner].append(bugid)
else:
winner = {
"mail": info["creator"]["email"],
"nickname": info["creator"]["nick"],
}
self.add_auto_ni(bugid, winner)
if no_nick:
def user_handler(user, data):
data[user["name"]] = user["nick"]
data = {}
BugzillaUser(
user_names=list(no_nick.keys()),
include_fields=["name", "nick"],
user_handler=user_handler,
user_data=data,
).wait()
for bzmail, bugids in no_nick.items():
nick = data[bzmail]
for bugid in bugids:
self.add_auto_ni(bugid, {"mail": bzmail, "nickname": nick})
def get_bz_params(self, date):
start_date, end_date = self.get_dates(date)
fields = [
"blocks",
"depends_on",
"assigned_to",
"creator",
"creation_time",
]
reporter_skiplist = self.get_config("reporter_skiplist", default=[])
reporter_skiplist = ",".join(reporter_skiplist)
params = {
"include_fields": fields,
"bug_status": "__open__",
"j1": "AND",
"f1": "OP",
"f2": "keywords",
"o2": "casesubstring",
"v2": "regression",
"j3": "OR",
"f3": "OP",
"f4": "blocked",
"o4": "isnotempty",
"f5": "dependson",
"o5": "isnotempty",
"f6": "CP",
"f7": "CP",
"f8": "regressed_by",
"o8": "isempty",
"n9": 1,
"f9": "regressed_by",
"o9": "changedafter",
"v9": "1970-01-01",
"f10": "creation_ts",
"o10": "greaterthan",
"v10": start_date,
"f11": "keywords",
"o11": "nowords",
"v11": "regressionwindow-wanted",
"f12": "reporter",
"o12": "nowords",
"v12": reporter_skiplist,
"n13": 1,
"f13": "longdesc",
"o13": "casesubstring",
"v13": "since this bug is a regression, could you fill (if possible) the regressed_by field",
}
return params
def get_bugs(self, date="today", bug_ids=[]):
bugs = super(RegressionWithoutRegressedBy, self).get_bugs(
date=date, bug_ids=bug_ids
)
bugs = self.filter_bugs(bugs)
self.set_autofix(bugs)
return bugs
if __name__ == "__main__":
RegressionWithoutRegressedBy().run()