11"""
22Automated Bug Triage Tool
3- This script scans markdown bug reports, classifies them by severity based on
3+ This script scans markdown bug reports, classifies them by severity based on
44keywords, and generates a summarized triage report in Markdown format.
55"""
66
77import glob
88import os
9- from datetime import datetime
10- from typing import Dict , List
9+ from datetime import datetime , UTC
1110from pathlib import Path
1211
1312# Configuration: Adjust these paths based on your local environment
1413BASE_DIR = Path (__file__ ).parent
1514BUG_PATH = str (BASE_DIR / "production" / "qa" / "bugs" / "*.md" )
1615OUTPUT_PATH = str (BASE_DIR / "production" / "qa" )
1716
17+
1818def classify_severity (content : str ) -> str :
1919 """
2020 Classifies bug severity based on specific keywords found in the content.
2121 Returns S1 (Critical) through S4 (Minor).
2222 >>> classify_severity("The application had a fatal crash on startup.")
2323 'S1'
2424 >>> classify_severity("The UI is a bit slow today.")
25- 'S3'
25+ 'S3'
2626 """
2727 content = content .lower ()
28-
2928 if any (k in content for k in ["crash" , "data loss" , "cannot start" , "fatal" ]):
3029 return "S1"
3130 if any (k in content for k in ["broken" , "not working" , "fail" ]):
@@ -43,25 +42,22 @@ def classify_priority(severity: str) -> str:
4342 >>> classify_priority("S4")
4443 'P4'
4544 """
46- priority_map = {
47- "S1" : "P1" ,
48- "S2" : "P2" ,
49- "S3" : "P3"
50- }
45+ priority_map = {"S1" : "P1" , "S2" : "P2" , "S3" : "P3" }
5146 return priority_map .get (severity , "P4" )
5247
5348
54- def read_bugs () -> List [ Dict ]:
49+ def read_bugs () -> list [ dict ]:
5550 """
5651 Reads all markdown files in the BUG_PATH and extracts metadata.
52+ >>> read_bugs()
53+ []
5754 """
5855 files = glob .glob (BUG_PATH )
5956 bugs = []
6057
61- # Sorting files ensures consistent BUG-ID assignment across runs
6258 for i , file_path in enumerate (sorted (files )):
6359 try :
64- with open (file_path , "r" , encoding = "utf-8" ) as f :
60+ with open (file_path , encoding = "utf-8" ) as f :
6561 content = f .read ()
6662
6763 severity = classify_severity (content )
@@ -72,28 +68,31 @@ def read_bugs() -> List[Dict]:
7268 "file" : file_path ,
7369 "severity" : severity ,
7470 "priority" : priority ,
75- # Extract first line as summary, capped at 80 chars
7671 "summary" : content .strip ().split ("\n " )[0 ][:80 ]
7772 })
78- except IOError as e :
73+ except OSError as e :
7974 print (f"⚠️ Could not read file { file_path } : { e } " )
8075
8176 return bugs
8277
8378
84- def generate_report (bugs : List [ Dict ]) -> None :
79+ def generate_report (bugs : list [ dict ]) -> None :
8580 """
8681 Groups bugs by priority and writes a summarized Markdown report.
82+ >>> generate_report([])
83+ ❌ No bugs to report.
8784 """
88- date = datetime .now ().strftime ("%Y-%m-%d" )
85+ if not bugs :
86+ print ("❌ No bugs to report." )
87+ return
88+
89+ date = datetime .now (UTC ).strftime ("%Y-%m-%d" )
8990
90- # Ensure the output directory exists
9191 if not os .path .exists (OUTPUT_PATH ):
9292 os .makedirs (OUTPUT_PATH )
9393
9494 output_file = os .path .join (OUTPUT_PATH , f"bug-triage-{ date } .md" )
9595
96- # Filter bugs into priority buckets
9796 p1 = [b for b in bugs if b ["priority" ] == "P1" ]
9897 p2 = [b for b in bugs if b ["priority" ] == "P2" ]
9998 p3 = [b for b in bugs if b ["priority" ] == "P3" ]
@@ -133,10 +132,7 @@ def generate_report(bugs: List[Dict]) -> None:
133132
134133
135134if __name__ == "__main__" :
135+ import doctest
136+ doctest .testmod ()
136137 extracted_bugs = read_bugs ()
137-
138- if not extracted_bugs :
139- print (f"❌ No bug files found in: { BUG_PATH } " )
140- print ("Tip: Ensure the directory exists and contains .md files." )
141- else :
142- generate_report (extracted_bugs )
138+ generate_report (extracted_bugs )
0 commit comments