From 290e24c7e56cee43691f3c370839edb5c5b994f7 Mon Sep 17 00:00:00 2001 From: yeeYash Date: Sat, 2 May 2026 20:27:38 +0530 Subject: [PATCH 1/6] feat: add automated bug triage utility --- bug_triage.py | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 bug_triage.py diff --git a/bug_triage.py b/bug_triage.py new file mode 100644 index 000000000000..3bf1d569309c --- /dev/null +++ b/bug_triage.py @@ -0,0 +1,134 @@ +""" +Automated Bug Triage Tool +This script scans markdown bug reports, classifies them by severity based on +keywords, and generates a summarized triage report in Markdown format. +""" + +import glob +import os +from datetime import datetime +from typing import Dict, List +from pathlib import Path + +# Configuration: Adjust these paths based on your local environment +BASE_DIR = Path(__file__).parent +BUG_PATH = str(BASE_DIR / "production" / "qa" / "bugs" / "*.md") +OUTPUT_PATH = str(BASE_DIR / "production" / "qa") + +def classify_severity(content: str) -> str: + """ + Classifies bug severity based on specific keywords found in the content. + Returns S1 (Critical) through S4 (Minor). + """ + content = content.lower() + + if any(k in content for k in ["crash", "data loss", "cannot start", "fatal"]): + return "S1" + if any(k in content for k in ["broken", "not working", "fail"]): + return "S2" + if any(k in content for k in ["slow", "incorrect", "glitch"]): + return "S3" + return "S4" + + +def classify_priority(severity: str) -> str: + """ + Maps the technical severity level to a business priority level. + """ + priority_map = { + "S1": "P1", + "S2": "P2", + "S3": "P3" + } + return priority_map.get(severity, "P4") + + +def read_bugs() -> List[Dict]: + """ + Reads all markdown files in the BUG_PATH and extracts metadata. + """ + files = glob.glob(BUG_PATH) + bugs = [] + + # Sorting files ensures consistent BUG-ID assignment across runs + for i, file_path in enumerate(sorted(files)): + try: + with open(file_path, "r", encoding="utf-8") as f: + content = f.read() + + severity = classify_severity(content) + priority = classify_priority(severity) + + bugs.append({ + "id": f"BUG-{i+1:03}", + "file": file_path, + "severity": severity, + "priority": priority, + # Extract first line as summary, capped at 80 chars + "summary": content.strip().split("\n")[0][:80] + }) + except IOError as e: + print(f"⚠️ Could not read file {file_path}: {e}") + + return bugs + + +def generate_report(bugs: List[Dict]) -> None: + """ + Groups bugs by priority and writes a summarized Markdown report. + """ + date = datetime.now().strftime("%Y-%m-%d") + + # Ensure the output directory exists + if not os.path.exists(OUTPUT_PATH): + os.makedirs(OUTPUT_PATH) + + output_file = os.path.join(OUTPUT_PATH, f"bug-triage-{date}.md") + + # Filter bugs into priority buckets + p1 = [b for b in bugs if b["priority"] == "P1"] + p2 = [b for b in bugs if b["priority"] == "P2"] + p3 = [b for b in bugs if b["priority"] == "P3"] + p4 = [b for b in bugs if b["priority"] == "P4"] + + report_content = [ + "# Bug Triage Report", + f"**Date**: {date} ", + f"**Open bugs processed**: {len(bugs)}", + "\n---\n", + "## Triage Summary\n", + "| Priority | Count |", + "|----------|-------|", + f"| P1 | {len(p1)} |", + f"| P2 | {len(p2)} |", + f"| P3 | {len(p3)} |", + f"| P4 | {len(p4)} |", + "\n---\n", + "## P1 Bugs (Critical)" + ] + + for b in p1: + report_content.append(f"- {b['id']} | {b['severity']} | {b['summary']}") + + report_content.append("\n## P2 Bugs (High)") + for b in p2: + report_content.append(f"- {b['id']} | {b['severity']} | {b['summary']}") + + report_content.append("\n## Backlog (P3/P4)") + for b in p3 + p4: + report_content.append(f"- {b['id']} | {b['severity']} | {b['summary']}") + + with open(output_file, "w", encoding="utf-8") as f: + f.write("\n".join(report_content)) + + print(f"✅ Report successfully generated at: {output_file}") + + +if __name__ == "__main__": + extracted_bugs = read_bugs() + + if not extracted_bugs: + print(f"❌ No bug files found in: {BUG_PATH}") + print("Tip: Ensure the directory exists and contains .md files.") + else: + generate_report(extracted_bugs) From 934e6f4cc07cb15e110ff7611e881e37456e61a4 Mon Sep 17 00:00:00 2001 From: yeeYash Date: Sat, 2 May 2026 20:47:04 +0530 Subject: [PATCH 2/6] made doctests capable --- bug_triage.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bug_triage.py b/bug_triage.py index 3bf1d569309c..70a870016d10 100644 --- a/bug_triage.py +++ b/bug_triage.py @@ -34,6 +34,10 @@ def classify_severity(content: str) -> str: def classify_priority(severity: str) -> str: """ Maps the technical severity level to a business priority level. + >>> classify_priority("S1") + 'P1' + >>> classify_priority("S4") + 'P4' """ priority_map = { "S1": "P1", From 1136d1201e173d028c5df094a4b9d638f2f62e09 Mon Sep 17 00:00:00 2001 From: yeeYash Date: Sat, 2 May 2026 21:05:04 +0530 Subject: [PATCH 3/6] Edit: classify_severity doctests capable --- bug_triage.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bug_triage.py b/bug_triage.py index 70a870016d10..ff79e3adc54c 100644 --- a/bug_triage.py +++ b/bug_triage.py @@ -19,6 +19,10 @@ def classify_severity(content: str) -> str: """ Classifies bug severity based on specific keywords found in the content. Returns S1 (Critical) through S4 (Minor). + >>> classify_severity("The application had a fatal crash on startup.") + 'S1' + >>> classify_severity("The UI is a bit slow today.") + 'S3' """ content = content.lower() From a7d6b9a6e354ae8edf607ea99ba90cf9836ecbd8 Mon Sep 17 00:00:00 2001 From: yeeYash Date: Sat, 2 May 2026 21:12:09 +0530 Subject: [PATCH 4/6] Changes for Ruff and Timezone awareness --- bug_triage.py | 46 +++++++-------- triage/__init__.py | 0 triage/bug_triage.py | 138 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 25 deletions(-) create mode 100644 triage/__init__.py create mode 100644 triage/bug_triage.py diff --git a/bug_triage.py b/bug_triage.py index ff79e3adc54c..5d477c253252 100644 --- a/bug_triage.py +++ b/bug_triage.py @@ -1,13 +1,12 @@ """ Automated Bug Triage Tool -This script scans markdown bug reports, classifies them by severity based on +This script scans markdown bug reports, classifies them by severity based on keywords, and generates a summarized triage report in Markdown format. """ import glob import os -from datetime import datetime -from typing import Dict, List +from datetime import datetime, UTC from pathlib import Path # Configuration: Adjust these paths based on your local environment @@ -15,6 +14,7 @@ BUG_PATH = str(BASE_DIR / "production" / "qa" / "bugs" / "*.md") OUTPUT_PATH = str(BASE_DIR / "production" / "qa") + def classify_severity(content: str) -> str: """ Classifies bug severity based on specific keywords found in the content. @@ -22,10 +22,9 @@ def classify_severity(content: str) -> str: >>> classify_severity("The application had a fatal crash on startup.") 'S1' >>> classify_severity("The UI is a bit slow today.") - 'S3' + 'S3' """ content = content.lower() - if any(k in content for k in ["crash", "data loss", "cannot start", "fatal"]): return "S1" if any(k in content for k in ["broken", "not working", "fail"]): @@ -43,25 +42,22 @@ def classify_priority(severity: str) -> str: >>> classify_priority("S4") 'P4' """ - priority_map = { - "S1": "P1", - "S2": "P2", - "S3": "P3" - } + priority_map = {"S1": "P1", "S2": "P2", "S3": "P3"} return priority_map.get(severity, "P4") -def read_bugs() -> List[Dict]: +def read_bugs() -> list[dict]: """ Reads all markdown files in the BUG_PATH and extracts metadata. + >>> read_bugs() + [] """ files = glob.glob(BUG_PATH) bugs = [] - # Sorting files ensures consistent BUG-ID assignment across runs for i, file_path in enumerate(sorted(files)): try: - with open(file_path, "r", encoding="utf-8") as f: + with open(file_path, encoding="utf-8") as f: content = f.read() severity = classify_severity(content) @@ -72,28 +68,31 @@ def read_bugs() -> List[Dict]: "file": file_path, "severity": severity, "priority": priority, - # Extract first line as summary, capped at 80 chars "summary": content.strip().split("\n")[0][:80] }) - except IOError as e: + except OSError as e: print(f"⚠️ Could not read file {file_path}: {e}") return bugs -def generate_report(bugs: List[Dict]) -> None: +def generate_report(bugs: list[dict]) -> None: """ Groups bugs by priority and writes a summarized Markdown report. + >>> generate_report([]) + ❌ No bugs to report. """ - date = datetime.now().strftime("%Y-%m-%d") + if not bugs: + print("❌ No bugs to report.") + return + + date = datetime.now(UTC).strftime("%Y-%m-%d") - # Ensure the output directory exists if not os.path.exists(OUTPUT_PATH): os.makedirs(OUTPUT_PATH) output_file = os.path.join(OUTPUT_PATH, f"bug-triage-{date}.md") - # Filter bugs into priority buckets p1 = [b for b in bugs if b["priority"] == "P1"] p2 = [b for b in bugs if b["priority"] == "P2"] p3 = [b for b in bugs if b["priority"] == "P3"] @@ -133,10 +132,7 @@ def generate_report(bugs: List[Dict]) -> None: if __name__ == "__main__": + import doctest + doctest.testmod() extracted_bugs = read_bugs() - - if not extracted_bugs: - print(f"❌ No bug files found in: {BUG_PATH}") - print("Tip: Ensure the directory exists and contains .md files.") - else: - generate_report(extracted_bugs) + generate_report(extracted_bugs) diff --git a/triage/__init__.py b/triage/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/triage/bug_triage.py b/triage/bug_triage.py new file mode 100644 index 000000000000..ca82c81d46ee --- /dev/null +++ b/triage/bug_triage.py @@ -0,0 +1,138 @@ +""" +Automated Bug Triage Tool +This script scans markdown bug reports, classifies them by severity based on +keywords, and generates a summarized triage report in Markdown format. +""" + +import glob +import os +from datetime import UTC, datetime +from pathlib import Path + +# Configuration: Adjust these paths based on your local environment +BASE_DIR = Path(__file__).parent +BUG_PATH = str(BASE_DIR / "production" / "qa" / "bugs" / "*.md") +OUTPUT_PATH = str(BASE_DIR / "production" / "qa") + + +def classify_severity(content: str) -> str: + """ + Classifies bug severity based on specific keywords found in the content. + Returns S1 (Critical) through S4 (Minor). + >>> classify_severity("The application had a fatal crash on startup.") + 'S1' + >>> classify_severity("The UI is a bit slow today.") + 'S3' + """ + content = content.lower() + if any(k in content for k in ["crash", "data loss", "cannot start", "fatal"]): + return "S1" + if any(k in content for k in ["broken", "not working", "fail"]): + return "S2" + if any(k in content for k in ["slow", "incorrect", "glitch"]): + return "S3" + return "S4" + + +def classify_priority(severity: str) -> str: + """ + Maps the technical severity level to a business priority level. + >>> classify_priority("S1") + 'P1' + >>> classify_priority("S4") + 'P4' + """ + priority_map = {"S1": "P1", "S2": "P2", "S3": "P3"} + return priority_map.get(severity, "P4") + + +def read_bugs() -> list[dict]: + """ + Reads all markdown files in the BUG_PATH and extracts metadata. + >>> read_bugs() + [] + """ + files = glob.glob(BUG_PATH) + bugs = [] + + for i, file_path in enumerate(sorted(files)): + try: + with open(file_path, encoding="utf-8") as f: + content = f.read() + + severity = classify_severity(content) + priority = classify_priority(severity) + + bugs.append({ + "id": f"BUG-{i+1:03}", + "file": file_path, + "severity": severity, + "priority": priority, + "summary": content.strip().split("\n")[0][:80] + }) + except OSError as e: + print(f"⚠️ Could not read file {file_path}: {e}") + + return bugs + + +def generate_report(bugs: list[dict]) -> None: + """ + Groups bugs by priority and writes a summarized Markdown report. + >>> generate_report([]) + ❌ No bugs to report. + """ + if not bugs: + print("❌ No bugs to report.") + return + + date = datetime.now(UTC).strftime("%Y-%m-%d") + + if not os.path.exists(OUTPUT_PATH): + os.makedirs(OUTPUT_PATH) + + output_file = os.path.join(OUTPUT_PATH, f"bug-triage-{date}.md") + + p1 = [b for b in bugs if b["priority"] == "P1"] + p2 = [b for b in bugs if b["priority"] == "P2"] + p3 = [b for b in bugs if b["priority"] == "P3"] + p4 = [b for b in bugs if b["priority"] == "P4"] + + report_content = [ + "# Bug Triage Report", + f"**Date**: {date} ", + f"**Open bugs processed**: {len(bugs)}", + "\n---\n", + "## Triage Summary\n", + "| Priority | Count |", + "|----------|-------|", + f"| P1 | {len(p1)} |", + f"| P2 | {len(p2)} |", + f"| P3 | {len(p3)} |", + f"| P4 | {len(p4)} |", + "\n---\n", + "## P1 Bugs (Critical)" + ] + + for b in p1: + report_content.append(f"- {b['id']} | {b['severity']} | {b['summary']}") + + report_content.append("\n## P2 Bugs (High)") + for b in p2: + report_content.append(f"- {b['id']} | {b['severity']} | {b['summary']}") + + report_content.append("\n## Backlog (P3/P4)") + for b in p3 + p4: + report_content.append(f"- {b['id']} | {b['severity']} | {b['summary']}") + + with open(output_file, "w", encoding="utf-8") as f: + f.write("\n".join(report_content)) + + print(f"✅ Report successfully generated at: {output_file}") + + +if __name__ == "__main__": + import doctest + doctest.testmod() + extracted_bugs = read_bugs() + generate_report(extracted_bugs) From e9bf0553e821d50737a273082953006d34d0a567 Mon Sep 17 00:00:00 2001 From: yeeYash Date: Sun, 3 May 2026 11:26:38 +0530 Subject: [PATCH 5/6] ruff tests passed locally, init added. --- bug_triage.py | 138 ------------------------------------------- triage/bug_triage.py | 4 +- 2 files changed, 2 insertions(+), 140 deletions(-) delete mode 100644 bug_triage.py diff --git a/bug_triage.py b/bug_triage.py deleted file mode 100644 index 5d477c253252..000000000000 --- a/bug_triage.py +++ /dev/null @@ -1,138 +0,0 @@ -""" -Automated Bug Triage Tool -This script scans markdown bug reports, classifies them by severity based on -keywords, and generates a summarized triage report in Markdown format. -""" - -import glob -import os -from datetime import datetime, UTC -from pathlib import Path - -# Configuration: Adjust these paths based on your local environment -BASE_DIR = Path(__file__).parent -BUG_PATH = str(BASE_DIR / "production" / "qa" / "bugs" / "*.md") -OUTPUT_PATH = str(BASE_DIR / "production" / "qa") - - -def classify_severity(content: str) -> str: - """ - Classifies bug severity based on specific keywords found in the content. - Returns S1 (Critical) through S4 (Minor). - >>> classify_severity("The application had a fatal crash on startup.") - 'S1' - >>> classify_severity("The UI is a bit slow today.") - 'S3' - """ - content = content.lower() - if any(k in content for k in ["crash", "data loss", "cannot start", "fatal"]): - return "S1" - if any(k in content for k in ["broken", "not working", "fail"]): - return "S2" - if any(k in content for k in ["slow", "incorrect", "glitch"]): - return "S3" - return "S4" - - -def classify_priority(severity: str) -> str: - """ - Maps the technical severity level to a business priority level. - >>> classify_priority("S1") - 'P1' - >>> classify_priority("S4") - 'P4' - """ - priority_map = {"S1": "P1", "S2": "P2", "S3": "P3"} - return priority_map.get(severity, "P4") - - -def read_bugs() -> list[dict]: - """ - Reads all markdown files in the BUG_PATH and extracts metadata. - >>> read_bugs() - [] - """ - files = glob.glob(BUG_PATH) - bugs = [] - - for i, file_path in enumerate(sorted(files)): - try: - with open(file_path, encoding="utf-8") as f: - content = f.read() - - severity = classify_severity(content) - priority = classify_priority(severity) - - bugs.append({ - "id": f"BUG-{i+1:03}", - "file": file_path, - "severity": severity, - "priority": priority, - "summary": content.strip().split("\n")[0][:80] - }) - except OSError as e: - print(f"⚠️ Could not read file {file_path}: {e}") - - return bugs - - -def generate_report(bugs: list[dict]) -> None: - """ - Groups bugs by priority and writes a summarized Markdown report. - >>> generate_report([]) - ❌ No bugs to report. - """ - if not bugs: - print("❌ No bugs to report.") - return - - date = datetime.now(UTC).strftime("%Y-%m-%d") - - if not os.path.exists(OUTPUT_PATH): - os.makedirs(OUTPUT_PATH) - - output_file = os.path.join(OUTPUT_PATH, f"bug-triage-{date}.md") - - p1 = [b for b in bugs if b["priority"] == "P1"] - p2 = [b for b in bugs if b["priority"] == "P2"] - p3 = [b for b in bugs if b["priority"] == "P3"] - p4 = [b for b in bugs if b["priority"] == "P4"] - - report_content = [ - "# Bug Triage Report", - f"**Date**: {date} ", - f"**Open bugs processed**: {len(bugs)}", - "\n---\n", - "## Triage Summary\n", - "| Priority | Count |", - "|----------|-------|", - f"| P1 | {len(p1)} |", - f"| P2 | {len(p2)} |", - f"| P3 | {len(p3)} |", - f"| P4 | {len(p4)} |", - "\n---\n", - "## P1 Bugs (Critical)" - ] - - for b in p1: - report_content.append(f"- {b['id']} | {b['severity']} | {b['summary']}") - - report_content.append("\n## P2 Bugs (High)") - for b in p2: - report_content.append(f"- {b['id']} | {b['severity']} | {b['summary']}") - - report_content.append("\n## Backlog (P3/P4)") - for b in p3 + p4: - report_content.append(f"- {b['id']} | {b['severity']} | {b['summary']}") - - with open(output_file, "w", encoding="utf-8") as f: - f.write("\n".join(report_content)) - - print(f"✅ Report successfully generated at: {output_file}") - - -if __name__ == "__main__": - import doctest - doctest.testmod() - extracted_bugs = read_bugs() - generate_report(extracted_bugs) diff --git a/triage/bug_triage.py b/triage/bug_triage.py index ca82c81d46ee..e57609efc30f 100644 --- a/triage/bug_triage.py +++ b/triage/bug_triage.py @@ -87,10 +87,10 @@ def generate_report(bugs: list[dict]) -> None: return date = datetime.now(UTC).strftime("%Y-%m-%d") - + if not os.path.exists(OUTPUT_PATH): os.makedirs(OUTPUT_PATH) - + output_file = os.path.join(OUTPUT_PATH, f"bug-triage-{date}.md") p1 = [b for b in bugs if b["priority"] == "P1"] From fd560d3d0f42c5cb5d9cccf00cc99a9afd1b3d07 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 3 May 2026 05:57:03 +0000 Subject: [PATCH 6/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- triage/bug_triage.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/triage/bug_triage.py b/triage/bug_triage.py index e57609efc30f..5ee8227400d1 100644 --- a/triage/bug_triage.py +++ b/triage/bug_triage.py @@ -63,13 +63,15 @@ def read_bugs() -> list[dict]: severity = classify_severity(content) priority = classify_priority(severity) - bugs.append({ - "id": f"BUG-{i+1:03}", - "file": file_path, - "severity": severity, - "priority": priority, - "summary": content.strip().split("\n")[0][:80] - }) + bugs.append( + { + "id": f"BUG-{i + 1:03}", + "file": file_path, + "severity": severity, + "priority": priority, + "summary": content.strip().split("\n")[0][:80], + } + ) except OSError as e: print(f"⚠️ Could not read file {file_path}: {e}") @@ -111,7 +113,7 @@ def generate_report(bugs: list[dict]) -> None: f"| P3 | {len(p3)} |", f"| P4 | {len(p4)} |", "\n---\n", - "## P1 Bugs (Critical)" + "## P1 Bugs (Critical)", ] for b in p1: @@ -133,6 +135,7 @@ def generate_report(bugs: list[dict]) -> None: if __name__ == "__main__": import doctest + doctest.testmod() extracted_bugs = read_bugs() generate_report(extracted_bugs)