Files
zzyy-cs/zendao_tools/__init__.py
2025-12-04 17:27:14 +08:00

216 lines
8.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import requests
import hashlib
from bs4 import BeautifulSoup
from datetime import datetime
# 最简版本 - 获取所有产品的Bug
def get_all_bugs():
"""获取所有产品的Bug"""
base_url = "http://39.170.26.156:8888"
username = "qiaoxinjiu"
password = "Qiao123456"
print("获取禅道Bug列表")
print("=" * 50)
# 登录
session = requests.Session()
password_md5 = hashlib.md5(password.encode()).hexdigest()
login_resp = session.post(f"{base_url}/api.php/v1/tokens",
json={"account": username, "password": password_md5},
headers={'Content-Type': 'application/json'})
if login_resp.status_code not in [200, 201]:
print("登录失败")
return
token = login_resp.json().get('token')
session.headers.update({'Token': token})
print("✅ 登录成功")
# 获取所有产品
products_resp = session.get(f"{base_url}/api.php/v1/products")
if products_resp.status_code != 200:
print("获取产品失败")
return
products = products_resp.json().get('products', [])
print(f"📦 共 {len(products)} 个产品")
all_bugs = []
# 遍历每个产品获取Bug
for product in products:
product_id = product.get('id')
product_name = product.get('name')
if product_id != 2:
continue
print(f"\n获取产品 '{product_name}' 的Bug...")
# 你的禅道实例没有开 REST bug 列表接口,只能爬取 HTML 页面
bugs_url = f"{base_url}/bug-browse-{product_id}.html"
bugs_resp = session.get(bugs_url, params={'product': product_id, 'limit': 50})
if bugs_resp.status_code == 200:
try:
html = bugs_resp.text
soup = BeautifulSoup(html, "html.parser")
# 找 bug 列表的主 table尽量精确一点
table = (
soup.find("table", id="bugList")
or soup.find("table", class_="table")
or soup.find("table")
)
if not table:
print(" ❌ 未找到 bug 列表 table")
# print(html[:500]) # 调试时可以打开
return
# 解析表头,建立「列名 -> 索引」映射
header_tr = table.find("tr")
if not header_tr:
print(" ❌ 未找到表头行")
return
header_map = {}
for idx, th in enumerate(header_tr.find_all(["th", "td"])):
text = th.get_text(strip=True)
if not text:
continue
if "ID" == text or text == "编号":
header_map["id"] = idx
elif "标题" in text:
header_map["title"] = idx
elif "状态" in text:
header_map["status"] = idx
elif "严重" in text:
header_map["severity"] = idx
elif "优先" in text or "优先级" in text:
header_map["pri"] = idx
elif "指派" in text:
header_map["assignedTo"] = idx
elif "创建" in text or "打开" in text:
# 如:创建日期 / 打开时间
header_map["openedDate"] = idx
bugs = []
# 遍历表体行
for tr in table.find_all("tr")[1:]:
tds = tr.find_all("td")
if not tds:
continue
# id
bug_id = tr.get("data-id")
if not bug_id and "id" in header_map and header_map["id"] < len(tds):
bug_id = tds[header_map["id"]].get_text(strip=True)
if not bug_id:
continue
# 标题
title = ""
if "title" in header_map and header_map["title"] < len(tds):
cell = tds[header_map["title"]]
link = cell.find("a")
title = (link or cell).get_text(strip=True)
# 状态
status = ""
if "status" in header_map and header_map["status"] < len(tds):
status = tds[header_map["status"]].get_text(strip=True)
# 严重程度(有的版本用颜色块 + title 提示)
severity = ""
if "severity" in header_map and header_map["severity"] < len(tds):
sev_cell = tds[header_map["severity"]]
severity = sev_cell.get_text(strip=True)
if not severity:
sev_span = sev_cell.find("span")
if sev_span and sev_span.get("title"):
severity = sev_span.get("title").strip()
# 优先级
pri = ""
if "pri" in header_map and header_map["pri"] < len(tds):
pri = tds[header_map["pri"]].get_text(strip=True)
# 指派给
assigned_to = ""
if "assignedTo" in header_map and header_map["assignedTo"] < len(tds):
assigned_to = tds[header_map["assignedTo"]].get_text(strip=True)
# 创建/打开日期
opened_date = ""
if "openedDate" in header_map and header_map["openedDate"] < len(tds):
opened_date = tds[header_map["openedDate"]].get_text(strip=True)
bug = {
"id": int(bug_id) if str(bug_id).isdigit() else bug_id,
"title": title,
"statusName": status,
"severity": severity,
"pri": pri,
"assignedToName": assigned_to,
"openedDate": opened_date,
"product_name": product_name,
}
bugs.append(bug)
print(f" ✅ 解析出 {len(bugs)} 个Bug")
all_bugs.extend(bugs)
except Exception as e:
print(f" ❌ 解析失败: {e}")
# 调试用:打印部分 HTML 看实际结构
# print(html[:800])
else:
print(f" ❌ 获取失败: {bugs_resp.status_code} - {bugs_resp.text[:100]}")
# 显示所有Bug
if all_bugs:
# 计算统计数据
today_str = datetime.today().strftime("%Y-%m-%d")
today_md_str = datetime.today().strftime("%m-%d")
total_bugs = len(all_bugs)
today_bugs = [
b for b in all_bugs
if (
today_str in str(b.get("openedDate", "")).strip()
or today_md_str in str(b.get("openedDate", "")).strip()
)
]
# 认为“未关闭”的状态:不在已关闭/已解决/已取消集合中
closed_keywords = {"已关闭", "已解决", "已完成", "已取消", "closed", "resolved", "done", "cancelled"}
open_bugs = [
b for b in all_bugs
if not any(kw in str(b.get("statusName", "")).lower() for kw in ["closed", "resolved", "done", "cancel"])
and not any(kw in str(b.get("statusName", "")) for kw in ["已关闭", "已解决", "已完成", "已取消"])
]
print(f"\n{'=' * 80}")
print(f"📊 Bug统计")
print(f"{'-' * 80}")
print(f" 今日新增 Bug 数: {len(today_bugs)}")
print(f" 未关闭 Bug 数: {len(open_bugs)}")
print(f" 总 Bug 数: {total_bugs}")
print(f"{'=' * 80}")
# 按ID排序
all_bugs.sort(key=lambda x: x.get('id', 0), reverse=True)
for i, bug in enumerate(all_bugs[:30], 1): # 只显示前30个
print(
f"{i}. [#{bug.get('id')}] {bug.get('title', '无标题')[:40]}{'...' if len(bug.get('title', '')) > 40 else ''}")
print(f" 产品: {bug.get('product_name')} | 状态: {bug.get('statusName', '未知')} | "
f"严重: {bug.get('severity', '未知')} | 指派: {bug.get('assignedToName', '未指派')} | "
f"创建时间: {bug.get('openedDate', '')}")
print(f"http://39.170.26.156:8888/bug-view-{bug.get('id')}.html")
if __name__ == "__main__":
# 获取所有Bug版本
get_all_bugs()