crashclouseau/report_bug.py (122 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 asyncio import functools from jinja2 import Environment, FileSystemLoader import libmozdata.config from libmozdata.hgmozilla import Mercurial import requests from urllib.parse import parse_qs, urlencode, urlparse from . import buginfo, models, utils def findall(p, s): """Yields all the positions of the pattern p in the string s.""" i = s.find(p) while i != -1: yield i i = s.find(p, i + 1) def get_bz_query(data): """Get the Bugzilla query inside the Socorro web page""" needle = 'href="https://bugzilla.mozilla.org/enter_bug.cgi?' for i in findall(needle, data): j = data.index('"', i + len(needle)) if j != -1: bz_url = data[i + len('href="'):j] bz_url = bz_url.replace("&amp;", "&") bz_url = bz_url.replace("&lt;", "<") bz_url = bz_url.replace("&gt;", ">") bz_url = bz_url.replace("&quot;", '"') bz_url = bz_url.replace("&apos;", "'") if "keywords=crash" in bz_url: query = parse_qs(urlparse(bz_url).query) return query return {} def improve(query, bzdata, bugid): """Improve the Bugzilla query we found with other useful info""" if "bugs" in bzdata and len(bzdata["bugs"]) == 1: bzdata = bzdata["bugs"][0] query["product"] = bzdata["product"] query["component"] = bzdata["component"] query["keywords"] = "{},regression".format(query["keywords"][0]) query["blocked"] = "clouseau,{}".format(bugid) return bzdata["assigned_to"] return "" def get_stats(data, buildid): """Get crash stats from Socorro to put in the bug report""" res = {} for i in data["facets"]["build_id"]: count = i["count"] facets = i["facets"] it = len(facets["install_time"]) if it == 100: it = facets["cardinality_install_time"]["value"] res[i["term"]] = {"count": count, "installs": it} if len(res) == 1: return True, res[buildid] else: count = 0 installs = 0 for v in res.values(): count += v["count"] installs += v["installs"] return False, {"count": count, "installs": installs} def finalize_comment(bzquery, first, stats, info, changeset, bugid): """Finalize the comment to put in the bug report""" comment = bzquery["comment"][0] env = Environment(loader=FileSystemLoader("templates")) template = env.get_template("bug.txt") channel = info["channel"] url = Mercurial.get_repo_url(channel) url = "{}/rev?node={}".format(url, changeset) if channel == "nightly": version = "nightly {}".format(utils.get_major(info["version"])) else: version = info["version"] comment = template.render( socorro_comment=comment, count=stats["count"], installs=stats["installs"], version=version, buildid=info["buildid"], bugid=bugid, changeset_url=url, first=first, ) comment = comment.replace("\\n", "\n") bzquery["comment"] = comment bzurl = "https://bugzilla.mozilla.org/enter_bug.cgi" return bzurl + "?" + urlencode(bzquery, True) async def get_info_helper(uuid, changeset): info = models.UUID.get_info(uuid) bugid = models.Node.get_bugid(changeset, info["channel"]) sgn = info["signature"] bzw, bugsdata = buginfo.get_bugs(sgn, wait=False) cs = "https://crash-stats.mozilla.org/report/index/" + uuid bz = "https://bugzilla.mozilla.org/rest/bug" bzh = {"X-Bugzilla-API-Key": libmozdata.config.get("Bugzilla", "token", "")} bzq = {"id": bugid, "include_fields": ["product", "component", "assigned_to"]} cs_api = "https://crash-stats.mozilla.org/api/SuperSearch/" cs_api_q = { "signature": "=" + info["signature"], "build_id": ">=" + info["buildid"], "product": info["product"], "release_channel": info["channel"], "_aggs.build_id": ["install_time", "_cardinality.install_time"], "_results_number": 0, "_facets": "release_channel", "_facets_size": 100, } loop = asyncio.get_event_loop() f1 = loop.run_in_executor(None, functools.partial(requests.get, cs)) if bugid: f2 = loop.run_in_executor( None, functools.partial(requests.get, bz, headers=bzh, params=bzq) ) f3 = loop.run_in_executor( None, functools.partial(requests.get, cs_api, params=cs_api_q) ) r1 = await f1 if bugid: r2 = await f2 r3 = await f3 bzquery = get_bz_query(r1.text) first, stats = get_stats(r3.json(), int(info["buildid"])) bzdata = r2.json() if bugid else {} ni = improve(bzquery, bzdata, bugid) url = finalize_comment(bzquery, first, stats, info, changeset, bugid) bzw.wait() return url, ni, sgn, bugsdata def get_info(uuid, changeset): """Get the info (comment and Bugzilla stuff) to put in the bug report""" return asyncio.get_event_loop().run_until_complete(get_info_helper(uuid, changeset))