必须用官方jira-python sdk(pip install jira),禁用pyjira或手写rest;连接用api token认证;maxresults需显式设(默认仅50);fields须明确列出以防none;日期范围用python计算避免jql时间精度陷阱;导出用pandas+openpyxl分步处理样式;字段访问全需防御性编程并加日志。

用 jira-python 连接 Jira 并查询缺陷数据
必须先装官方 SDK:pip install jira,别用 pyjira 或自己拼 REST 请求——后者要手动处理认证、分页、字段映射,容易漏掉 statusCategory 或 resolutiondate 这类关键字段。
连接时优先用 API Token(不是密码),Jira Cloud 要求必须用;Server 版如果关了 Basic Auth,也得切 Token。配置示例:
from jira import JIRA
<p>jira = JIRA(
server="<a href="https://www.php.cn/link/788117e45830fe1ff21da0c7c7a6aff5">https://www.php.cn/link/788117e45830fe1ff21da0c7c7a6aff5</a>",
basic_auth=("your-email@example.com", "your-api-token")
)
issues = jira.search_issues(
jql='project = "PROJ" AND issuetype = Bug AND updatedDate >= startOfWeek(-1)',
maxResults=500,
fields="summary,status,severity,priority,resolutiondate,created,updated,reporter,assignee"
)
</p>
注意:maxResults 默认只返回 50 条,不设会丢数据;fields 必须显式列出,否则 issue.fields.severity 可能为 None(即使 UI 显示了)。
按周维度过滤并归类缺陷(含时间计算陷阱)
Jira 的 startOfWeek(-1) 是“上周一 00:00”,但国内常要“上周一至本周日”或“周一至周日”,直接写 startOfWeek(-1) AND endOfWeek(-1) 会少最后一天——因为 endOfWeek(-1) 是“上周日 23:59:59.999”,而 Jira 时间戳带毫秒,实际入库可能差几毫秒。
更稳的做法是用 Python 算好日期范围再转字符串:
from datetime import datetime, timedelta
<p>def get_last_week_range():
today = datetime.now().date()
last_monday = today - timedelta(days=today.weekday() + 7)
next_sunday = last_monday + timedelta(days=6)
return last_monday, next_sunday</p><p>start, end = get_last_week_range()
jql = f'project = "PROJ" AND issuetype = Bug AND updatedDate >= "{start}" AND updatedDate </p>
-
updatedDate比created更适合周报统计——反映本周真实进展 - 别用
createdDate做范围,新提的 bug 可能被误判为“本周新增”,但实际是上周遗留未分配 - 如果 Jira 字段是自定义的“发现日期”,得用
cf[10012]这种 ID 形式查,先在 UI 里点开字段看 URL 获取 ID
导出为 Excel 时绕过 openpyxl 的样式和合并单元格坑
用 pandas.DataFrame.to_excel() 最快,但默认不支持合并表头、条件格式、列宽自适应——强行加 openpyxl 引擎后,to_excel() 的 freeze_panes 和 set_column() 得手动调 Workbook 对象,极易报 AttributeError: 'Worksheet' object has no attribute 'write'。
简单够用的方案:先用 pandas 写基础表格,再用 openpyxl 补样式:
import pandas as pd
from openpyxl import load_workbook
<p>df = pd.DataFrame([{
"Key": i.key,
"Summary": i.fields.summary,
"Status": i.fields.status.name,
"Updated": i.fields.updated.date()
} for i in issues])</p><p>df.to_excel("weekly_report.xlsx", index=False)</p><h1>补样式(只做一次)</h1><p>wb = load_workbook("weekly_report.xlsx")
ws = wb.active
ws.freeze_panes = "A2"
for col in ["A", "B", "C", "D"]:
ws.column_dimensions[col].width = 20
wb.save("weekly_report.xlsx")
</p>
- 别在循环里反复
load_workbook,IO 开销大;导出完再一次性修样式 - 如果要高亮“Reopened”状态,用
openpyxl的PatternFill,但注意 fill 颜色值必须是"FFEE1111"格式(不是(238, 17, 17)) - Excel 表头中文乱码?确保 pandas 写入时不加
encoding参数——xlsx 不走编码,那是 csv 的事
定时运行与错误隔离:别让单个 issue 崩掉整份周报
脚本跑在服务器上,最怕某条 issue 缺字段(比如 assignee 是 None)导致 AttributeError 中断,结果周报发一半就卡住。
必须对每条 issue 做防御性取值:
def safe_get(field, default="—"):
try:
return getattr(field, "displayName", getattr(field, "name", field)) or default
except:
return default
<p>row = {
"Assignee": safe_get(issue.fields.assignee),
"Reporter": safe_get(issue.fields.reporter),
"Severity": safe_get(getattr(issue.fields, "customfield_10014", None)),
}
</p>
- 所有字段访问都包一层
try/except或用getattr(..., None),别信 JQL 返回的每条数据结构都一致 - 加日志:用
logging.warning(f"Skip issue {issue.key}: {e}")记下异常,方便排查是权限问题还是字段改名 - Cron 定时任务加
2>&1 >> /var/log/jira-weekly.log,否则 stdout/stderr 全丢
真正麻烦的是 Jira 字段权限——有些项目对 reporter 字段做了隐藏,API 返回 None 但不报错,得提前用测试账号查一遍 /rest/api/3/field 确认字段可读性。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











