如果我在 Azure Devops 中查询我的工作项,当我添加要显示的列时,只有
comments count
和 discussions
,但是是否有任何位置可以在我的输出保存的 csv 中包含工作项的所有注释文件?我需要将每个工作项目的评论存档在 csv 中。
我可以通过 azure 桌面 Web UI 执行此操作吗?或者我是否需要使用 azure api 编写自己的脚本来查看评论并将其添加到工作项
我可以通过 azure 桌面 Web UI 执行此操作吗?或者我需要 使用azure api编写我自己的脚本来查看评论并添加 他们到一个工作项目
对于你的第一个问题,答案是“否”。既然你想要一个工作项的所有评论,那么内置的 UI 功能将无法实现你的要求。 对于你的第二个问题,答案是
是。
from azure.devops.connection import Connection
from msrest.authentication import BasicAuthentication
import requests
import csv
import os
#get all the comments of a work item
def get_work_items_comments(wi_id):
#get a connection to Azure DevOps
organization_url = 'https://dev.azure.com/xxx'
personal_access_token = 'xxx'
credentials = BasicAuthentication('', personal_access_token)
connection = Connection(base_url=organization_url, creds=credentials)
work_item_tracking_client = connection.clients.get_work_item_tracking_client()
#get the work item
work_item = work_item_tracking_client.get_work_item(wi_id)
#get the comments of the work item
comments_ref = work_item._links.additional_properties['workItemComments']['href']
#send a request to get the comments
response = requests.get(comments_ref, auth=('', personal_access_token))
#get the comments
comments = response.json()['comments']
return comments
#return work item id, work item title and related work item comments
def get_work_items_results(wi_id):
#get a connection to Azure DevOps
organization_url = 'https://dev.azure.com/xxx'
personal_access_token = 'xxx'
credentials = BasicAuthentication('', personal_access_token)
connection = Connection(base_url=organization_url, creds=credentials)
work_item_tracking_client = connection.clients.get_work_item_tracking_client()
#get the work item
work_item = work_item_tracking_client.get_work_item(wi_id)
#get the title of the work item
title = work_item.fields['System.Title']
#get the work item id
id = work_item.id
#get the comments of the work item
items = get_work_items_comments(wi_id)
array_string = []
for item in items:
text = item['text']
array_string.append(text)
print(item['text'])
return id, title, array_string
#Save the work item id, work item title and related work item comments to a csv file
#create folder workitemresults if not exist
if not os.path.exists('workitemresults'):
os.makedirs('workitemresults')
with open('workitemresults/comments_results.csv', 'w', newline='') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(['Workitem ID','Workitem Title','Workitem Comments'])
#===if you want multiple work items, just for loop in this place and replace the value 120 in this place, 120 is the workitem id on my side.===
writer.writerow(get_work_items_results(120))
上面的代码是捕获一个工作项的评论和信息。它在我这边工作得很好(我已经在代码中提到了应该放置“for 循环”的位置,在该位置使用 for 循环您可以捕获多个工作项):
#remove <div> and </div> from the text
text = text.replace('<div>','')
text = text.replace('</div>','')
结果:
注1
:此脚本用纯文本替换html以生成有效的.csv,这意味着任何格式都将丢失
注 2
:这大约需要 15 分钟。 5000张门票
from datetime import datetime
from bs4 import BeautifulSoup
from tqdm import tqdm
from azure.devops.connection import Connection
from azure.devops.exceptions import AzureDevOpsServiceError
from msrest.authentication import BasicAuthentication
import requests
import csv
import os
EXPORT_DIR_PATH = "exports" # The path of the directory to export the tickets to
ORGANIZATION_URL = "https://dev.azure.com/<company>" # The root path of your DevOps instance
PERSONAL_ACCESS_TOKEN = "<token>" # A personal access token, which you can generate under "User settings" --> "Personal access tokens" --> "New Token"
FIRST_WORK_ITEM_ID = 0 # The ID of the first work item you want to export
LAST_WORK_ITEM_ID = 1000 # The ID of the last work item you want to export (note that any IDs for which no work item can be found will be skipped)
def html_to_plain_text(html: str) -> str:
return ' '.join(BeautifulSoup(html, "html.parser").get_text().split())
def get_comments_of_work_item(work_item) -> dict:
"""
Returns all comments of a given work item
:param work_item:
:return:
"""
comments_ref = work_item._links.additional_properties["workItemComments"]["href"]
response = requests.get(comments_ref, auth=("", PERSONAL_ACCESS_TOKEN))
return response.json()["comments"]
def get_work_item(devops_client, wi_id: int) -> (int, str, str, str, list[str]):
"""
Returns a work item as a tuple
:param devops_client:
:param wi_id:
:return:
"""
# Get the work item
try:
work_item = devops_client.get_work_item(wi_id)
except AzureDevOpsServiceError:
# print(f"Work item #{wi_id} not found")
return None, None, None
# Get the work item's comments
comments = []
for comment in get_comments_of_work_item(work_item):
comments.append(f"{comment['createdBy']['displayName']}: {html_to_plain_text(comment['text'])}")
return (work_item.id,
work_item.fields["System.WorkItemType"],
work_item.fields["System.AssignedTo"]["displayName"] if "System.AssignedTo" in work_item.fields else None,
work_item.fields["System.Title"],
html_to_plain_text(work_item.fields["System.Description"]) if "System.Description" in work_item.fields else None,
comments)
def connect_to_devops() -> any:
"""
Connects to the devops
:return: A connected client
"""
credentials = BasicAuthentication("", PERSONAL_ACCESS_TOKEN)
connection = Connection(base_url=ORGANIZATION_URL, creds=credentials)
return connection.clients.get_work_item_tracking_client()
if __name__ == "__main__":
if not os.path.exists(EXPORT_DIR_PATH):
os.makedirs(EXPORT_DIR_PATH)
datetime_str = datetime.now().strftime(f"%Y%m%d-%H%M%S")
file_path = os.path.join(EXPORT_DIR_PATH, f"work_items_{datetime_str}.csv")
with open(file_path, "w", newline="", encoding="utf-8") as csvfile:
writer = csv.writer(csvfile, delimiter=";")
writer.writerow(["ID", "Assigned To", "Title", "Description", "Comments"])
client = connect_to_devops()
total_exported_tickets = 0
for i in (pbar := tqdm(range(FIRST_WORK_ITEM_ID, LAST_WORK_ITEM_ID + 1))):
pbar.set_description(f"Downloading work item #{i}")
work_item_line = get_work_item(client, i)
if work_item_line[0] is not None:
writer.writerow(work_item_line)
total_exported_tickets += 1
break
print(f"Finished: Exported {total_exported_tickets} tickets to '{file_path}'")