由于工作的需要,我們每天都會(huì)使用到JIRA去記錄并跟蹤產(chǎn)品設(shè)計(jì)問(wèn)題的解決。JIRA里面有大量的數(shù)據(jù),Reporter, Assignee, Severity, Description, Comment etc... 而JIRA 網(wǎng)頁(yè)上生成出來(lái)的項(xiàng)目Excel報(bào)告,比較難看。
以前也寫過(guò)用Excel VBA的方法,來(lái)導(dǎo)出JIRA某個(gè)項(xiàng)目Project的問(wèn)題,但這里面出現(xiàn)一個(gè)問(wèn)題,就是要求每個(gè)用戶的JIRA里面的問(wèn)題內(nèi)容的列排序要求一致。且Project ID選擇上做的很不友好。
偶然發(fā)現(xiàn)JIRA有官方的Python 庫(kù),研究了一輪。
1 設(shè)計(jì)了簡(jiǎn)單的登錄界面,輸入JIRA的賬戶,密碼。因?yàn)榭紤]到程序是給不同的用戶使用。這個(gè)必須的。不過(guò)如果每次打開都輸入,挺麻煩的??梢钥紤]把賬戶密碼,寫入線外的某個(gè)文件中,以后就直接自動(dòng)調(diào)用即可。

