Tools: Add script to report (high) priority issues per module #120022
|
@ -0,0 +1,126 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
"""
|
||||
# This script prints the numbers of open issues per module.
|
||||
|
||||
Example usage:
|
||||
|
||||
python ./issues_module_listing.py --severity High
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import dataclasses
|
||||
from datetime import date
|
||||
from gitea_utils import gitea_json_issues_search
|
||||
|
||||
@dataclasses.dataclass
|
||||
class ModuleInfo:
|
||||
name: str
|
||||
labelid: str
|
||||
buglist: list[str] = dataclasses.field(default_factory=list)
|
||||
|
||||
# Label names and IDs are taken from https://projects.blender.org/blender/blender/labels.
|
||||
ThomasDinges marked this conversation as resolved
Outdated
|
||||
modules = {
|
||||
"Module/Animation & Rigging" : ModuleInfo(name="Animation & Rigging", labelid="268"),
|
||||
"Module/Core" : ModuleInfo(name="Core", labelid="269"),
|
||||
"Module/EEVEE & Viewport" : ModuleInfo(name="EEVEE & Viewport", labelid="272"),
|
||||
"Module/Grease Pencil" : ModuleInfo(name="Grease Pencil", labelid="273"),
|
||||
"Module/Modeling" : ModuleInfo(name="Modeling", labelid="274"),
|
||||
"Module/Nodes & Physics" : ModuleInfo(name="Nodes & Physics", labelid="275"),
|
||||
"Module/Pipeline, Assets & IO" : ModuleInfo(name="Pipeline, Assets & I/O", labelid="276"),
|
||||
"Module/Platforms, Builds, Test & Devices" : ModuleInfo(name="Platforms, Builds, Test & Devices", labelid="278"),
|
||||
"Module/Python API" : ModuleInfo(name="Python API", labelid="279"),
|
||||
"Module/Render & Cycles" : ModuleInfo(name="Render & Cycles", labelid="280"),
|
||||
"Module/Sculpt, Paint & Texture" : ModuleInfo(name="Sculpt, Paint & Texture", labelid="281"),
|
||||
"Module/User Interface" : ModuleInfo(name="User Interface", labelid="283"),
|
||||
"Module/VFX & Video" : ModuleInfo(name="VFX & Video", labelid="284"),
|
||||
}
|
||||
|
||||
base_url = "https://projects.blender.org/blender/blender/issues?q=&type=all&sort=&state=open&labels="
|
||||
total_url = "https://projects.blender.org/blender/blender/issues?q=&type=all&sort=&state=open&labels=285%2c-297%2c-298%2c-299%2c-301"
|
||||
|
||||
severity_labelid = {
|
||||
"Low": "286",
|
||||
"Normal": "287",
|
||||
"High": "285",
|
||||
"Unbreak Now!": "288"
|
||||
}
|
||||
ThomasDinges marked this conversation as resolved
Campbell Barton
commented
Calling Calling `list()` is mainly used for converting to a list, for empty lists `[]` is typically written.
|
||||
|
||||
|
||||
def compile_list(severity: str) -> None:
|
||||
|
||||
label = f"Priority/{severity}"
|
||||
issues_json = gitea_json_issues_search(
|
||||
type="issues",
|
||||
state="open",
|
||||
labels=label,
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
uncategorized_reports = []
|
||||
|
||||
for issue in issues_json:
|
||||
html_url = issue["html_url"]
|
||||
ThomasDinges marked this conversation as resolved
Campbell Barton
commented
*typo* \*typo\* `assignment`, also end sentences with full-stops.
|
||||
number = issue["number"]
|
||||
|
||||
# Check reports module assignment and fill in data.
|
||||
for label_iter in issue["labels"]:
|
||||
label = label_iter["name"]
|
||||
if label not in modules:
|
||||
continue
|
||||
|
||||
modules[label].buglist.append(f"[#{number}]({html_url})")
|
||||
break
|
||||
else:
|
||||
uncategorized_reports.append(f"[#{number}]({html_url})")
|
||||
|
||||
ThomasDinges marked this conversation as resolved
Campbell Barton
commented
Having module used twice under different types causes mypy to report type errors (see Having module used twice under different types causes mypy to report type errors (see ` mypy --strict tools/triage/issues_module_listing.py`), renaming it to `module_key`, `module_id` or similar resolves.
|
||||
uncategorized_reports = (', '.join(uncategorized_reports))
|
||||
|
||||
# Print statistics
|
||||
print(f"Open {severity} Priority bugs as of {date.today()}:\n")
|
||||
|
||||
total = 0
|
||||
for module in modules.values():
|
||||
total += len(module.buglist)
|
||||
str_list = (', '.join(module.buglist))
|
||||
full_url = base_url + severity_labelid[severity] + "%2c" + module.labelid
|
||||
ThomasDinges marked this conversation as resolved
Campbell Barton
commented
While personal preference I find the While personal preference I find the `{str(len(module.bug_list))}` a bit cryptic, the `str()` can be removed, assigning a variable `buglist_len = len(module.buglist)` means this can be written as `{buglist_len}` which reads better to me - but I'm not _that_ fussed.
|
||||
buglist_len = len(module.buglist)
|
||||
if not module.buglist or severity != 'High':
|
||||
print(f"- [{module.name}]({full_url}): *{buglist_len}*")
|
||||
else:
|
||||
print(f"- [{module.name}]({full_url}): *{buglist_len}* _{str_list}_")
|
||||
|
||||
ThomasDinges marked this conversation as resolved
Campbell Barton
commented
can be can be `f"Total: {total}"` - since f-strings are used everywhere else here.
|
||||
print()
|
||||
print(f"[Total]({total_url}): {total}")
|
||||
|
||||
print()
|
||||
print(f"Uncategorized: {uncategorized_reports}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Print statistics on open bug reports per module",
|
||||
epilog="This script is used to help module teams",
|
||||
ThomasDinges marked this conversation as resolved
Outdated
Campbell Barton
commented
Adding
... shows up in Adding `choices=severity_labelid.keys(),` gives a nice error if an invalid choice is passed in. .e.g
```
issues_module_listing.py: error: argument --severity: invalid choice: 'Unbreak' (choose from 'Low', 'Normal', 'High', 'Unbreak Now!')
```
... shows up in `--help` too.
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--severity",
|
||||
dest="severity",
|
||||
default="High",
|
||||
ThomasDinges marked this conversation as resolved
Campbell Barton
commented
*picky* prefer trailing comma and newline, makes diffs less noisy when new args are added. Same for \*picky\* prefer trailing comma and newline, makes diffs less noisy when new args are added. Same for `ArgumentParser`.
|
||||
type=str,
|
||||
required=False,
|
||||
choices=severity_labelid.keys(),
|
||||
help="Severity level of reports",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
compile_list(args.severity)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Worth adding a note about where these values are from - if we need to update the list.