【问题标题】:Show Visual Studio Online Work Item Cycle Times显示 Visual Studio Online 工作项周期时间
【发布时间】:2015-10-17 22:22:55
【问题描述】:

理想情况下,我想通过 Power BI 在 Visual Studio Online 中计算由 Product Backlog Items 表示的整个价值流的周期时间。 (然后我想知道每个状态的时间,即它处于“新”状态多长时间,或者它停留在“已提交”状态多长时间。)

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

product backlog item time between created timestamp to closed timestamp

这将是一个起点,但这当然会显示“按标题排列的持续时间分钟数”。

大多数其他尝试导致:

无法确定字段之间的关系

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

【问题讨论】:

    标签: azure-devops powerbi


    【解决方案1】:

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

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

    更新:在研究之后(见下面我的 cmets),我意识到构建您需要的查询需要一些奥林匹克级的强大查询技能。所以我为你创建了它:)。以下内容适用于 Power BI 桌面,但由于某种原因无法在 PowerBI.com 中刷新(我正在检查)。为此,您需要将您的 VSO 帐户和查询 ID 放入 ConnectionInfo 记录中。

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

    您可以通过将共享查询保存到您的 VSO 并使用 curl 使用该查询检索 id 来获取 QueryID:

    //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 =”开始的一些步骤,因为您的查询可能具有与我的不同的字段。如果您返回创建、解决和关闭的日期,我使用的持续时间计算应该适合您。当在 Power BI Desktop 中受到质询时,您将身份验证设置为“基本”并提供所需的凭据。

    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
    

    【讨论】:

    • 这可以在 Power BI 门户网站中使用吗?
    • 您可以在 Power BI Desktop 或安装了 Power Query 的 Excel 2013 中执行此操作。由于您提到 Power BI Web 门户,我假设您在 Power BI 中使用 Visual Studio 在线内容包。目前,我们仍在制定将 VSO 的查询连接性引入 PBI 桌面的计划。您需要以其他方式获取数据,例如从 VSO 提取数据(如果可能的话)来进行您想要进行的分析。持续时间函数可以让你做你想要的计算,你只需要在那里获取数据:)。
    • 我刚刚检查过,看起来 VSO 对其其余 API 具有基本身份验证。我自己没有尝试过,但是您应该能够使用具有基本身份验证的其余 API 和 Power BI Desktop 中的 Web 源获取数据体验来提取工作项数据。您将需要使用“平面”VSO 查询结构来拉取查询。您需要专门使用 GET 动词(因为在查询体验中 POST 动词存在一些安全限制)。这将要求您在 VSO 中有一个保存的查询,您将从中提取结果。如果你不能让它工作,请告诉我。 HTH
    猜你喜欢
    • 2015-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-14
    • 2023-03-21
    • 1970-01-01
    • 2015-05-31
    相关资源
    最近更新 更多