Skip to content

Commit d1ffd53

Browse files
committed
Changes for Ruff and Timezone awareness
1 parent 1136d12 commit d1ffd53

2 files changed

Lines changed: 159 additions & 25 deletions

File tree

bug_triage.py

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,30 @@
11
"""
22
Automated 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
44
keywords, and generates a summarized triage report in Markdown format.
55
"""
66

77
import glob
88
import os
9-
from datetime import datetime
10-
from typing import Dict, List
9+
from datetime import datetime, UTC
1110
from pathlib import Path
1211

1312
# Configuration: Adjust these paths based on your local environment
1413
BASE_DIR = Path(__file__).parent
1514
BUG_PATH = str(BASE_DIR / "production" / "qa" / "bugs" / "*.md")
1615
OUTPUT_PATH = str(BASE_DIR / "production" / "qa")
1716

17+
1818
def 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

135134
if __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)

triage/bug_triage.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
"""
2+
Automated Bug Triage Tool
3+
This script scans markdown bug reports, classifies them by severity based on
4+
keywords, and generates a summarized triage report in Markdown format.
5+
"""
6+
7+
import glob
8+
import os
9+
from datetime import UTC, datetime
10+
from pathlib import Path
11+
12+
# Configuration: Adjust these paths based on your local environment
13+
BASE_DIR = Path(__file__).parent
14+
BUG_PATH = str(BASE_DIR / "production" / "qa" / "bugs" / "*.md")
15+
OUTPUT_PATH = str(BASE_DIR / "production" / "qa")
16+
17+
18+
def classify_severity(content: str) -> str:
19+
"""
20+
Classifies bug severity based on specific keywords found in the content.
21+
Returns S1 (Critical) through S4 (Minor).
22+
>>> classify_severity("The application had a fatal crash on startup.")
23+
'S1'
24+
>>> classify_severity("The UI is a bit slow today.")
25+
'S3'
26+
"""
27+
content = content.lower()
28+
if any(k in content for k in ["crash", "data loss", "cannot start", "fatal"]):
29+
return "S1"
30+
if any(k in content for k in ["broken", "not working", "fail"]):
31+
return "S2"
32+
if any(k in content for k in ["slow", "incorrect", "glitch"]):
33+
return "S3"
34+
return "S4"
35+
36+
37+
def classify_priority(severity: str) -> str:
38+
"""
39+
Maps the technical severity level to a business priority level.
40+
>>> classify_priority("S1")
41+
'P1'
42+
>>> classify_priority("S4")
43+
'P4'
44+
"""
45+
priority_map = {"S1": "P1", "S2": "P2", "S3": "P3"}
46+
return priority_map.get(severity, "P4")
47+
48+
49+
def read_bugs() -> list[dict]:
50+
"""
51+
Reads all markdown files in the BUG_PATH and extracts metadata.
52+
>>> read_bugs()
53+
[]
54+
"""
55+
files = glob.glob(BUG_PATH)
56+
bugs = []
57+
58+
for i, file_path in enumerate(sorted(files)):
59+
try:
60+
with open(file_path, encoding="utf-8") as f:
61+
content = f.read()
62+
63+
severity = classify_severity(content)
64+
priority = classify_priority(severity)
65+
66+
bugs.append({
67+
"id": f"BUG-{i+1:03}",
68+
"file": file_path,
69+
"severity": severity,
70+
"priority": priority,
71+
"summary": content.strip().split("\n")[0][:80]
72+
})
73+
except OSError as e:
74+
print(f"⚠️ Could not read file {file_path}: {e}")
75+
76+
return bugs
77+
78+
79+
def generate_report(bugs: list[dict]) -> None:
80+
"""
81+
Groups bugs by priority and writes a summarized Markdown report.
82+
>>> generate_report([])
83+
❌ No bugs to report.
84+
"""
85+
if not bugs:
86+
print("❌ No bugs to report.")
87+
return
88+
89+
date = datetime.now(UTC).strftime("%Y-%m-%d")
90+
91+
if not os.path.exists(OUTPUT_PATH):
92+
os.makedirs(OUTPUT_PATH)
93+
94+
output_file = os.path.join(OUTPUT_PATH, f"bug-triage-{date}.md")
95+
96+
p1 = [b for b in bugs if b["priority"] == "P1"]
97+
p2 = [b for b in bugs if b["priority"] == "P2"]
98+
p3 = [b for b in bugs if b["priority"] == "P3"]
99+
p4 = [b for b in bugs if b["priority"] == "P4"]
100+
101+
report_content = [
102+
"# Bug Triage Report",
103+
f"**Date**: {date} ",
104+
f"**Open bugs processed**: {len(bugs)}",
105+
"\n---\n",
106+
"## Triage Summary\n",
107+
"| Priority | Count |",
108+
"|----------|-------|",
109+
f"| P1 | {len(p1)} |",
110+
f"| P2 | {len(p2)} |",
111+
f"| P3 | {len(p3)} |",
112+
f"| P4 | {len(p4)} |",
113+
"\n---\n",
114+
"## P1 Bugs (Critical)"
115+
]
116+
117+
for b in p1:
118+
report_content.append(f"- {b['id']} | {b['severity']} | {b['summary']}")
119+
120+
report_content.append("\n## P2 Bugs (High)")
121+
for b in p2:
122+
report_content.append(f"- {b['id']} | {b['severity']} | {b['summary']}")
123+
124+
report_content.append("\n## Backlog (P3/P4)")
125+
for b in p3 + p4:
126+
report_content.append(f"- {b['id']} | {b['severity']} | {b['summary']}")
127+
128+
with open(output_file, "w", encoding="utf-8") as f:
129+
f.write("\n".join(report_content))
130+
131+
print(f"✅ Report successfully generated at: {output_file}")
132+
133+
134+
if __name__ == "__main__":
135+
import doctest
136+
doctest.testmod()
137+
extracted_bugs = read_bugs()
138+
generate_report(extracted_bugs)

0 commit comments

Comments
 (0)