問題
很難說 atlassian jira 是最受歡迎的問題跟蹤器和項目管理解決方案之一。你可以喜歡它,也可以討厭它,但如果你被某家公司聘用為軟件工程師,那么很有可能會遇到 jira。
如果您正在從事的項目非常活躍,可能會有數千個各種類型的 jira 問題。如果您領導著一個工程師團隊,您可能會對分析工具感興趣,這些工具可以幫助您根據 jira 中存儲的數據了解項目中發生的情況。 jira 集成了一些報告工具以及第三方插件。但其中大多數都是非常基本的。例如,很難找到相當靈活的“預測”工具。
項目越大,您對集成報告工具的滿意度就越低。在某些時候,您最終將使用 api 來提取、操作和可視化數據。在過去 15 年的 jira 使用過程中,我看到了圍繞該領域的數十個采用各種編程語言的此類腳本和服務。
許多日常任務可能需要一次性數據分析,因此每次都編寫服務并沒有什么回報。您可以將 jira 視為數據源并使用典型的數據分析工具帶。例如,您可以使用 jupyter,獲取項目中最近的錯誤列表,準備“特征”列表(對分析有價值的屬性),利用 pandas 計算統計數據,并嘗試使用 scikit-learn 預測趨勢。在這篇文章中,我想解釋一下如何做到這一點。
準備
jira api 訪問
這里我們要說的是云版jira。但如果您使用的是自托管版本,主要概念幾乎是相同的。
首先,我們需要創建一個密鑰來通過 rest api 訪問 jira。為此,請轉到配置文件管理 – https://id.atlassian.com/manage-profile/profile-and-visibility 如果選擇“安全”選項卡,您將找到“創建和管理 api 令牌”鏈接:
在此處創建一個新的 api 令牌并安全地存儲它。我們稍后會使用這個令牌。
jupyter 筆記本
處理數據集最方便的方法之一是使用 jupyter。如果您不熟悉這個工具,請不要擔心。我將展示如何使用它來解決我們的問題。對于本地實驗,我喜歡使用 jetbrains 的 dataspell,但也有免費的在線服務。 kaggle 是數據科學家中最知名的服務之一。但是,他們的筆記本不允許您建立外部連接以通過 api 訪問 jira。另一項非常受歡迎的服務是 google 的 colab。它允許您進行遠程連接并安裝額外的 python 模塊。
jira 有一個非常易于使用的 rest api。您可以使用您最喜歡的 http 請求方式進行 api 調用并手動解析響應。然而,我們將利用一個優秀且非常流行的 jira 模塊來實現此目的。
實際使用的工具
數據分析
讓我們結合所有部分來提出解決方案。
進入google colab界面并創建一個新筆記本。創建筆記本后,我們需要將之前獲得的 jira 憑據存儲為“秘密”。單擊左側工具欄中的“密鑰”圖標打開相應的對話框并添加兩個具有以下名稱的“秘密”:jira_user 和 jira_password。在屏幕底部,您可以看到如何訪問這些“秘密”:
接下來是安裝額外的 python 模塊以進行 jira 集成。我們可以通過在筆記本單元范圍內執行 shell 命令來做到這一點:
!pip install jira
登錄后復制
輸出應如下所示:
collecting jira downloading jira-3.8.0-py3-none-any.whl (77 kb) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 77.5/77.5 kb 1.3 mb/s eta 0:00:00 requirement already satisfied: defusedxml in /usr/local/lib/python3.10/dist-packages (from jira) (0.7.1) ... installing collected packages: requests-toolbelt, jira successfully installed jira-3.8.0 requests-toolbelt-1.0.0
登錄后復制
我們需要獲取“秘密”/憑證:
from google.colab import userdata jira_url = 'https://******.atlassian.net' jira_user = userdata.get('jira_user') jira_password = userdata.get('jira_password')
登錄后復制
并驗證與 jira cloud 的連接:
from jira import jira jira = jira(jira_url, basic_auth=(jira_user, jira_password)) projects = jira.projects() projects
登錄后復制
如果連接正常并且憑據有效,您應該會看到一個非空的項目列表:
[<jira project: key="proj1" name="name here.." id="10234">, <jira project: key="proj2" name="friendly name.." id="10020">, <jira project: key="proj3" name="one more project" id="10045">, ... </jira></jira></jira>
登錄后復制
這樣我們就可以從 jira 連接并獲取數據了。下一步是獲取一些數據以使用 pandas 進行分析。讓我們嘗試獲取某個項目在過去幾周內已解決問題的列表:
jira_filter = 19762 issues = jira.search_issues( f'filter={jira_filter}', maxresults=false, fields='summary,issuetype,assignee,reporter,aggregatetimespent', )
登錄后復制
我們需要將數據集轉換為pandas數據框:
import pandas as pd df = pd.dataframe([{ 'key': issue.key, 'assignee': issue.fields.assignee and issue.fields.assignee.displayname or issue.fields.reporter.displayname, 'time': issue.fields.aggregatetimespent, 'summary': issue.fields.summary, } for issue in issues]) df.set_index('key', inplace=true) df
登錄后復制
輸出可能如下所示:
我們想分析一下解決問題通常需要多長時間。人都是不理想的,所以有時他們會忘記記錄工作。如果您嘗試使用 jira 內置工具分析此類數據,則會帶來麻煩。但我們用pandas做一些調整并不是問題。例如,我們可以將“時間”字段從秒轉換為小時,并用中值替換缺失的值(注意,如果有很多間隙,dropna 可能更合適):
df['time'].fillna(df['time'].median(), inplace=true) df['time'] = df['time'] / 3600
登錄后復制
我們可以輕松地可視化分布以找出異常情況:
df['time'].plot.bar(xlabel='', xticks=[])
登錄后復制
查看受讓人解決的問題的分布也很有趣:
top_solvers = df.groupby('assignee').count()[['time']] top_solvers.rename(columns={'time': 'tickets'}, inplace=true) top_solvers.sort_values('tickets', ascending=false, inplace=true) top_solvers.plot.barh().invert_yaxis()
登錄后復制
它可能看起來像下面這樣:
預測
讓我們嘗試預測完成所有未決問題所需的時間。當然,我們可以在沒有機器學習的情況下,通過使用簡單的近似和平均時間來解決問題。因此,預計所需時間就是未解決問題的數量乘以解決問題的平均時間。例如,解決一個問題的平均時間為 2 小時,而我們有 9 個未解決的問題,因此解決所有問題所需的時間為 18 小時(近似值)。這是一個足夠好的預測,但我們可能知道解決的速度取決于產品、團隊和問題的其他屬性。如果我們想改進預測,我們可以利用機器學習來解決這個任務。
高級方法如下所示:
獲取“學習”數據集
清理數據
準備“特征”,又名“特征工程”
訓練模型
使用模型來預測目標數據集的某些值
第一步,我們將使用過去 30 周的門票數據集。出于說明目的,此處對某些部分進行了簡化。在現實生活中,用于學習的數據量應該足夠大才能建立有用的模型(例如,在我們的例子中,我們需要分析數千個問題)。
issues = jira.search_issues( f'project = pps and status in (resolved) and created >= -30w', maxresults=false, fields='summary,issuetype,customfield_10718,customfield_10674,aggregatetimespent', ) closed_tickets = pd.dataframe([{ 'key': issue.key, 'team': issue.fields.customfield_10718, 'product': issue.fields.customfield_10674, 'time': issue.fields.aggregatetimespent, } for issue in issues]) closed_tickets.set_index('key', inplace=true) closed_tickets['time'].fillna(closed_tickets['time'].median(), inplace=true) closed_tickets
登錄后復制
就我而言,大約有 800 張門票,并且只有兩個“學習”字段:“團隊”和“產品”。
下一步是獲取我們的目標數據集。我為什么這么早做?我想一次性清理兩個數據集并進行“特征工程”。否則,結構之間的不匹配可能會導致問題。
issues = jira.search_issues( f'project = pps and status in (open, reopened)', maxresults=false, fields='summary,issuetype,customfield_10718,customfield_10674', ) open_tickets = pd.dataframe([{ 'key': issue.key, 'team': issue.fields.customfield_10718, 'product': issue.fields.customfield_10674, } for issue in issues]) open_tickets.set_index('key', inplace=true) open_tickets
登錄后復制
請注意,我們這里沒有“時間”列,因為我們想要預測它。讓我們取消它并結合兩個數據集來準備“特征”。
open_tickets['time'] = 0 tickets = pd.concat([closed_tickets, open_tickets]) tickets
登錄后復制
“團隊”和“產品”列包含字符串值。處理這個問題的方法之一是將每個值轉換為帶有布爾標志的單獨字段。
products = pd.get_dummies(tickets['product'], prefix='product') tickets = pd.concat([tickets, products], axis=1) tickets.drop('product', axis=1, inplace=true) teams = pd.get_dummies(tickets['team'], prefix='team') tickets = pd.concat([tickets, teams], axis=1) tickets.drop('team', axis=1, inplace=true) tickets
登錄后復制
結果可能如下所示:
組合數據集準備完畢后,我們可以將其分成兩部分:
closed_tickets = tickets[:len(closed_tickets)] open_tickets = tickets[len(closed_tickets):][:]
登錄后復制
現在是時候訓練我們的模型了:
from sklearn.model_selection import train_test_split from sklearn.tree import decisiontreeregressor features = closed_tickets.drop(['time'], axis=1) labels = closed_tickets['time'] features_train, features_val, labels_train, labels_val = train_test_split(features, labels, test_size=0.2) model = decisiontreeregressor() model.fit(features_train, labels_train) model.score(features_val, labels_val)
登錄后復制
最后一步是使用我們的模型進行預測:
open_tickets['time'] = model.predict(open_tickets.drop('time', axis=1, errors='ignore')) open_tickets['time'].sum() / 3600
登錄后復制
最終的輸出,就我而言,是 25 小時,這比我們最初的粗略估計要高。這是一個基本的例子。但是,通過使用 ml 工具,您可以顯著擴展分析 jira 數據的能力。
結論
有時,jira 內置工具和插件不足以進行有效分析。此外,許多第 3 方插件相當昂貴,每年花費數千美元,而且您仍然很難讓它們按照您想要的方式工作。然而,您可以通過 jira api 獲取必要的信息,輕松利用眾所周知的數據分析工具,并超越這些限制。我花了很多時間使用各種 jira 插件,試圖為項目創建好的報告,但它們經常錯過一些重要的部分。在 jira api 之上構建工具或功能齊全的服務通常看起來也有些過頭了。這就是為什么 jupiter、pandas、matplotlib、scikit-learn 等典型數據分析和 ml 工具在這里可能效果更好。