2014-02-18 15 views
1

我使用TFS 2012和Scrum流程模板開始增加。跟蹤的故事後一個Sprint在TFS

我想要一種方法來查看在衝刺的時間軸中的某個點後添加到衝刺中的故事。

我們通常有一箇中間衝刺「迷你」規劃會議。這使我們能夠重新平衡整個團隊中的現有工作負載,但是如果我們看到我們完成了比預期更多的工作,它還允許我們向sprint添加更多的PBI /用戶故事。

我看不到查詢方式來查看故事的迭代路徑何時發生更改。這可能嗎?

+0

但不知道這是否有助於歷史審計數據(隨時間變化),我發現它更容易直接查詢TFS數據倉庫。我所做的是對我想要的數據進行SQL查詢並將其嵌入到Excel中,易於訪問,易於刷新。如果您選擇此選項,您正在查找的視圖是WorkItemHistoryView –

回答

2

我發現有必要創建一個使用TFS的API來完成這個自定義應用程序。

首先,你需要獲得相關的工作項列表:

var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(options.TfsUri)); 

    var workItemStore = tfs.GetService<WorkItemStore>(); 
    var project = workItemStore.Projects[options.ProjectName]; 
    var projectIterationPath = string.Format("{0}\\{1}", project.Name, options.IterationPath); 

    var items = project.Store.Query(
      "SELECT [System.Id] FROM WorkItems WHERE [System.WorkItemType] IN ('User Story', 'Product Backlog Item', 'Bug') AND [System.IterationPath] = '" + 
      projectIterationPath + "'"); 

然後,你必須總結各個點的總數(總工VS完成的工作):

var pointTotal = workItems 
     .Where(
      i => 
       i.Fields[effortField].Value != null && 
       decimal.TryParse(i.Fields[effortField].Value.ToString(), out temp)) 
     .Sum(i => decimal.Parse(i[effortField].ToString())); 

    var pointsCompleted = workItems 
     .Where(
      i => 
       (string.Compare(i.State, "done", true) == 0 || string.Compare(i.State, "closed", true) == 0) && 
       i.Fields[effortField].Value != null && 
       decimal.TryParse(i.Fields[effortField].Value.ToString(), out temp)) 
     .Sum(i => decimal.Parse(i[effortField].ToString())); 

然後,你需要獲取有關開始和結束迭代日期的信息:

