165 lines
5.3 KiB
Python
Executable File
165 lines
5.3 KiB
Python
Executable File
#!/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
|
|
import sys
|
|
|
|
from datetime import date
|
|
from gitea_utils import gitea_json_issues_search
|
|
|
|
IS_ATTY = sys.stdout.isatty()
|
|
|
|
|
|
@dataclasses.dataclass
|
|
class ModuleInfo:
|
|
name: str
|
|
labelid: str
|
|
buglist: list[str] = dataclasses.field(default_factory=list)
|
|
buglist_full: list[str] = dataclasses.field(default_factory=list)
|
|
|
|
|
|
# Label names and IDs are taken from https://projects.blender.org/blender/blender/labels.
|
|
modules = {
|
|
"Module/Animation & Rigging": ModuleInfo(name="Animation & Rigging", labelid="268"),
|
|
"Module/Core": ModuleInfo(name="Core", labelid="269"),
|
|
"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 & Tests": 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"),
|
|
"Module/Viewport & EEVEE": ModuleInfo(name="Viewport & EEVEE", labelid="272"),
|
|
}
|
|
|
|
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"
|
|
}
|
|
|
|
|
|
def compile_list(severity: str) -> None:
|
|
|
|
label = f"Severity/{severity}"
|
|
issues_json = gitea_json_issues_search(
|
|
type="issues",
|
|
state="open",
|
|
labels=label,
|
|
verbose=True,
|
|
)
|
|
|
|
# Create a dictionary of format {module_id: module_name}
|
|
module_label_ids = {}
|
|
for module_name in modules:
|
|
module_label_ids[modules[module_name].labelid] = module_name
|
|
|
|
uncategorized_reports = []
|
|
|
|
issues_json_sorted = sorted(issues_json, key=lambda x: x["number"])
|
|
|
|
for issue in issues_json_sorted:
|
|
html_url = issue["html_url"]
|
|
number = issue["number"]
|
|
created_at = issue["created_at"].rsplit('T', 1)[0]
|
|
title = issue["title"]
|
|
|
|
# Check reports module assignment and fill in data.
|
|
for label_iter in issue["labels"]:
|
|
label_id = str(label_iter["id"])
|
|
if label_id not in module_label_ids:
|
|
continue
|
|
|
|
current_module_name = module_label_ids[label_id]
|
|
if current_module_name != label_iter["name"]:
|
|
new_label_name = label_iter["name"]
|
|
print(f"ALERT: The name of label of '{current_module_name}' changed.")
|
|
print(f"The new name is '{new_label_name}'.")
|
|
if IS_ATTY:
|
|
input("Press enter to continue: \n")
|
|
|
|
modules[current_module_name].buglist.append(f"[#{number}]({html_url})")
|
|
modules[current_module_name].buglist_full.append(f"* [{title}]({html_url}) - {created_at}\n")
|
|
break
|
|
else:
|
|
uncategorized_reports.append(f"[#{number}]({html_url})")
|
|
|
|
# Print statistics
|
|
print(f"Open {severity} Severity bugs as of {date.today()}:\n")
|
|
|
|
# Module overview with numbers
|
|
total = 0
|
|
for module in modules.values():
|
|
buglist_str = (", ".join(module.buglist))
|
|
buglist_len = len(module.buglist)
|
|
total += buglist_len
|
|
full_url = base_url + severity_labelid[severity] + "%2c" + module.labelid
|
|
if not module.buglist or severity != "High":
|
|
print(f"- [{module.name}]({full_url}): *{buglist_len}*")
|
|
else:
|
|
print(f"- [{module.name}]({full_url}): *{buglist_len}* _{buglist_str}_")
|
|
|
|
print()
|
|
print(f"[Total]({total_url}): {total}")
|
|
print()
|
|
print("Uncategorized:", ", ".join(uncategorized_reports))
|
|
print()
|
|
|
|
# Module overview with titles and creation date
|
|
for module in modules.values():
|
|
buglist_full_str = ("".join(module.buglist_full))
|
|
buglist_full_len = len(module.buglist_full)
|
|
if buglist_full_len != 0:
|
|
print(f"{module.name}:")
|
|
print(f"{buglist_full_str}")
|
|
|
|
|
|
def main() -> None:
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description="Print statistics on open bug reports per module",
|
|
epilog="This script is used to help module teams",
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--severity",
|
|
dest="severity",
|
|
default="High",
|
|
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()
|