2 登錄完成后,輸入產(chǎn)品的型號(hào)。由于產(chǎn)品型號(hào)對(duì)應(yīng)不同的Project ID,我把Project ID按一定的格式另外存放在一個(gè)Excel文件中,然后用Pandas庫(kù)讀取了這份Excel表,進(jìn)行型號(hào)與Project ID的比對(duì)。代碼片段如下:
TLA=e3.get()
n=""
file=pd.read_excel("D:\\My Documents\\JIRA\\Export DFM Report\\Project_ID.xlsx")
res1=file[file['TLA PN']==TLA]['Project ID'].values
if res1.size>0:
n="".join(res1)
else:
n=0
if n==0:
print("Can't find the TLA PN, please double check")
else:
exportDFM(TLA,n)
- 通過(guò)第二步獲取了型號(hào)對(duì)應(yīng)的Project ID后,就在JIRA中搜索該P(yáng)roject ID對(duì)應(yīng)的issue。這里寫了一句搜索JQL語(yǔ)句
jql="project = "+prj+" and type=DFM"
由于JIRA最多只能導(dǎo)出1000條issue,如果該P(yáng)roject的issue超過(guò)1000條就無(wú)法正常導(dǎo)出了,所以用了個(gè)while循環(huán),每1000條問(wèn)題搜索一次JIRA。r然后把所有問(wèn)題都寫入一個(gè)列表allissues中去。
block_size=1000
block_num=0
allissues=[]
while True:
start_idx=block_num*block_size
jql="project = "+prj+" and type=DFM"
issues=jira.search_issues(jql,start_idx,block_size)
if len(issues)==0:
break
block_num+=1
for issue in issues:
allissues.append(issue)
- 獲得要的issue列表后,利用Pandas,把列表的內(nèi)容整理成表單。這里使用到字典一一對(duì)應(yīng)。字典的鍵作為表單的列頭,值就是列表中對(duì)應(yīng)的內(nèi)容。
dfm=pd.DataFrame()
for item in allissues:
d={'key':item.key,
'PCB': item.fields.customfield_10424,
'Summary':item.fields.summary,
'Severity': item.fields.customfield_10323,
'Description':item.fields.description,
'Reporter':item.fields.reporter,
'Assignee':item.fields.assignee,
'Create Stage': item.fields.customfield_10324,
'Created Date':item.fields.created[0:10],
'Updated Date':item.fields.updated[0:10],
'Status':item.fields.status,
'Solution':None
}
這里面有一些常用的filed和自定義filed的使用。
例如:一般問(wèn)題的ields中的屬性分為固定屬性和自定義屬性,自定義屬性格式一般為類似customfield_10012這種。常用的問(wèn)題的Fields有:
- assignee:經(jīng)辦人
- created: 創(chuàng)建時(shí)間
- creator: 創(chuàng)建人
- labels: 標(biāo)簽
- priority: 優(yōu)先級(jí)
- project: 所示項(xiàng)目
- reporter: 報(bào)告人
- status: 狀態(tài)
- summary: 問(wèn)題描述
- updated: 更新時(shí)間
- watches: 關(guān)注者
- comments: 評(píng)論
- resolution: 解決方案
關(guān)于Comment的導(dǎo)出處理。Comments field里面一般有很多條留言,以下代碼是導(dǎo)出所有留言,并按時(shí)間整理,避免混亂。
solution=""
comments=jira.comments(item)
for comment in comments: #Get DFM comment filed content
solution=solution+comment.created[0:10] + " " + str(comment.author.name) + ": " + comment.body+'\n'
d['Solution']=solution
- 當(dāng)所有問(wèn)題表單整理完畢后,我們?cè)倮肞andas導(dǎo)出到一份新的excel表中。
dfm=dfm[['key','Reporter','Assignee','PCB','Status','Severity','Description','Solution','Create Stage','Created Date','Updated Date']]
outputfile="D:\\My Documents\\JIRA\\Export DFM Report\\"+TLA+" DFM Report.xlsx"
dfm.to_excel(outputfile,encoding='UTF-8',header=True,index=False)
完成的程序代碼如下:
from tkinter import *
import tkinter as tk
import tkinter.font as tf
from jira import JIRA
from jira.exceptions import JIRAError
import pandas as pd
import openpyxl
def login():
account=e1.get()
pw=e2.get()
global e3
try:
jira=JIRA("http://jira-ep.ecp.priv",auth=(account,pw))
#e1.grid_forget()
e1.destroy()
e2.destroy()
b1.destroy()
l1.destroy()
l2.destroy()
Label(frame2, text="Hi "+account+", Please input or select a TLA PN: ").grid(row=3,column=1, padx=5,pady=5,sticky=E,columnspan=3)
v3=StringVar()
e3=Entry(frame2,textvariable=v3)
e3.grid(row=3,column=6,padx=5,pady=5,sticky=W)
b2=Button(frame2,text="Submit",width=10,command=CheckDFM)
b2.grid(row=3,column=8,padx=5,pady=5,sticky=W)
except:
pass
#Label(frame2, text="Login to JIRA failed. Check your username and password").grid(row=1,padx=5,pady=5,sticky=E,columnspan=2)
def CheckDFM():
TLA=e3.get()
n=""
file=pd.read_excel("D:\\My Documents\\JIRA\\Export DFM Report\\Project_ID.xlsx")
res1=file[file['TLA PN']==TLA]['Project ID'].values
if res1.size>0:
n="".join(res1)
else:
n=0
if n==0:
print("Can't find the TLA PN, please double check")
else:
exportDFM(TLA,n)
def exportDFM(TLA,prj):
jira=JIRA("JIRA的域名",auth=('登錄賬戶','密碼'))
block_size=1000
block_num=0
allissues=[]
while True:
start_idx=block_num*block_size
jql="project = "+prj+" and type=DFM"
issues=jira.search_issues(jql,start_idx,block_size)
if len(issues)==0:
break
block_num+=1
for issue in issues:
allissues.append(issue)
dfm=pd.DataFrame()
for item in allissues:
d={'key':item.key,
'PCB': item.fields.customfield_10424,
'Summary':item.fields.summary,
'Severity': item.fields.customfield_10323,
'Description':item.fields.description,
'Reporter':item.fields.reporter,
'Assignee':item.fields.assignee,
'Create Stage': item.fields.customfield_10324,
'Created Date':item.fields.created[0:10],
'Updated Date':item.fields.updated[0:10],
'Status':item.fields.status,
'Solution':None
}
solution=""
comments=jira.comments(item)
for comment in comments: #Get DFM comment filed content
solution=solution+comment.created[0:10] + " " + str(comment.author.name) + ": " + comment.body+'\n'
d['Solution']=solution
dfm =dfm.append(d,ignore_index=True)
dfm=dfm[['key','Reporter','Assignee','PCB','Status','Severity','Description','Solution','Create Stage','Created Date','Updated Date']]
outputfile="D:\\My Documents\\JIRA\\Export DFM Report\\"+TLA+" DFM Report.xlsx"
dfm.to_excel(outputfile,encoding='UTF-8',header=True,index=False)
print("Done")
app=Tk()
app.title("JIRA DFM Report")
ft=tf.Font(family="Segoe UI",size=12)
frame1=Frame(app)
frame2=Frame(app)
frame3=Frame(app)
frame1.grid(row=1,column=0)
frame2.grid(row=2,column=0)
frame3.grid(row=3,column=0)
Label(frame1,text="Welcome,this tool is used to export DFM report from JIRA").grid(row=0,column=0,padx=5,pady=5)
l1=Label(frame2, text="JIRA Account: ")
l1.grid(row=3,column=0,padx=5,pady=5,sticky=E)
v1=StringVar()
e1=Entry(frame2,textvariable=v1)
e1.grid(row=3,column=1,padx=5,pady=5,sticky=E)
l2=Label(frame2, text="Password: ")
l2.grid(row=3,column=2,padx=5,pady=5,sticky=W)
v2=StringVar()
e2=Entry(frame2,textvariable=v2)
e2.grid(row=3,column=3,padx=5,pady=5,sticky=E)
b1=Button(frame2,text="Login",width=10,command=login)
b1.grid(row=3,column=4,padx=10,pady=5,sticky=E)
app.mainloop()
程序參考資料:
https://courses.cs.ut.ee/LTAT.05.008/2018_spring/uploads/Main/HW4-2018-pdf
https://jira.readthedocs.io/en/latest/