var iterationSchedule = GetIterationSchedule(tfs, project.Uri.ToString(), 

    private static ScheduleInfo GetIterationSchedule(TfsTeamProjectCollection tfs, string projectUri, string iterationPath) 
    { 
     var css = tfs.GetService<ICommonStructureService4>(); 
     var structures = css.ListStructures(projectUri); 
     var iterations = structures.FirstOrDefault(s => s.StructureType.Equals("ProjectLifecycle")); 

     if (iterations != null) 
     { 
      string projectName = css.GetProject(projectUri).Name; 

      XmlElement iterationsTree = css.GetNodesXml(new[] {iterations.Uri}, true); 
      return GetIterationDates(iterationsTree.ChildNodes[0], projectName, iterationPath); 
     } 

     return null; 
    } 

    private static ScheduleInfo GetIterationDates(XmlNode node, string projectName, string iterationPath) 
    { 
     var targetIterationPath = string.Format("\\{0}\\Iteration\\{1}", projectName, iterationPath); 
     XElement targetIteration = null; 

     if (node != null) 
     { 
      var iterations = XDocument.Parse(node.InnerXml); 

      targetIteration = iterations.Descendants("Node") 
       .Where(n => n.Attribute("Path") != null && !string.IsNullOrEmpty(n.Attribute("Path").Value)) 
       .SingleOrDefault(n => string.Compare(n.Attribute("Path").Value, targetIterationPath, true) == 0); 
     } 

     if (targetIteration != null) 
     { 
      // Attempt to read the start and end dates if they exist. 
      string strStartDate = (targetIteration.Attribute("StartDate") != null) 
       ? targetIteration.Attribute("StartDate").Value 
       : null; 
      string strEndDate = (targetIteration.Attribute("FinishDate") != null) 
       ? targetIteration.Attribute("FinishDate").Value 
       : null; 

      DateTime? rStateDate = null, rEndDate = null; 

      if (!string.IsNullOrEmpty(strStartDate) && !string.IsNullOrEmpty(strEndDate)) 
      { 
       DateTime startDate; 
       if (DateTime.TryParse(strStartDate, out startDate)) 
        rStateDate = startDate; 

       DateTime endDate; 
       if (DateTime.TryParse(strEndDate, out endDate)) 
        rEndDate = endDate; 
      } 

      return new ScheduleInfo 
      { 
       IterationPath = iterationPath, 
       StartDate = rStateDate, 
       EndDate = rEndDate 
      }; 
     } 

     return null; 
    } 

最後你把它所有在一起,以生成您的衝刺總結:

 foreach (var item in workItems) 
     { 
      var postSprintStartRevisions = (from r in item.Revisions.Cast<Revision>() 
       where r.Fields.Cast<Field>() 
         .Any(f => f.Name == "Revised Date" && ((DateTime) f.Value) >= postStartDate) 
        && r.Fields.Cast<Field>() 
         .Any(
          f => 
           f.Name == "Iteration Path" 
           && string.Compare(f.OriginalValue.ToString(), projectIterationPath, true) != 0 
           && string.Compare(f.Value.ToString(), projectIterationPath, true) == 0) 
       select r).ToArray(); 

      if (postSprintStartRevisions.Any() && item[effortField] != null) 
      { 
       pointsPostSprintStart += decimal.Parse(item[effortField].ToString()); 
      } 
     } 

該代碼並不漂亮,但它確實讓我想我需要。

用法:

Usage: pbitracker -TfsUri [uri] -ProjectName [name] -IterationPath [path] -b 3 

    -u, --TfsUri    Required. TFS Project Collection Uri. Ex: 
          http://tfs:8080/tfs/defaultcollection 

    -p, --ProjectName  Required. TFS Project Name. Ex: MyProject 

    -i, --IterationPath  Required. Iteration Path. Ex: 'GA\Sprint 3' 

    -b, --StartDateBuffer (Default: 3) Number of days after the start date to 
          consider the worked as added post-start 

    --help     Display this help screen. 

我考慮什麼「後加入啓動」基於傳遞到應用程序的選項。我默認爲3天,所以如果你的衝刺活動在星期一開始,並且你在星期四添加了工作,那麼這項工作被認爲是在開始後添加的點數。我會嘗試在GitHub或CodePlex上解決這個問題,然後我會更新答案。

輸出示例:

Querying with the following options... 
TFS Uri:    http://tfs:8080/tfs/DefaultCollection 
Project Name:   MyProject 
Iteration Path:   GA\Sprint 16 
Please wait... 


Iteration Start Date:     2/24/2014 12:00:00 AM 
Iteration End Date:      3/7/2014 12:00:00 AM 
Iteration Point Total:     168 
Iteration Point Total Completed:  168 
Iteration Points Starting Points:  162 
Iteration Points Added Post-Start:  6 
0

我想有很多方法可以做到這一點。但我所做的是創建一個簡單的查詢。在下面的屏幕截圖中,我使用了TFS2012 ALM虛擬機和FabrikamFiber示例。您可以看到查詢和結果窗格的構造。

最後一項顯示了衝刺開始後我拉入衝刺的產品積壓項目,我認爲這是您要求的內容。您可以通過「更改日期」值比衝刺開始日期更晚看到此情況。

enter image description here

此後,這是一個簡單的任務,該查詢添加到您的收藏夾。不要忘記改變迭代路徑!

+0

更改的日期是否更新*工作項的任何*更改? – RMD

+0

是的。這將包括對其他字段進行更新的項目。 –