显示 Visual Studio 联机工作项周期时间

Show Visual Studio Online Work Item Cycle Times

理想情况下,我想通过 Power BI 在线计算 Visual Studio 中产品待办列表项表示的整个价值流的周期时间。 (然后我很想获得每个状态的时间,即它处于 "New" 状态多长时间,或者它停留在 "Committed" 状态多长时间。)

首先,我对使用代表产品积压项目 created dateclosed date 之间的时间的计算值感兴趣。在此之后,我热衷于找到值的分布。

product backlog item time between created timestamp to closed timestamp

将是一个起点,但这当然显示 "queued duration minutes by title"。

大多数其他尝试的结果是:

Can’t determine relationships between the fields

有没有办法获得一些周期时间的指示?

假设每一行都有创建日期和关闭日期,您可以使用 power query duration 函数来获取两个日期之间的差异。

https://msdn.microsoft.com/en-us/library/mt296613.aspx

更新:在调查之后(请参阅下面我的评论)我意识到构建您需要的查询需要一些奥林匹克级别的查询技巧。所以我为你创建了它 :)。以下内容适用于 Power BI 桌面,但由于某种原因在 PowerBI.com 中没有刷新(我正在检查)。要完成这项工作,您需要将您的 VSO 帐户和查询 ID 放入 ConnectionInfo 记录中。

建议设置备用凭据而不是使用 BASIC 身份验证。

您可以通过将共享查询保存到您的 VSO 来获取 QueryID,然后使用 curl 通过查询检索 ID:

//Replace YOURACCOUNT and YOURPROJECT with correct values below
curl -u username:password https://YOURACCOUNT.visualstudio.com/DefaultCollection/YOURPROJECT/_apis/wit/queries?$depth=1&api-version=1.0

然后您可以使用以下查询(创建一个空白查询并将其粘贴到 Power BI Desktop 的高级编辑器中)。您可能需要调整从 "workitems =" 开始的一些步骤,因为您的查询可能包含与我的查询不同的字段。如果您 return 创建、解决和关闭日期,我使用的持续时间计算应该适合您。在 Power BI Desktop 中接受质询并提供所需凭据时,您会将身份验证设置为 "Basic"。

let
//TODO: replace YOURACCOUNT and YOURQUERYID below 
ConnectionInfo = [account = "YOURACCOUNT.visualstudio.com", queryID="YOURQUERYID"],
account = Record.FieldValues(ConnectionInfo){0},
rootQuery = let
    queryID = Record.FieldValues(ConnectionInfo){1},
    query = "https://" & account & "/DefaultCollection/PowerBIClients/_apis/wit/wiql/" & queryID,
    Source = Json.Document(Web.Contents(query))
in
    Source,

#"INT-columns" = let
    Source = rootQuery,
    columns = Source[columns],
    #"Converted to Table" = Table.FromList(columns, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Expanded Column1" = Table.ExpandRecordColumn(#"Converted to Table", "Column1", {"referenceName", "name", "url"}, {"referenceName", "name", "url"})
in
    #"Expanded Column1",

#"RAW-workitems" = let
    Source = rootQuery,
    workItems = Source[workItems],
    #"Converted to Table" = Table.FromList(workItems, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Expanded Column1" = Table.ExpandRecordColumn(#"Converted to Table", "Column1", {"id", "url"}, {"id", "url"})
in
    #"Expanded Column1",

#"INT-columnClause" = let
    Source = #"INT-columns",
    colNames = Table.RemoveColumns(Source,{"name", "url"}),
    list = Table.ToList(colNames),
    joined = Text.Combine(list, ",")
in
    joined,

#"INT-workitemsToGet" = let
    Source = #"RAW-workitems",
    col = Table.RemoveColumns(Source,{"url"}),
    #"Changed Type" = Table.TransformColumnTypes(col,{{"id", type text}}),
    list = Table.ToList(#"Changed Type"),
    l = List.Count(list),
    limits = List.Generate(()=>0, each _ < l, each _ + 100),
    #"Converted to Table" = Table.FromList(limits, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Renamed Columns" = Table.RenameColumns(#"Converted to Table",{{"Column1", "iterations"}}),
    #"Added Custom" = Table.AddColumn(#"Renamed Columns", "itemsToGet", each List.Range(list, [iterations], 100)),
    #"Added Custom1" = Table.AddColumn(#"Added Custom", "itemsToGetString", each Text.Combine([itemsToGet], ",")),
    #"Removed Columns" = Table.RemoveColumns(#"Added Custom1",{"iterations", "itemsToGet"})
in
    #"Removed Columns",

#"INT-workitemRequests" = let
    Source = #"INT-workitemsToGet",

    #"Added Custom" = Table.AddColumn(Source, "requests", each "https://" & account & "/DefaultCollection/_apis/wit/workitems?ids=" & [itemsToGetString] & "&fields=" & #"INT-columnClause"),
    #"Removed Columns" = Table.RemoveColumns(#"Added Custom",{"itemsToGetString"})
in
    #"Removed Columns",

workitems = let
    #"INT-workitemsRequests (2)" = let
    Source = #"INT-workitemRequests",
    results = Table.AddColumn(Source, "Results", each Json.Document(Web.Contents([requests]))),
    out = 1
in
    results,
    #"Removed Columns" = Table.RemoveColumns(#"INT-workitemsRequests (2)",{"requests"}),
    #"Expanded Results" = Table.ExpandRecordColumn(#"Removed Columns", "Results", {"count", "value"}, {"Results.count", "Results.value"}),
    #"Expanded Results.value" = Table.ExpandListColumn(#"Expanded Results", "Results.value"),
    #"Expanded Results.value1" = Table.ExpandRecordColumn(#"Expanded Results.value", "Results.value", {"id", "fields", "url"}, {"Results.value.id", "Results.value.fields", "Results.value.url"}),
    #"Expanded Results.value.fields" = Table.ExpandRecordColumn(#"Expanded Results.value1", "Results.value.fields", {"System.Id", "System.WorkItemType", "System.State", "System.AssignedTo", "System.CreatedDate", "System.Title", "Microsoft.VSTS.Common.ResolvedDate", "Microsoft.VSTS.Common.ClosedDate"}, {"System.Id", "System.WorkItemType", "System.State", "System.AssignedTo", "System.CreatedDate", "System.Title", "Microsoft.VSTS.Common.ResolvedDate", "Microsoft.VSTS.Common.ClosedDate"}),
    #"Removed Columns1" = Table.RemoveColumns(#"Expanded Results.value.fields",{"Results.count", "Results.value.id"}),
    #"Renamed Columns" = Table.RenameColumns(#"Removed Columns1",{{"Results.value.url", "WorkItemUrl"}}),
    #"Changed Type" = Table.TransformColumnTypes(#"Renamed Columns",{{"Microsoft.VSTS.Common.ResolvedDate", type datetime}, {"Microsoft.VSTS.Common.ClosedDate", type datetime}, {"System.CreatedDate", type datetime}}),
    #"Added Custom" = Table.AddColumn(#"Changed Type", "DurationToResolved", each Duration.TotalDays([Microsoft.VSTS.Common.ResolvedDate] - [System.CreatedDate])),
    #"Added Custom1" = Table.AddColumn(#"Added Custom", "DurationToClosed", each Duration.TotalDays([Microsoft.VSTS.Common.ClosedDate] - [System.CreatedDate])),
    #"Changed Type1" = Table.TransformColumnTypes(#"Added Custom1",{{"DurationToResolved", type number}, {"DurationToClosed", type number}})
in
    #"Changed Type1"    

in workitems