Заметки консультанта

Шамрай Александр Владимирович

Archive for Январь 2014

Практическое руководство по отчетности TFS. Часть 1 – VS Online и OData

Posted by Шамрай Александр на Январь 20, 2014

Переведена часть руководство по организации отчетности в TFS. Ниже представлена первая часть, которая касается проблематики отчетности в TFS Online. Источник и примеры находятся здесь: http://vsarreportguide.codeplex.com/

Содержание

Posted in Microsoft, Team Foundation Server, TFS Practical Reporting Guide, Visual Studio | Отмечено: , , | Leave a Comment »

Практическое руководство по отчетности TFS. Введение

Posted by Шамрай Александр на Январь 20, 2014

<<Содержание

Добро пожаловать в руководство по отчетности TFS, где мы, Рейнджеры ALM, возьмем вас в увлекательное путешествие, в котором вы откроете для себя интересные и мощные функции отчетности, предоставляемые сервером Team Foundation Server.

Это руководство фокусируется на предоставлении практических рекомендаций и решения с примером, которые позволяют пользователям Visual Studio Online создавать отчеты, основанные на данных из рабочих элементов.

Целевая аудитория

Мы ожидаем, что большинство нашей аудитории будут разработчики. Однако руководство предназначено для всех, от разработчика до администратора, оно объяснит, как создать и использовать отчеты, которые предназначены для Visual Studio Online, чтобы управлять проектами разработки программного обеспечения, размещенными в облаке. Руководство предполагает наличие базовых знаний о Visual Studio Online и понимания отчетности, иными словами, пользователи Visual Studio Online среднего уровня и продвинутые.

Что вам понадобится

Для этого руководства необходимы следующие поддерживаемые компоненты:

  • Visual Studio 2012 и Visual Studio 2013
  • Visual Studio Online или локальный Team Foundation Server
  • Microsoft Office Excel

Рейнджеры Visual Studio ALM

Рейнджеры Visual Studio ALM представляют собой особую группу из членов группы продуктов Visual Studio, Майкрософт, Microsoft Most Valuable Professionals (MVP) и лидеров сообщества Visual Studio. Их миссия заключается в обеспечении внешних решений для отсутствующих возможностей и руководств. Постоянно пополняемое содержимое Рейнджеров доступно онлайн.

Участники

Гордон Биминг, Джефф Левинсон, Джим Зубрит, Маттиас Скольд и Тед Мэлоун.

Использование исходного кода для примера, ошибки и поддержка

Весь исходный код данного руководстве доступен для скачивания на домашней странице Практического руководства отчетности TFS, где мы также предоставляем последние исправления и обновления. Скачайте файл eBook1-Package.exe.

Дополнительные ресурсы Рейнджеров ALM

Информация о Рейнджерах ALM – http://aka.ms/vsarunderstand

Решения Рейнджеров Visual Studio ALM – http://aka.ms/vsarsolutions

<<Содержание

Posted in Microsoft, Team Foundation Server, TFS Practical Reporting Guide, Visual Studio | Отмечено: , , | Leave a Comment »

Практическое руководство по отчетности TFS. Заключение

Posted by Шамрай Александр на Январь 20, 2014

<<Содержание

На этом завершается наше путешествие по модели табличного хранилища. Мы затронули теорию, познакомили вас с моделью табличного хранилища, PowerPivot, OData и отчетностью и показали, когда следует использовать эти технологии. Мы рассмотрели различные примеры в руководстве, которые провели вас через теорию, проектирование и практическое использование PowerPivot и OData.

На заключительных страницах этого руководства вы найдете наш постер(ы) Краткий Справочник. Вы можете напечатать и повесить их в комнате с командой.

Мы находимся в начале пути создания решения и примеров отчетов. Мы надеемся, что вы оцените эти технологии и что вы найдете это руководство полезным.

Искренне

Microsoft Visual Studio ALM Rangers

<<Содержание

Posted in Microsoft, Team Foundation Server, TFS Practical Reporting Guide, Visual Studio | Отмечено: , , | Leave a Comment »

Практическое руководство по отчетности TFS. Предисловие

Posted by Шамрай Александр на Январь 20, 2014

<<Содержание

Функциональность отчетов в Team Foundation Server работает как центр мира управления жизненным циклом приложений. В почти каждом демо для заказчика руководители готовы все подписать, как только они видят, как можно настроить панели мониторинга SharePoint, чтобы дать представление о статусе проекта из нескольких проектов, реализуемых в организации. Это был один из ключевых моментов продажи, когда руководители выбирают среди различных решений ALM, – TFS имеет все необходимые данные и по ним можно строить отчеты. К сожалению наш существующий стек отчетности не может быть перенесен в облако. Поэтому мы исследуем как вложить наши значительные инвестиции в создание нового стека отчетности, который может работать как локально, так и для наших клиентов облака. Мы получили много отзывов о том, что нашего существующего сценария для отчетности недостаточно, и мы ищем пути, чтобы мы могли использовать последний стек Microsoft Business Intelligence не только для удовлетворения существующей функциональности, но и чтобы превзойти ее более простым и быстрым решением. Табличные модели, PowerPivot и PowerView – это все новые удивительные технологии, которые, как мы надеемся, помогут нам в достижении этих долгосрочных целей. Командой разработчиков уже ведутся работы над созданием коробочного решения и для сервисов, которое может обеспечить для отчетности TFS многообещающее предложение. Поэтому мы надеемся, что этот документ послужит хорошим руководством, уменьшающим непонимание, и мы поощряем обратную связь из вашего опыта его использования, в свою очередь мы можем гарантировать, что ваши отзывы будут учтены и включены в дальнейшее развитие продукта командой.

Джастин Маркс – старший руководитель программы Cloud Dev Services

<<Содержание

Posted in Microsoft, Team Foundation Server, TFS Practical Reporting Guide, Visual Studio | Отмечено: , , | Leave a Comment »

Практическое руководство по отчетности TFS. Приложение.

Posted by Шамрай Александр на Январь 16, 2014

<<Содержание

Пример кода – настройка OData

WorkItemRevision

Code 1 – WorkItemRevision

// ———————————————————————————-

// Microsoft Developer & Platform Evangelism

//

// Copyright (c) Microsoft Corporation. All rights reserved.

//

// THIS CODE AND INFORMATION ARE PROVIDED «AS IS» WITHOUT WARRANTY OF ANY KIND,

// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES

// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

// ———————————————————————————-

// The example companies, organizations, products, domain names,

// e-mail addresses, logos, people, places, and events depicted

// herein are fictitious. No association with any real company,

// organization, product, domain name, email address, logo, person,

// places, or events is intended or should be inferred.

// ———————————————————————————-

namespace Microsoft.Samples.DPE.ODataTFS.Model.Entities

{

using System;

using System.Collections.Generic;

using System.Data.Services;

using System.Data.Services.Common;

using System.Reflection;

using Microsoft.Data.Services.Toolkit.QueryModel;

//Field references:

//http://msdn.microsoft.com/en-us/library/ms194971.aspx

[DataServiceKey(«Id»)]

[ETag(«ChangedDate»)]

[EntityPropertyMapping(«ChangedDate», SyndicationItemProperty.Updated, SyndicationTextContentKind.Plaintext, true)]

[EntityPropertyMapping(«Title», SyndicationItemProperty.Title, SyndicationTextContentKind.Plaintext, true)]

[EntityPropertyMapping(«Description», SyndicationItemProperty.Summary, SyndicationTextContentKind.Plaintext, true)]

public class WorkItemRevision

{

//Base information to provide in addition to WorkItem fields

public int Id { get; set; }

public string Project { get; set; }

public string Type { get; set; }

public string WebEditorUrl { get; set; }

//Base fields

public string AreaPath { get; set; }

public string IterationPath { get; set; }

public int Revision { get; set; } //aka Rev

public string Priority { get; set; }

public string Severity { get; set; }

public double StackRank { get; set; }

public string AssignedTo { get; set; }

public DateTime CreatedDate { get; set; }

public string CreatedBy { get; set; }

public DateTime ChangedDate { get; set; }

public string ChangedBy { get; set; }

public string ResolvedBy { get; set; }

public string Title { get; set; }

//valid states and transitions specific to template and work item

public string State { get; set; }

public string Reason { get; set; }

public double CompletedWork { get; set; }

public double RemainingWork { get; set; }

public string Description { get; set; }

public string ReproSteps { get; set; }

public string FoundInBuild { get; set; }

public string IntegratedInBuild { get; set; }

public int AttachedFileCount { get; set; }

public int HyperLinkCount { get; set; }

public int RelatedLinkCount { get; set; }

//Agile

public string Risk { get; set; }

public double StoryPoints { get; set; }

//Agile and CMMI

public double OriginalEstimate { get; set; }

//Scrum

public double BacklogPriority { get; set; }

public int BusinessValue { get; set; }

public double Effort { get; set; }

//Scrum and CMMI

public string Blocked { get; set; }

//CMMI

public double Size { get; set; }

[ForeignProperty]

public IEnumerable<Attachment> Attachments { get; set; }

[ForeignProperty]

public IEnumerable<Link> Links { get; set; }

public void Set(WorkItem w)

{

foreach (PropertyInfo p in w.GetType().GetProperties())

{

SetField(p.Name, p.GetValue(w, null));

}

}

public void SetField(string field, object value)

{

if (this.GetType().GetProperty(field) != null)

{

if (value != null)

{

this.GetType().GetProperty(field).SetValue(this, System.Convert.ChangeType(value, this.GetType()

.GetProperty(field) .PropertyType), null);

}

else

{

this.GetType().GetProperty(field).SetValue(this, value, null);

}

}

}

}

}

WorkItemHistoryProxy

Code 2 – WorkItemHistoryProxy

// ———————————————————————————-

// Microsoft Developer & Platform Evangelism

//

// Copyright (c) Microsoft Corporation. All rights reserved.

//

// THIS CODE AND INFORMATION ARE PROVIDED «AS IS» WITHOUT WARRANTY OF ANY KIND,

// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES

// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

// ———————————————————————————-

// The example companies, organizations, products, domain names,

// e-mail addresses, logos, people, places, and events depicted

// herein are fictitious. No association with any real company,

// organization, product, domain name, email address, logo, person,

// places, or events is intended or should be inferred.

// ———————————————————————————-

namespace Microsoft.Samples.DPE.ODataTFS.Model.Serialization

{

using System;

using System.Collections.Generic;

using System.Globalization;

using System.Linq;

using System.Net;

using System.Linq.Expressions;

using System.Text;

using System.Text.RegularExpressions;

using System.Web;

using Microsoft.Samples.DPE.ODataTFS.Model.Entities;

using Microsoft.Samples.DPE.ODataTFS.Model.ExpressionVisitors;

using System.Collections;

using Microsoft.Data.Services.Toolkit.QueryModel;

using Microsoft.TeamFoundation.WorkItemTracking;

public class TFSWorkItemHistoryProxy : TFSBaseProxy, ITfsWorkItemHistoryProxy

{

public TFSWorkItemHistoryProxy(Uri uri, ICredentials credentials)

: base(uri, credentials){}

public WorkItem GetWorkItem(int workItemId)

{

var wiql = string.Format(CultureInfo.InvariantCulture, «SELECT [System.Id] FROM WorkItems WHERE [System.Id] = {0}», workItemId);

return this.QueryWorkItems(wiql)

.Cast<TeamFoundation.WorkItemTracking.Client.WorkItem>()

.Select(w => w.ToModel(this.GetTfsWebAccessArtifactUrl(w.Uri)))

.FirstOrDefault();

}

public IEnumerable<WorkItemRevision> GetWorkItemHistoryByProject(string projectName, FilterNode rootFilterNode,

ODataQueryOperation operation)

{

var key = string.Format(CultureInfo.InvariantCulture, «TFSWorkItemProxy.GetWorkItemsByProject_{0}_{1}», projectName, this.GetFilterNodeKey(rootFilterNode));

HttpContext.Current.Items[key] = this.RequestWorkItemsByProject(projectName, rootFilterNode, operation);

return (IEnumerable<WorkItemRevision>)HttpContext.Current.Items[key];

}

private static string BuildWiql(FilterNode rootFilterNode, ODataQueryOperation operation)

{

var constrains = string.Empty;

if (rootFilterNode != null)

{

foreach (var filterNode in rootFilterNode)

{

constrains += AddComparisonConstrainToWiql(filterNode, Constants.TFS.FieldLookup(filterNode.Key));

}

}

if (constrains.StartsWith(» OR «, StringComparison.OrdinalIgnoreCase))

{

constrains = constrains.Substring(» OR «.Length);

}

if (constrains.StartsWith(» AND «, StringComparison.OrdinalIgnoreCase))

{

constrains = constrains.Substring(» OR «.Length);

}

var wiql = string.Format(CultureInfo.InvariantCulture, «SELECT [System.Id] FROM WorkItems {0} {1}», string.IsNullOrEmpty(constrains) ? string.Empty : «WHERE», constrains).Trim();

string orderByWiql = string.Empty;

if (operation.OrderStack.Count > 0)

{

orderByWiql = TFSWorkItemHistoryProxy.GenerateOrderByWiql(operation.OrderStack);

}

wiql = wiql + » « + orderByWiql;

return wiql;

}

private static string GenerateOrderByWiql(Stack<ODataOrderExpression> orderExpressionStack)

{

if (orderExpressionStack == null)

{

throw new ArgumentNullException(«orderExpressionStack»);

}

StringBuilder finalOrderBy = new StringBuilder(«ORDER BY «);

//When the $top param is used with WCF Data Services Toolkit, this appears to

//also imply ordering by key ascending (in this case work item ID), so if the

//user also issues an explicit $orderby param for Id there will be a duplicate.

//It appears that the first $oderby value (top of stack) is the one provided

//explicitly by the user, so we take that one and track that it has been seen

//already. Subsequent order by fields are ignored.

ISet<string> seen = new HashSet<string>();

while (orderExpressionStack.Count > 0)

{

ODataOrderExpression expr = orderExpressionStack.Pop();

UnaryExpression unExpr = ((UnaryExpression)expr.Expression);

var opString = unExpr.Operand.ToString();

var name = opString.Substring(opString.IndexOf(‘.’) + 1);

name = Constants.TFS.FieldLookup(name);

if (!seen.Contains(name))

{

if (expr.OrderMethodName.StartsWith(«ThenBy»))

{

finalOrderBy.Append(» , «);

}

finalOrderBy.Append(name);

switch (expr.OrderMethodName)

{

case «OrderBy»:

case «ThenBy»:

finalOrderBy.Append(» asc»);

break;

case «OrderByDescending»:

case «ThenByDescending»:

finalOrderBy.Append(» desc»);

break;

}

seen.Add(name);

}

}

return finalOrderBy.ToString();

}

private static string AddComparisonConstrainToWiql(FilterNode filterNode, string tfsFieldName)

{

if (filterNode != null)

{

var sign = default(string);

switch (filterNode.Sign)

{

case FilterExpressionType.Equal:

sign = «=»;

break;

case FilterExpressionType.NotEqual:

sign = «<>»;

break;

case FilterExpressionType.GreaterThan:

sign = «>»;

break;

case FilterExpressionType.GreaterThanOrEqual:

sign = «>=»;

break;

case FilterExpressionType.LessThan:

sign = «<«;

break;

case FilterExpressionType.LessThanOrEqual:

sign = «<=»;

break;

case FilterExpressionType.Contains:

sign = filterNode.Key.Equals(«AreaPath», StringComparison.OrdinalIgnoreCase) || filterNode.Key.Equals(«IterationPath», StringComparison.OrdinalIgnoreCase) ? «UNDER» : «CONTAINS»;

break;

case FilterExpressionType.NotContains:

sign = filterNode.Key.Equals(«AreaPath», StringComparison.OrdinalIgnoreCase) || filterNode.Key.Equals(«IterationPath», StringComparison.OrdinalIgnoreCase) ? «NOT UNDER» : «NOT CONTAINS»;

break;

default:

throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, «WorkItem {0} can only be filtered with equal, not equal, greater than, lower than, greater than or equal, lower than or equal operators», filterNode.Key));

}

return string.Format(CultureInfo.InvariantCulture, » {0} {1} {2} ‘{3}’ «, filterNode.NodeRelationship.ToString(), tfsFieldName, sign, filterNode.Value);

}

return string.Empty;

}

private IEnumerable<WorkItemRevision> RequestWorkItemsByProject(string projectName, FilterNode rootFilterNode, ODataQueryOperation operation)

{

FilterNode newFilterNodeStartWithProject = new FilterNode() { Key = «Project, Sign = FilterExpressionType.Equal, Value = projectName };

if (rootFilterNode != null)

{

newFilterNodeStartWithProject.AddNode(rootFilterNode);

}

var wiql = BuildWiql(newFilterNodeStartWithProject, operation);

return this.ExecuteWiqlRequest(wiql, operation);

}

private IEnumerable<WorkItemRevision> ExecuteWiqlRequest(string wiql, ODataQueryOperation operation)

{

IEnumerable<WorkItemRevision> retWorkItems = null;

if (!operation.IsCountRequest)

{

if (operation.TopCount == 0)

{

//workaround for bug (I think) in WCF Data Services Toolkit.

//It appears that ODataQueryOperation.TopCount will be 0 when $select

// param is used and $top param is not explicitly sent by client

//(normally TopCount would have whatever was set for entity page size in //TFSService.InitializeService()

operation.TopCount = Constants.DefaultEntityPageSize;

}

IEnumerable<TeamFoundation.WorkItemTracking.Client.WorkItem> wilist = this.QueryWorkItems(wiql)

.Cast<TeamFoundation.WorkItemTracking.Client.WorkItem>()

.Skip(operation.SkipCount).Take(operation.TopCount)

.Select(w => w).ToArray();

List<WorkItemRevision> lst = new List<WorkItemRevision>();

foreach (TeamFoundation.WorkItemTracking.Client.WorkItem w in wilist)

{

// Get All work item revisions

foreach (TeamFoundation.WorkItemTracking.Client.Revision revision in w.Revisions)

{

WorkItemRevision wrkRev = new WorkItemRevision();

wrkRev.Set(

w.ToModel(this.GetTfsWebAccessArtifactUrl(w.Uri))

);

wrkRev.Revision = revision.Index;

// Get value of fields in the work item revision

foreach (TeamFoundation.WorkItemTracking.Client.Field field in w.Fields)

{

wrkRev.SetField(field.Name, revision.Fields[field.Name].Value);

}

lst.Add(wrkRev);

}

}

}

else

{

var workItemServer = this.TfsConnection.GetService<TeamFoundation .WorkItemTracking.Client.WorkItemStore>();

try

{

Microsoft.TeamFoundation.WorkItemTracking.Client.Query q =

new TeamFoundation.WorkItemTracking.Client.Query(workItemServer, wiql, null, false);

int cnt = q.RunCountQuery();

List<WorkItemRevision> wiBlanks = new List<WorkItemRevision>(cnt);

WorkItemRevision blank = new WorkItemRevision();

for (int i = 0; i < cnt; i++)

{

wiBlanks.Add(blank);

}

retWorkItems = wiBlanks;

}

catch (Microsoft.TeamFoundation.WorkItemTracking.Client.ValidationException ex)

{

throw new System.Data.Services.DataServiceException(500, «Internal Server Error», ex.Message, «en-US», ex);

}

}

return retWorkItems;

}

}

}

WorkItemRevisionRepositories

Code 3 – WorkItemRevisionRepositories

// ———————————————————————————-

// Microsoft Developer & Platform Evangelism

//

// Copyright (c) Microsoft Corporation. All rights reserved.

//

// THIS CODE AND INFORMATION ARE PROVIDED «AS IS» WITHOUT WARRANTY OF ANY KIND,

// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES

// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

// ———————————————————————————-

// The example companies, organizations, products, domain names,

// e-mail addresses, logos, people, places, and events depicted

// herein are fictitious. No association with any real company,

// organization, product, domain name, email address, logo, person,

// places, or events is intended or should be inferred.

// ———————————————————————————-

namespace Microsoft.Samples.DPE.ODataTFS.Model.Repositories

{

using System;

using System.Collections;

using System.Collections.Generic;

using System.Data.Services;

using System.Globalization;

using System.Linq;

using Microsoft.Data.Services.Toolkit.QueryModel;

using Microsoft.Samples.DPE.ODataTFS.Model.Entities;

using Microsoft.Samples.DPE.ODataTFS.Model.ExpressionVisitors;

using Microsoft.Samples.DPE.ODataTFS.Model.Serialization;

public class WorkItemHistoryRepository : IRepository<WorkItemRevision>

{

private readonly ITfsWorkItemHistoryProxy proxy;

public WorkItemHistoryRepository(ITfsWorkItemHistoryProxy proxy)

{

this.proxy = proxy;

}

public IEnumerable<WorkItemRevision> GetAll()

{

List<WorkItemRevision> lst = new List<WorkItemRevision>();

return lst;

}

public WorkItemRevision GetOne(string id)

{

return null;

}

[RepositoryBehavior(HandlesFilter = true, HandlesSkip = true, HandlesTop = true, HandlesOrderBy = true)]

public IEnumerable<WorkItemRevision> GetWorkItemHistoryByProject(ODataSelectManyQueryOperation operation)

{

if (operation == null)

{

throw new ArgumentNullException(«operation»);

}

var parameters = new WorkItemFilterExpressionVisitor(operation.FilterExpression).Eval();

return this.proxy.GetWorkItemHistoryByProject(operation.Key, parameters, operation);

}

}

}

TFSWorkItemFactProxy

Code 4 – WorkItemFactProxy

// ———————————————————————————-

// Microsoft Developer & Platform Evangelism

//

// Copyright (c) Microsoft Corporation. All rights reserved.

//

// THIS CODE AND INFORMATION ARE PROVIDED «AS IS» WITHOUT WARRANTY OF ANY KIND,

// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES

// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

// ———————————————————————————-

// The example companies, organizations, products, domain names,

// e-mail addresses, logos, people, places, and events depicted

// herein are fictitious. No association with any real company,

// organization, product, domain name, email address, logo, person,

// places, or events is intended or should be inferred.

// ———————————————————————————-

namespace Microsoft.Samples.DPE.ODataTFS.Model.Serialization

{

using System;

using System.Collections.Generic;

using System.Globalization;

using System.Linq;

using System.Net;

using System.Linq.Expressions;

using System.Text;

using System.Text.RegularExpressions;

using System.Web;

using Microsoft.Samples.DPE.ODataTFS.Model.Entities;

using Microsoft.Samples.DPE.ODataTFS.Model.ExpressionVisitors;

using System.Collections;

using Microsoft.Data.Services.Toolkit.QueryModel;

using Microsoft.TeamFoundation.WorkItemTracking;

public class TFSWorkItemFactProxy : TFSBaseProxy, ITfsWorkItemFactProxy

{

public TFSWorkItemFactProxy(Uri uri, ICredentials credentials)

: base(uri, credentials)

{

}

public WorkItem GetWorkItem(int workItemId)

{

var wiql = string.Format(CultureInfo.InvariantCulture, «SELECT [System.Id] FROM WorkItems WHERE [System.Id] = {0}», workItemId);

return this.QueryWorkItems(wiql)

.Cast<TeamFoundation.WorkItemTracking.Client.WorkItem>()

.Select(w => w.ToModel(this.GetTfsWebAccessArtifactUrl(w.Uri)))

.FirstOrDefault();

}

public IEnumerable<WorkItemFact> GetWorkItemFactByProject(string projectName, FilterNode rootFilterNode, ODataQueryOperation operation)

{     

var key = string.Format(CultureInfo.InvariantCulture, «TFSWorkItemFactProxy.GetWorkItemsFactByProject_{0}_{1}», projectName, this.GetFilterNodeKey(rootFilterNode));

HttpContext.Current.Items[key] = this.RequestWorkItemFactsByProject(projectName, rootFilterNode, operation);

return (IEnumerable<WorkItemFact>)HttpContext.Current.Items[key];

}

private static string BuildWiql(FilterNode rootFilterNode, ODataQueryOperation operation)

{

var constrains = string.Empty;

if (rootFilterNode != null)

{

foreach (var filterNode in rootFilterNode)

{

constrains += AddComparisonConstrainToWiql(filterNode, Constants.TFS.FieldLookup(filterNode.Key));

}

}

if (constrains.StartsWith(» OR «, StringComparison.OrdinalIgnoreCase))

{

constrains = constrains.Substring(» OR «.Length);

}

if (constrains.StartsWith(» AND «, StringComparison.OrdinalIgnoreCase))

{

constrains = constrains.Substring(» OR «.Length);

}

var wiql = string.Format(CultureInfo.InvariantCulture, «SELECT [System.Id] FROM WorkItems {0} {1}», string.IsNullOrEmpty(constrains) ? string.Empty : «WHERE», constrains).Trim();

string orderByWiql = string.Empty;

if (operation.OrderStack.Count > 0)

{

orderByWiql = TFSWorkItemFactProxy.GenerateOrderByWiql(operation.OrderStack);

}

wiql = wiql + » « + orderByWiql;

return wiql;

}

private static string GenerateOrderByWiql(Stack<ODataOrderExpression> orderExpressionStack)

{

if (orderExpressionStack == null)

{

throw new ArgumentNullException(«orderExpressionStack»);

}

StringBuilder finalOrderBy = new StringBuilder(«ORDER BY «);

//When the $top param is used with WCF Data Services Toolkit, this appears to also imply

//ordering by key ascending (in this case work item ID), so if the user also issues an

//explicit $orderby param for Id there will be a duplicate. It appears that the first

//$oderby value (top of stack) is the one provided explicitly by the user, so we take that

//one and track that it has been seen already. Subsequent order by fields are ignored.

ISet<string> seen = new HashSet<string>();

while (orderExpressionStack.Count > 0)

{

ODataOrderExpression expr = orderExpressionStack.Pop();

UnaryExpression unExpr = ((UnaryExpression)expr.Expression);

var opString = unExpr.Operand.ToString();

var name = opString.Substring(opString.IndexOf(‘.’) + 1);

name = Constants.TFS.FieldLookup(name);

if (!seen.Contains(name))

{

if (expr.OrderMethodName.StartsWith(«ThenBy»))

{

finalOrderBy.Append(» , «);

}

finalOrderBy.Append(name);

switch (expr.OrderMethodName)

{

case «OrderBy»:

case «ThenBy»:

finalOrderBy.Append(» asc»);

break;

case «OrderByDescending»:

case «ThenByDescending»:

finalOrderBy.Append(» desc»);

break;

}

seen.Add(name);

}

}

return finalOrderBy.ToString();

}

private static string AddComparisonConstrainToWiql(FilterNode filterNode, string tfsFieldName)

{

if (filterNode != null)

{

var sign = default(string);

switch (filterNode.Sign)

{

case FilterExpressionType.Equal:

sign = «=»;

break;

case FilterExpressionType.NotEqual:

sign = «<>»;

break;

case FilterExpressionType.GreaterThan:

sign = «>»;

break;

case FilterExpressionType.GreaterThanOrEqual:

sign = «>=»;

break;

case FilterExpressionType.LessThan:

sign = «<«;

break;

case FilterExpressionType.LessThanOrEqual:

sign = «<=»;

break;

case FilterExpressionType.Contains:

sign = filterNode.Key.Equals(«AreaPath», StringComparison.OrdinalIgnoreCase) || filterNode.Key.Equals(«IterationPath», StringComparison.OrdinalIgnoreCase) ? «UNDER» : «CONTAINS»;

break;

case FilterExpressionType.NotContains:

sign = filterNode.Key.Equals(«AreaPath», StringComparison.OrdinalIgnoreCase) || filterNode.Key.Equals(«IterationPath», StringComparison.OrdinalIgnoreCase) ? «NOT UNDER» : «NOT CONTAINS»;

break;

default:

throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, «WorkItem {0} can only be filtered with equal, not equal, greater than, lower than, greater than or equal, lower than or equal operators», filterNode.Key));

}

return string.Format(CultureInfo.InvariantCulture, » {0} {1} {2} ‘{3}’ «, filterNode.NodeRelationship.ToString(), tfsFieldName, sign, filterNode.Value);

}

return string.Empty;

}

private IEnumerable<WorkItemFact> RequestWorkItemFactsByProject(string projectName, FilterNode rootFilterNode, ODataQueryOperation operation)

{

FilterNode newFilterNodeStartWithProject = new FilterNode() {

Key = «Project», Sign = FilterExpressionType.Equal, Value = projectName

};

if (rootFilterNode != null)

{

newFilterNodeStartWithProject.AddNode(rootFilterNode);

}

var wiql = BuildWiql(newFilterNodeStartWithProject, operation);

return this.ExecuteWiqlRequest(wiql, operation);

}

private IEnumerable<WorkItemFact> ExecuteWiqlRequest(string wiql, ODataQueryOperation operation)

{

IEnumerable<WorkItemFact> retWorkItems = null;

if (!operation.IsCountRequest)

{

if (operation.TopCount == 0)

{

//workaround for bug (I think) in WCF Data Services Toolkit.

//It appears that ODataQueryOperation.TopCount will be 0 when $select param is used

//and $top param is not explicitly sent by client (normally TopCount would have

//whatever was set for entity page size in TFSService.InitializeService()

operation.TopCount = Constants.DefaultEntityPageSize;

}

IEnumerable<TeamFoundation.WorkItemTracking.Client.WorkItem> wilist = this.QueryWorkItems(wiql).Cast<TeamFoundation.WorkItemTracking.Client.WorkItem>() .Skip(operation.SkipCount).Take(operation.TopCount)

.Select(w => w).ToArray();

List<WorkItemFact> lst = new List<WorkItemFact>();

foreach (TeamFoundation.WorkItemTracking.Client.WorkItem w in wilist)

{

TeamFoundation.WorkItemTracking.Client.Revision prevRev=null;

// Get All work item revisions

foreach (TeamFoundation.WorkItemTracking.Client.Revision revision in w.Revisions)

{

if(prevRev!=null)

{

WorkItemFact wiFactPrev = new WorkItemFact();

wiFactPrev.Project = w.Project.Name;

wiFactPrev.Id = w.Id;

wiFactPrev.Revision = (int)prevRev.Fields[«System.Rev»].Value;

wiFactPrev.RevisionDate = System.Convert.ToDateTime(revision.Fields[«Changed Date»].Value).Date; // Take the date for the next revision

wiFactPrev.RevisionCount=null;

wiFactPrev.RecordCount=-1;

wiFactPrev.SurrogateKey = wiFactPrev.Id + «:» + wiFactPrev.Revision;

// add numeric values to the fact

foreach (TeamFoundation.WorkItemTracking.Client.Field field in prevRev.Fields)

{

switch (prevRev.Fields[field.Name].FieldDefinition.FieldType)

{

case TeamFoundation.WorkItemTracking.Client.FieldType.Integer:

wiFactPrev.SetField(field.Name, System.Convert.ToInt64(field.Value) * -1);

break;

case TeamFoundation.WorkItemTracking.Client.FieldType.Double:

wiFactPrev.SetField(field.Name, System.Convert.ToDouble(field.Value) * -1);

break;

}

}

lst.Add(wiFactPrev);

}

prevRev=revision;

WorkItemFact wiFact = new WorkItemFact();

wiFact.Project = w.Project.Name;

wiFact.Id = w.Id;

wiFact.Revision = (int)revision.Fields[«System.Rev»].Value;

wiFact.RevisionCount = 1;

wiFact.RecordCount = 1;

wiFact.RevisionDate = System.Convert.ToDateTime( revision.Fields[«Changed Date»].Value).Date;

wiFact.SurrogateKey = wiFact.Id + «:» + wiFact.Revision;

// add numeric values to the fact

foreach (TeamFoundation.WorkItemTracking.Client.Field field in revision.Fields)

{

switch (revision.Fields[field.Name].FieldDefinition.FieldType)

{

case TeamFoundation.WorkItemTracking.Client.FieldType.Integer:

wiFact.SetField(field.Name, System.Convert.ToInt64(field.Value) );

break;

case TeamFoundation.WorkItemTracking.Client.FieldType.Double:

wiFact.SetField(field.Name, System.Convert.ToDouble(field.Value) );

break;

}

}

lst.Add(wiFact);

}

}

retWorkItems = lst;

}

else

{

var workItemServer = this.TfsConnection .GetService<TeamFoundation.WorkItemTracking .Client.WorkItemStore>();

try

{

Microsoft.TeamFoundation.WorkItemTracking.Client.Query q =

new TeamFoundation.WorkItemTracking.Client.Query(workItemServer, wiql, null, false);

int cnt = q.RunCountQuery();

List<WorkItemFact> wiBlanks = new List<WorkItemFact>(cnt);

WorkItemFact blank = new WorkItemFact();

for (int i = 0; i < cnt; i++)

{

wiBlanks.Add(blank);

}

retWorkItems = wiBlanks;

}

catch (Microsoft.TeamFoundation.WorkItemTracking.Client.ValidationException ex)

{

throw new System.Data.Services.DataServiceException(500, «Internal Server Error», ex.Message, «en-US», ex);

}

}

return retWorkItems;

}

}

}

WorkItemFactRepository

Code 5 – WorkItemFactRepositories

// ———————————————————————————-

// Microsoft Developer & Platform Evangelism

//

// Copyright (c) Microsoft Corporation. All rights reserved.

//

// THIS CODE AND INFORMATION ARE PROVIDED «AS IS» WITHOUT WARRANTY OF ANY KIND,

// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES

// OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

// ———————————————————————————-

// The example companies, organizations, products, domain names,

// e-mail addresses, logos, people, places, and events depicted

// herein are fictitious. No association with any real company,

// organization, product, domain name, email address, logo, person,

// places, or events is intended or should be inferred.

// ———————————————————————————-

namespace Microsoft.Samples.DPE.ODataTFS.Model.Repositories

{

using System;

using System.Collections;

using System.Collections.Generic;

using System.Data.Services;

using System.Globalization;

using System.Linq;

using Microsoft.Data.Services.Toolkit.QueryModel;

using Microsoft.Samples.DPE.ODataTFS.Model.Entities;

using Microsoft.Samples.DPE.ODataTFS.Model.ExpressionVisitors;

using Microsoft.Samples.DPE.ODataTFS.Model.Serialization;

public class WorkItemFactRepository : IRepository<WorkItemFact>

{

private readonly ITfsWorkItemFactProxy proxy;

public WorkItemFactRepository(ITfsWorkItemFactProxy proxy)

{

this.proxy = proxy;

}

public IEnumerable<WorkItemFact> GetAll()

{

List<WorkItemFact> lst = new List<WorkItemFact>();

return lst;

}

public WorkItemFact GetOne(string id)

{

return null;

}

[RepositoryBehavior(HandlesFilter = true, HandlesSkip = true, HandlesTop = true, HandlesOrderBy = true)]

public IEnumerable<WorkItemFact> GetWorkItemFactsByProject(ODataSelectManyQueryOperation operation)

{

if (operation == null)

{

throw new ArgumentNullException(«operation»);

}

var parameters = new WorkItemFilterExpressionVisitor(operation.FilterExpression).Eval();

return this.proxy.GetWorkItemFactByProject(operation.Key, parameters, operation);

}

}

}

Настройка альтернативных учетных данных для VS Online

Процедура настройки альтернативных учетных данных описана на странице http://tfodata.visualstudio.com и является необходимым условием для данного руководства.

Шаг Инструкции
1. Опционально – Включить и настроить базовые учетные данные для tfs.visualstudio.com
[ ] — Сделано
  • Перейдите к учетной записи, которую вы хотите использовать на https://tfs.visualstudio.com. Например, вы можете иметь https://account.visualstudio.com.
  • В правом верхнем углу нажмите на имени вашей учетной записи и затем выберите My Profile.
  • Выберите вкладку Credentials.
  • Нажмите ссылку Enable alternate credentials and set password,
  • Введите пароль. Предполагается, что вы выберите уникальный пароль здесь (не связанный с другими учетными записями),
  • Нажмите кнопку Save Changes.
2. Настроить аутентификацию через сервисы OData
[ ] — Сделано
  • Для проверки подлинности через службы OData, вам нужно отправить ваши учетные данные для базовой аутентификации в формате домен\имя_пользователя и пароль:
    • домен\имя_пользователя
    • пароль
  • учетная запись из account.visualstudio.com, имя пользователя на вкладке Credentials в разделе My Profile, и пароль – это пароль, созданный на шаге 1.

<<Содержание

Posted in TFS Practical Reporting Guide | Отмечено: , , | Leave a Comment »

Практическое руководство по отчетности TFS. Настройка отчетов для трендов

Posted by Шамрай Александр на Январь 15, 2014

<<Содержание

Повременная отчетность необходима для усовершенствования процесса. Незнание влияния изменений процесса или знание того, что грядет, основано на том, что происходило в прошлом, нет другого пути узнать, являются ли изменения положительными или отрицательными. Подход в отчетности для тенденции в удаленной службе отличается от подхода для отчетов на момент времени, приведенных ранее.

Примечание

Чтобы создавать отчеты для тенденции, канал OData должен был быть дополнен, поскольку отсутствуют необходимые данные в существующих Odata. Отчеты на момент времени, описанные ранее, могут быть реализованы с существующими каналами. Для реализации этого решения, необходимо создать расширенный канал Odata как описано на этой странице.

Основные сведения о данных, подготовленных расширенным каналом OData

Предупреждение

Если в системе нет данных, расширенный канал OData ничего не вернет, даже схему, поэтому обновление не удастся.

Расширенный канал OData предоставляет два новых канала, необходимых для отчетов по тенденции (и которые могут использоваться отчеты на один момент времени с использованием более сложных функций, чем доступно в оригинальном решении). Первый канал – это канал Work Item History, а второй – Work Item Facts, и оба они используются вместе, чтобы обесечить возможность предоставить отчет по тенденции. Рисунок 2 показывает результирующую модель PowerPivot с использованием необходимых каналов и таблицы дат.

Рисунок 2 – Модель PowerPivot для отчетов с тенденцией

Таблица Date находится в Excel файле в решении с примером и описана более подробно ниже.

Канал Work Item History

Канал Work Item History представляет собой дамп истории всех рабочих элементов в данной коллекции. Он определяет момент времени, в котором рабочий элемент был в конкретном состоянии, присвоенным конкретному пользователю и различные другие общие атрибуты, такие как пути итерации и области, заблокирован или нет, ранг стека и другие данные.

Примечание

В модели, показанной на рисунке 2, поток WorkItemHistory был переименован в «Work Items»

Для этого для канала данных, как только он был передан в модель PowerPivot, требуется только одно изменение – к каналу должен быть добавлен альтернативный (или суррогатный) ключ. Этот столбец является простым вычисляемым столбцом. Чтобы добавить его, сделайте следующее:

  1. Щелкните первую строку пустого столбца в конце таблицы.
  2. Введите формулу как: =[Id] & «:» & [Revision] и нажмите ENTER.
  3. Переименуйте столбец ВычисляемыйСтолбец1 в AlternateKey.

Канал Work Item Fact

Этот канал является более интересным и позволяет пользователю выполнить вычисления простым, но более мощный методом. Для тех, кто не знаком с хранилищем данных, ниже описаны критические столбцы, а также обоснование для создания этих столбцов и связи с таблицей Work Item History.

После импорта канала таблица была переименована в «Work Item Measures», которая является немного более понятной конечному пользователю, чем таблица Work Item Fact. Кроме того, каждый столбец в этой таблице скрыт от клиентских средств, так как пользователи должны его использовать только для обеспечения мер. Все другие основные данные рабочего элемента выходят из таблицы Work Items.

В этой таблице есть один добавленный столбец – DateSK. Этот столбец обеспечивает полосу времени для столбца Revision Date, т.к. мера детализации ограничена выбранным днем, а не одним часом или минутой в течение дня. Более детализированный отчет возможен, но необходимо существенно расширить таблицу дат, чтобы включить в список каждый час, каждый день и каждый год. Причина именования этого столбца DateSK проста – первичным ключом из таблицы «Date» является DateSK и соединение происходит через этот столбец.

Колонка/Мера Описание
RevisionCount Этот столбец содержит одно значение 1 – для подсчета каждой версии, показанной в столбце Revision. Вы заметите, что каждая версия дублируется (см. объяснение RecordCount).
RecordCount Record Count — это количество рабочих элементов, которые существуют на данный день для целей тенденций в отчетах. Каждая версия рабочего элемента будет иметь одну или две строки в этой таблице. Первая строка покажет RecordCount как «1». Вторая строка (если есть второй ряд) покажет «-1». Почему? Рассмотрим следующую ситуацию:

  • Пользователь добавляет рабочий элемент 1/10/2013, который имеет версию 1 и ID 560
  • Другой пользователь делает изменение этого рабочего элемента 5/10/2013, который имеет версию 2
  • Следующий пользователь создает версию 8/10/2013, которая имеет номер 3

Этот сценарий будет отображаться в этой таблице со следующей структурой:

ID RevisionDate Revision RecordCount
560 10/1/2013 1 1
560 10/5/2013 1 -1
560 10/5/2013 2 1
560 10/8/2013 2 -1
560 10/8/2013 3 1

Это позволяет модели PowerPivot выполнить необходимые вычисления для промежуточных дат правильно для отчетов по тенденции. Когда создается куб, и когда конечный пользователь добавляет дату в свою сводную таблицу, строки для этой записи показывают, в какие даты рабочий элемент 560 существует в промежуток с 1/10/2013 до 10/8/2013 (скажем, что это происходит 8-го числа).

Теперь в модели эта таблица связана с таблицей Work Items и все остальные связанные данные могут быть получены из этой таблицы. Давайте сделаем еще одно допущение для этого сценария, например, что состояния для данного рабочего элемента следующие:

ID

RevisionDate Revision State

560

10/1/2013 1 To Do

560

10/5/2013 2 In Work

560

10/8/2013 3 Done

Эти таблицы соединяются через альтернативный ключ, которым является ID:Revision.

Теперь сводная таблица отображает дату в строке, состояние в колонке и количество рабочих элементов как мера (с фильтрацией по этому рабочему элементу). Она будет выглядеть следующим образом:

State
Date To Do In Work Done
10/1/2013 1
10/2/2013 1
10/3/2013 1
10/4/2013 1
10/5/2013 1
10/6/2013 1
10/7/2013 1
10/8/2013 1

Тут все правильно сгенерировано для значения RecordCount. Куб говорит, что между 1/10/2013 и 4/10/2013, количество записей для данного рабочего элемента 1. Однако, 5/10/2013 версия изменилась. Если бы там не было -1, когда бы выполнялся расчет количества рабочих элементов, было бы количество 2.

Однако это не так – существует только один рабочий элемент с идентификатором 560. Он находится в другом состоянии. Поэтому эта структура говорит нам, что нет рабочего элемента с идентификатором 560 на дату 10/5 с состоянием «To Do», но рабочий элемент с идентификатором 560 есть 10/5 в состоянии «In Work». Что будет, если конечный пользователь решит не включать состояния в сводную таблицу? Она станет простым списком дат для каждой даты, с «1» рядом с ней, т.к. в каждый день, сумма поля RecordCount всегда равна единице.

Примечание

Эта логика применяется для всех других полей мер, таких как поля завершено и оставшиеся трудозатраты.

Смотрите ниже меру Work Item Count, чтобы понять работу расчета, который этой делает.

Work Item Count Расчет для количества следующий: CALCULATE(sum([RecordCount]), DATESBETWEEN (‘Date'[DateSK], BLANK(), LASTDATE(‘Date'[DateSK])), ALL(‘Date’))По сути, это сумма в столбце RecordCount, но, используя функцию DAX CALCULATE, она предоставляет контекст для функции Sum. Если пользователь изменяет область даты, количество будет правильно обновлено. Без функции CALCULATE количество записей будет оставаться статическим.

Расчет сам по себе очень быстр, потому что он просто добавляет в общей сложности «1» и «-1» для каждой строки в таблице. Для любого выбранного рабочего элемента итог всегда равен 1. Для выбранного набора рабочих элементов итог всегда представляет собой совокупность рабочих элементов, которые были в данной версии в выбранный период времени. Используя пример выше, если пользователь изменил диапазон дат в сводной таблице до 10/7, то таблица никогда не будет отображать столбец «Готово», потому что на основе данного диапазона, Рабочий элемент не имеет записей до даты 10/7.

Remaining Work Расчет для работы: CALCULATE(Sum([RemainingWork]), DATESBETWEEN(‘Date'[DateSK], BLANK(), LASTDATE(‘Date'[DateSK])), ALL(‘Date’))Тут идентично Work Item Count. Таблица Fact сформирована так, чтобы обеспечивался единый метод для расчета мер.
Revision Count CALCULATE(Sum([RevisionCount]), DATESBETWEEN(‘Date'[DateSK], BLANK(), LASTDATE(‘Date'[DateSK])), ALL(‘Date’))
Total Effort CALCULATE(Sum([Effort]), DATESBETWEEN(‘Date'[DateSK], BLANK(), LASTDATE(‘Date'[DateSK])), ALL(‘Date’))

Таблица 12 – Канал Work Item Fact

Таблица Date

Таблица Date обеспечивает диапазон дат, для которых может быть рассчитан куб. Это непрерывный список дат от даты начала до сегодняшнего дня (хотя он может быть продлен настолько далеко, насколько это необходимо). Это позволяет кубу заполнить значения, для которых не существует конкретных дат. Это достигается за счет самих мер, которые ссылаются на таблицу дат, и тот факт, что таблица Work Item Measures связана с таблицей дат.

Таблица Date не предоставляется через канал – она должна предоставляться вам через альтернативный механизм. В стандартном механизме отчетности TFS хранилище данных содержит таблицу дат, сформированную с использованием кода. Т.к. в удаленной службе нет хранилища, мы предоставили шаблон решения со скрытой вкладкой Date (чтобы показать ее, нужно выбрать любую вкладку, нажать Показать и выбрать вкладку Date). Данные из этой вкладки используются для создания связанной таблицы в PowerPivot.

Для обеспечения согласованности с локальным хранилищем структура этого листа идентична таблице дат хранилища данных даже несмотря на то, что не все столбцы необходимы. Расширить данные (диапазон дат идет только до даты публикации в проекте для примера), просто выделите все ячейки пяти последних строк и протяните вниз. Это можно сделать также с помощью кода VBA, а также загрузкой в таблицу SQL и планированием задания для обновления таблицы один раз в день.

В таблице дат созданы две иерархии. Эти иерархии соответствуют иерархии даты в стандартном кубе TFS. Это иерархии год – месяц – дата и год – неделя – дата, и они все доступны вам через выпадающий список для этих структур времени.

Таблица Projects

Примечание

Смотрите файл ALMRangersTrendDataReferenceSolution.xlsx, которая содержит рабочую книгу для примера, выполненную по этому руководству

Отчет тенденции похож на отчет на момент времени за исключением того, что строка обычно разделена по дате для создания диаграмм, которые выглядят так:

Рисунок 3 – Пример Тренда

Пример на рисунке 2 — Стандартная накопительная диаграмма (Cumulative Flow Diagram, CFD) для задач проекта Рейнджеров ALM с момента его создания 16 августа 2012. Для создания отчета похожего на этот выполните следующие шаги:

Шаг Описание
1. Выбрать PowerPivot
[ ] – Сделано
  • Выберите PowerPivot, потом Управление из меню для Excel проекта с примером

2. Выбрать лист
[ ] – Сделано
  • В окне PowerPivot на вкладке В начало выберите PivotTable.

  • Выберите Новый лист (или Использовать существующий и выберите начальную область – обычной A1) и нажмите ОК.
  • Это отобразит список Поля PivotTable в правой области окна Excel.

Примечание

Таблица дат отображается как таблица 1 вместо Date, но если раскрыть ее будут отображены правильные данные. Пока нет метода, чтобы исправить это.
3. Определить меры
[ ] – Сделано
  • Раскройте секцию Work Item Measures и выберите меру Work Item Count.
  • Это добавит меру Work Item Count в раздел Значения на панель Поля и покажет количество рабочих элементов в сводной таблице.
4. Определить измерения
[ ] – Сделано
  • Раскройте измерение Work Items и перетащите поле Type на панель Фильтры панели Поля
  • В ячейке B2 измените значение фильтра Type из Все на Task.
  • Раскройте измерения Table1 и папку Больше полей и перетащите поле Date в раздел Строки из панели Поля.
  • Далее под Work Items перетащите поле State в раздел Столбцы из панели Поля
  • Теперь, и предполагая, что вы используете ваши данные, может потребоваться изменить порядок столбцов State и/или удалить столбцы. Чтобы удалить состояние, щелкните раскрывающийся список рядом с Название колонки (ячейка B3, если все идет по текущему примеру) и снимите флажок рядом с состоянием, чтобы удалить (или добавить). Чтобы изменить порядок столбцов, просто схватите заголовок столбца (например, ячейка B4) и перетащите его в нужное место. В диаграмме CFD, созданной с помощью Excel, минимальное значение начинается слева и максимальное значение находится справа с точки зрения порядка столбцов.
  • Раздел Поля PivotTable должен выглядеть примерно так:

5. Создать диаграмму
[ ] – Сделано
  • Наконец, для создания диаграммы, выберите на ленте Анализ, выберите Сводная диаграмма и выберите Область с накоплением.
  • Эти данные можно заменить на любой другой тип данных для необходимой разбивки. Даты также можно использоваться для ограничения диапазона, а не использовать в качестве строк с датами.

Таблица 13 – Шаги: Пример отчета с трендом

Пример отчета с использованием руководства для трендов

Рисунок 4 – Пример, показывающий тренд по датам

Рисунок 5 – Пример, показывающий количество рабочих элементов для выбранного спринта

Рисунок 6 – Пример, показывающий задачи, назначенные на участников команды, для выбранного спринта и команды

Книга является вашей устрицей J со срезами, форматами и отчетами с тенденцией данных, которые необходимы для вашей команды.

<<Содержание

Posted in Microsoft, Team Foundation Server, TFS Practical Reporting Guide, Visual Studio | Отмечено: , , | Leave a Comment »

Практическое руководство по отчетности TFS. Настройка OData для трендов

Posted by Шамрай Александр на Январь 14, 2014

<<Содержание

Примечание

Вы можете скачать последнюю версию решения OData, опубликованного Брайаном Келлером, которое можно загрузить здесь. Архив eBook1-Package.zip содержит оригинальную версию OData v2.3 и дополненную версию OData, которая описана в этом документе.

Зачем расширять проект TFS OData?

Существует несколько причин, по которым может потребоваться расширить канал TFS OData, например:

  • Настраиваемые поля рабочих элементов
    Проект TFS OData возвращает только базовый набор полей. Для доступа к вашим настраиваемым полям добавьте их, расширив проект OData.
  • Недостающие данные
    Проект TFS OData предоставляет доступ ко многому, но не все данные обрабатываются в TFS. Если нужно получить доступ к элементу, например, WorkItemHistory или данным связанным с тестами, это можно реализовать как расширение OData.
  • Реализация параметров запросов
    Проект TFS OData поддерживает только ограниченный набор параметров запроса, например, $filter. Для реализации специфического параметра для запроса, например, $expand, нужно расширить проект OData.

В этом руководстве мы добавим канал для всех версий для всех рабочих элементов в командном проекте. Это очень полезно, если вы планируете делать отчеты по тенденции или историческим данным, что мы и планируем сделать в рамках нашего решения.

Выполнение настройки

Примечание

Вы можете найти примеры полного кода в приложении и как часть решения для примера.

Выбор URI

Для нашего решения нам нужно получить доступ ко всем версиям всех рабочих элементов в командном проекте. Мы также должны получить доступ к предварительно рассчитанной таблице Fact для версий рабочего элемента. Для этой цели мы выбрали следующие URI:

Они обеспечивают прямой доступ к WorkItemHistory и предварительно рассчитанной таблице WorkItemFact для выбранного командного проекта.

Добавление к проекту WorkItemHistory и WorkItemFacts

При выполнении запроса проект OData создает экземпляр сущности Project и ищет член WorkItemHistory для возврата. То, что нам нужно сделать, это в дальнейшем добавить этот член в сущность Project.

Данные, которые мы хотим вернуть, — это плоский список всех версий.

  • Перейдите к проекту TFS.OData.Model, откройте папку Entities и откройте файл Project.cs.
  • В класс Project добавьте следующие члены.

    [ForeignProperty]

    public
    IEnumerable<WorkItemRevision> WorkItemHistory { get;
    set; }

    public
    IEnumerable<WorkItemFact> WorkItemFacts { get;
    set; }

Возврат списка WorkItemRevisions и WorkItemFacts

Теперь при выполнении запроса OData будет искать список WorkItemRevisions, нам нужно сделать так, чтобы проект знал о существовании WorkItemRevisions.

  • Перейдите к папке Entities проекта TFS.OData.Model и добавьте новый класс WorkItemRevision. Пример реализации в приложении.
  • Откройте файл TFSData.cs в корневом каталоге проекта TFS.OData.Model и добавьте следующие члены к классу TFSData.

    public
    IQueryable<WorkItemRevision> WorkItemHistory

    {

    get { return CreateQuery<WorkItemRevision>(); }

    }

    public
    IQueryable<WorkItemFact> WorkItemFact

    {

    get { return CreateQuery<WorkItemFact>(); }

    }

  • Вставьте следующий код в метод public override object RepositoryFor(string fullTypeName) класса TFSDAta

    if (fullTypeName == typeof(WorkItemRevision).FullName || fullTypeName == typeof(WorkItemRevision[]).FullName)

    {

    return new WorkItemHistoryRepository(this.tfsProxyFactory.TfsWorkItemHistoryProxy);

    }

    if (fullTypeName == typeof(WorkItemFact).FullName || fullTypeName == typeof(WorkItemFact[]).FullName)

    {

    return new WorkItemFactRepository(this.tfsProxyFactory.TfsWorkItemFactProxy);

    }

  • Откройте файл TfsProxyFactor.cs в папке Serializations проекта TFS.OData.Model и добавьте следующие члены для класса TFSProxyFactory.

    public
    ITfsWorkItemHistoryProxy TfsWorkItemHistoryProxy

    {

    get { return new TFSWorkItemHistoryProxy(this.tfsUri, this.tfsCredentials); }

    }

    public
    ITfsWorkItemFactProxy TfsWorkItemFactProxy

    {

    get { return new TFSWorkItemFactProxy(this.tfsUri, this.tfsCredentials); }

    }

Извлечение WorkItemRevisions

Теперь нам нужно реализовать логику для извлечения WorkItemRevisions.

  • Добавьте следующий интерфейс (желательно поместить его в папку Serializations):

    public
    interface ITfsWorkItemHistoryProxy

    {

    IEnumerable<WorkItemRevision> GetWorkItemHistoryByProject(string projectName, FilterNode rootFilterNode, ODataQueryOperation operation);

    }

  • Добавьте класс TFSWorkItemHistoryProxy в папке Repositories для реализации отношения к классу TFSWorkItemProxy.
  • Добавьте код к методу ExecuteWIQLRequest, который проходит по результату запроса и создает новые экземпляры WorkItemRevision для каждой версии рабочего элемента.

    List<WorkItem> wilist = this.QueryWorkItems(wiql).Cast<WorkItem>() .Skip(operation.SkipCount).Take(operation.TopCount) .Select(w => w).ToArray();

    List<WorkItemRevision> lst = new List<WorkItemRevision>();

    foreach (WorkItem w in wilist) {

    // Get All work item revisions

    foreach (Revision revision in w.Revisions) {

    WorkItemRevision wrkRev = new WorkItemRevision();

    wrkRev.Set(w.ToModel(this.GetTfsWebAccessArtifactUrl(w.Uri)));

    wrkRev.Revision = revision.Index;

    // Get value of fields in the work item revision

    foreach (Field field in w.Fields) {

    wrkRev.SetField(field.Name, revision.Fields[field.Name].Value);

    }

    lst.Add(wrkRev);

    }

    }

Извлечение WorkItemFacts

Теперь нам нужно реализовать логику для извлечения WorkItemFacts.

  • Добавьте следующий интерфейс (желательно поместить его в папку Serializations):

    public
    interface ITfsWorkItemFactProxy

    {

    IEnumerable<WorkItemFact> GetWorkItemFactByProject(string projectName, FilterNode rootFilterNode, ODataQueryOperation operation );

    }

  • Добавьте класс TFSWorkItemFactProxy в папке Repositories для реализации отношения к классу TFSWorkItemProxy.
  • Добавьте код к методу ExecuteWIQLRequest, который пройдет по результату запроса и создаст два новых экземпляра WorkItemFact для каждой версии рабочего элемента, за исключением последней.

    IEnumerable<TeamFoundation.WorkItemTracking.Client.WorkItem> wilist = this.QueryWorkItems(wiql)

    .Cast<TeamFoundation.WorkItemTracking.Client.WorkItem>()

    .Skip(operation.SkipCount).Take(operation.TopCount)

    .Select(w => w).ToArray();

    List<WorkItemFact> lst = new List<WorkItemFact>();

    foreach (TeamFoundation.WorkItemTracking.Client.WorkItem w in wilist)

    {

    TeamFoundation.WorkItemTracking.Client.Revision prevRev=null;

    // Get All work item revisions

    foreach (TeamFoundation.WorkItemTracking.Client.Revision revision in w.Revisions)

    {

    if(prevRev!=null)

    {

    WorkItemFact wiFactPrev = new WorkItemFact();

    wiFactPrev.Project = w.Project.Name;

    wiFactPrev.Id = w.Id;

    wiFactPrev.Revision = (int)prevRev.Fields[«System.Rev»].Value;

    wiFactPrev.RevisionDate = System.Convert.ToDateTime(revision.Fields[ «Changed Date»].Value).Date;

    // Take the date for the next revision

    wiFactPrev.RevisionCount=null;

    wiFactPrev.RecordCount=-1;

    wiFactPrev.SurrogateKey = wiFactPrev.Id + «:» + wiFactPrev.Revision;

    // add numeric values to the fact

    foreach (TeamFoundation.WorkItemTracking.Client.Field field in prevRev.Fields)

    {

    switch (prevRev.Fields[field.Name].FieldDefinition.FieldType)

    {

    case
    TeamFoundation.WorkItemTracking.Client.FieldType.Integer:

    wiFactPrev.SetField(field.Name, System.Convert.ToInt64(field.Value) * -1);

    break;

    case
    TeamFoundation.WorkItemTracking.Client.FieldType.Double:

    wiFactPrev.SetField(field.Name, System.Convert.ToDouble(field.Value) * -1);

    break;

    }

    }

    lst.Add(wiFactPrev);

    }

    prevRev=revision;

    WorkItemFact wiFact = new WorkItemFact();

    wiFact.Project = w.Project.Name;

    wiFact.Id = w.Id;

    wiFact.Revision = (int)revision.Fields[«System.Rev»].Value;

    wiFact.RevisionCount = 1;

    wiFact.RecordCount = 1;

    wiFact.RevisionDate = System.Convert.ToDateTime(revision.Fields[ «Changed Date»].Value).Date;

    wiFact.SurrogateKey = wiFact.Id + «:» + wiFact.Revision;

    // add numeric values to the fact

    foreach (TeamFoundation.WorkItemTracking.Client.Field field in revision.Fields)

    {

    switch (revision.Fields[field.Name].FieldDefinition.FieldType)

    {

    case
    TeamFoundation.WorkItemTracking.Client.FieldType.Integer:

    wiFact.SetField(field.Name, System.Convert.ToInt64(field.Value) );

    break;

    case
    TeamFoundation.WorkItemTracking.Client.FieldType.Double:

    wiFact.SetField(field.Name, System.Convert.ToDouble(field.Value) );

    break;

    }

    }

    lst.Add(wiFact);

    }

    }

    retWorkItems = lst;

Соединяем

Для объединения нам нужно реализовать некоторые соединения OData.

  • Добавьте класс WorkItemRevisionRepositories в папку Repositories реализации IRepository <WorkItemRevision>
  • Добавьте класс WorkItemFactRepositories в папку Repositories реализации IRepository <WorkItemFact>

Нам также необходимо задать правила доступа для наших новых WorkItemRevision и WorkItemFacts. Это выполняется редактированием метода void InitializeService(DataServiceConfiguration config) класса TFSService, который находится в файле TFSService.cs в корневой папке проекта ODataTFS.Web.

Добавьте следующий код:

config.SetEntitySetAccessRule («WorkItemHistory», EntitySetRights.AllRead);

config.SetEntitySetAccessRule («WorkItemFact», EntitySetRights.AllRead);

config.SetEntitySetPageSize («WorkItemHistory», Constants.DefaultEntityPageSize);

config.SetEntitySetPageSize («WorkItemFact», Constants.DefaultEntityPageSize);

Сборка и развертывание

Соберите и установите службы, как описано в разделе Настройка TfsOData.

Тестирование расширения

Теперь вы можете выполнить запрос для получения WorkItemHistory, откройте браузер и перейдите к

http://localhost/DefaultCollection/Projects(‘projectName’)/WorkItemHistory

Примечание

Это только отрывок из всего кода. Свяжитесь с ALM Рейнджерами или Брайаном Келлером, чтобы получить весь код.

Шаги развертывания для настроенного OData Services на IIS

Примечание

Это пошаговое руководство было проверено на Windows Server 2008 R2 Standard SP1, Windows Server 2012 Standard и в Windows Server 2012 R2. Вам может потребоваться адаптировать его в соответствии с вашей средой, если есть какие-либо отличия.
Шаг Описание
1. Перестроить OData для сервера[ ] — Сделано
  • Откройте измененное решение ODataTFS.sln
  • Выберите Build | Rebuild Solution, чтобы удостовериться, что все будет обновлено.
2. Опубликовать web-проект OData на диск[ ] — Сделано
  • Щелкните правой кнопкой мыши на проекте ODataTFS.Web в обозревателе решений и выберите Publish.
  • Вы можете использовать любой метод публикации, который вы хотите, но данная инструкции основывается на методе публикации в файловой системе.

3. Скопировать опубликованный веб-сайт на сервер[ ] — Сделано
  • Скопировать директорий в c:\Inetpub\wwwroot\TfsOdataAlmRangers.
Примечание Если вы выполнили шаги в разделе Настройка TFsOData, пропустите следующие шаги.
  • В IIS менеджере создайте новый веб-сайт.
  • Назовите его tfsodata и свяжите с новым созданным каталогом
  • Убедитесь, что вы используете HTTPS и SSL-сертификат для привязки.
  • Предпочтительно использовать физический путь c:\Inetpub\wwwroot\TfsOdataAlmRangers, вместо c:\odata.

Примечание

Для тестирования, можно использовать самозаверяющийся сертификат. Один из способов сделать это, с использованием IIS Manager перейти в узел машины, открыть Server Certificates и выбрать Create Self-Signed Certificate. Чтобы узнать больше о SSL и IIS, смотрите http://learn.iis.net/page.aspx/144/how-to-set-up-ssl-on-iis-7/
4. Импортировать сертификат[ ] — Сделано
  • Перейдите по адресу https://localhost:433/ и вы получите следующее предупреждение

  • Нажмите на ссылку Continue to this website. Поле URL станет красным и отобразит ошибку сертификата

  • Нажмите на ошибку, выберите Показать сертификат и импортируйте сертификат.
5. Добавить сертификат[ ] — Сделано
  • С этого момента сервисы OData готовы к использованию.

Таблица 11. Руководство: Развертывание служб OData на сервере IIS

<<Содержание

Posted in TFS Practical Reporting Guide | Отмечено: , , | Leave a Comment »

Практическое руководство по отчетности TFS. Отчеты на основе PowerPivot

Posted by Шамрай Александр на Январь 13, 2014

<<Содержание

Удаленно размещённые службы TFS не предоставляют из коробки механизмов отчетов на основе данных. Поэтому ALM Рейнджеры решили обеспечить по крайней мере базовый уровень возможностей для отчетности на основе различных блоков информации. Для достижения этих целей решение, которое приводится для примера, основывается на веб-канале OData, который надстраивается над Удаленно Размещенным TFS (подробную информацию о канале OData, см. в этой ссылке здесь).

Целью этого решения является дать возможность командам извлечь данные из удаленно размещенных служб TFS, чтобы они могли создавать свои собственные отчеты, используя знакомый интерфейс Microsoft Excel. Текущее решение использует Microsoft PowerPivot для обеспечения такой возможности, которое затем позволяет пользователям создавать Сводные Диаграммы или Power View на рабочем столе или с помощью Microsoft Excel 2013.

Важная информация для подготовки к этому руководству

Примечание

См. файл ALMRangersReferenceSolution.xlsx, который является примером, сформированным на основе этого пошагового руководства.

Мы используем @МАРКЕРЫ@ вместо реальных данных, чтобы защитить IP и сделать более гибким это руководство. Прежде чем продолжить, пожалуйста, выделите несколько минут, чтобы собрать и записать необходимую информацию, а потом заменить @МАРКЕРЫ@ значениями.

Информация Пример среды ALM Рейнджеров МАРКЕР используемый в руководстве Ваше значение
URL локальных служб almrangers.visualstudio.com @SERVICE@
Коллекция командных проектов DefaultCollection @TPC@
Пользователь для подключения к @SERVICE@ @USER@
Пароль пользователя для подключения к @SERVICE@ @PASSWORD@
Путь области для построения отчета VisualStudio.ALM @AREAPATH@
Командный проект для построения отчета VisualStudio.ALM @TP@
Команда на основе которой необходимо построить отчет, основан на пути области VisualStudio.ALM\vsarUnitTestFx @TEAM@
Имя электронной таблицы AlmRangersReferenceSolution.xlsx @FILE@

Таблица 4. Информация, необходимая для решения

Ограничения решения

В настоящее время канал OData имеет следующие ограничения:

  • Не представляет большинство данных, содержащихся в TFS.
  • Следующее не доступно для отчетности:
    • тренды данных для таблицы рабочих элементов
    • имена команд, что делает необходимым соответствие путей области и итераций с командами.

Обзор решения

В целом решение включает в себя таблицы Work Items, Projects и Builds, как показано на рисунке 1.

Рисунок 1 – Модель решения

Каждое измерение соединяется через имя проекта. Измерения Work Items и Builds содержат меры по предоставлению информации о текущем состоянии информации.

Измерение Work Items

Этот измерение включает в себя всю информацию о текущем состоянии рабочих элементов и позволяет вам выполнять агрегирование работы. Общими случаями использования можно представить сбор отчетности количества работы назначенной пользователю, количество назначенных рабочих элементов на каждого, количество оставшейся работы в итерации и т.д. В этом измерении доступны следующие меры:

  • Количество рабочих элементов
  • Количество версий рабочих элементов
  • Выполненная работа
  • Оставшаяся работа

Команда на основе имеющихся данных может добавить любое количество мер, но это скорее всего будут четыре базовые меры, которые ей необходимы.

Измерение Builds

Измерение Builds включает в себя все сведения о завершенных сборках. Это стандартная информация из потока OData с одним исключением -добавлен столбец, который вычисляет продолжительность сборки для того, чтобы продемонстрировать как добавлять дополнительные столбцы. В этом измерении доступны следующие меры:

  • Количество сборок
  • Общая продолжительности сборки

Измерение Projects

Т.к. это решение предназначено для работы с удаленной службой, для команды все ограничивается одной коллекцией. Каждая учетная запись (например, accountname.visualstudio.com) состоит из коллекции с названием «DefaultCollection». Поэтому это измерение содержит имена проектов в рамках коллекции по умолчанию, что представляет собой список всех командных проектов.

Создание собственного решения

Предоставленное решение является основой для разработки ваших собственных отчетов, но кроме этого в удаленной службе доступна и другая информация. Страница канала OData, расположенная здесь, содержит информацию о других данных, которые доступны для пользователя. Решения PowerPivot и Табличная Модель будут в будущем являться основой для отчетности для Team Foundation Server, потому что они обеспечивают гибкость для добавления мер и дополнительных полей без необходимости разработки адаптеров хранилища данных. Кроме этого, решение позволяет пользователям извлечь данные, которые содержат часы и минуты вместо только дней, что означает, что пользователи могут создавать отчеты в гранулированном виде, как это и ожидаемо.

Поэтому этот раздел описывает создание модели PowerPivot из канала OData. Это даст пользователям возможность создавать собственные решения на основе доступных каналов, но это транслируется в возможность вытащить и использовать данные PowerPivot с любым источником данных.

Подключение к каналу OData TFS

Начнем с того, что данные должны быть помещены в PowerPivot для построения модели данных. Перед выполнением этого пошагового руководства не забудьте прочитать инструкции здесь. Уделите особое внимание разделу с названием «Team Foundation Service Authentication» и настройте проверку подлинности, как там описано. После этого вы будете готовы начать.

Шаг Инструкции
1. Начало
[ ] — Сделано
  • Запустить Microsoft Excel и выбрать пустую книгу
2. Конфигурирование OData
[ ] — Сделано
  • Выберите на ленте вкладку PowerPivot и нажмите кнопку Управление.
  • Нажмите кнопку Из других источников и затем Другие каналы.
  • В диалоговом окне Мастер импорта таблиц, введите понятное имя, например, Builds.
  • На странице каналов TFS OData вы можете увидеть URL следующий за наименованием Builds.
  • Для примера используется URL-адрес https://tfsodata.visualstudio.com/DefaultCollection/Builds. Скопируйте этот URL-адрес и вставьте его в поле URL-адрес веб-канала данных в Excel.
3. Настроить безопасность
[ ] — Сделано
  • Нажмите кнопку Дополнительно и сделайте следующие изменения:
    • Значение Integrated Security должно быть установлено в Basic.
    • User ID (@USER@) должно состоять из [account]\[username], где account – это первая часть удаленного TFS @URL@ (например в URL test.visualstudio.com «test» будет account), и username –это имя пользователя, установленное при конфигурировании альтернативной аутентификации в предварительных настройках данного руководства.
    • Password (@PASSWORD@) это пароль установленный при конфигурировании альтернативной аутентификации.
Примечание Процедура установки альтернативных учетных данных описаны на странице https://tfsodata.visualstudio.com/ и в приложении.
4. Проверить соединение
[ ] — Сделано
  • Нажмите кнопку Проверить соединение.
  • После удачной проверки нажмите кнопку Ок в окне дополнительных настроек.
5. Получить данные
[ ] – Сделано
  • В окне Мастер импорта таблиц нажмите Далее и затем Готово.
  • На данный момент данные будут получены из веб-канала OData в PowerPivot. В зависимости от объема данных это может занять от 30 секунд до нескольких минут.
6. Завершить
[ ] – Сделано
  • После того, как вы увидите зеленый флажок, нажмите кнопку Закрыть.
  • Теперь одна таблица находится в PowerPivot. Чтобы добавить другие каналы OData, просто повторите этот процесс, но введите другой URL-адрес импортируемого канала OData.

Таблица 5. Подключение к веб-каналу TFS OData

Добавление дополнительных таблиц и их подключение

В этой части руководства вы добавите две дополнительные таблицы (Projects и Work Items) в модель и соедините их друг с другом.

Шаг Инструкции
1. Добавить таблицу Projects
[ ] – Сделано
2. Добавить таблицу Work Items
[ ] – Сделано
3. Подготовить книгу
[ ] – Сделано
На данный момент есть три таблицы в модели.

  • Переименуйте таблицу WorkItems в Work Items.
    • правой кнопкой мыши щелкните по листу, который содержит «WorkItems» и выберите пункт Переименовать.
    • добавьте пробел между «Work» и «Items» и нажмите клавишу ENTER.
  • Переключитесь на представление диаграммы, щелкнув значок в дальнем нижнем правом углу окна PowerPivot (рядом с маркером перетаскивания в правом нижнем углу).
  • Из таблицы Projects, перетащите поле Name в поле Project таблицы Builds.
  • Из таблицы Projects, перетащите поле Name в поле Project таблицы Work Items.
4. Сохраните книгу
[ ] – Сделано
  • Сохраните книгу с именем @FILE@

Таблица 6. Добавление таблиц

Изменение столбцов и мер

Теперь, когда данные хранятся в модели PowerPivot, ими можно манипулировать: переименовывать, удалять или добавлять столбцы или меры. В этом руководстве будет показано, как была создана модель решения.

Как примечание можно сказать, что обычно помогает именование столбцов в удобном для пользователя виде, т.е. написание слов, установка пробелов между словами и т.д. Эти изменения должны быть сделаны прежде чем делать что-нибудь еще. В противном случае вам придется исправлять формулы, потому что переименование столбца автоматически не исправляет все формулы, связанные с указанным столбцом.

В этом пошаговом руководстве будут вводиться несколько формул. Это не типичные формулы Excel. Они являются формулами выражения анализа данных (Data Analysis Expression, сокращенно DAX). Дополнительные сведения о DAX смотрите на страницах MSDN здесь.

Шаги Инструкции
1. Переименовать столбец StartTime
[ ] – Сделано
  • Вернитесь в представление таблицы и перейдите к данным сборки.
  • Выберите столбец StartTime, щелкнув на заголовок для этого столбца.
  • Щелкните правой кнопкой мыши на столбце и выберите пункт Переименовать.
  • Поставьте пробел между Start и Time и нажмите ENTER.
2. Переименовать столбец FinishTime Повторите шаги для FinishTime, поставив пробел между Finish и Time
3. Добавить вычисляемые столбцы
[ ] – Сделано
Вычисляемый столбец — любой столбец, который не существует в источнике данных.

  • Перейдите к последнему столбцу, который содержит текст Добавление столбца.
  • Щелкните на первой пустой ячейке под заголовком Добавление столбца, введите указанную ниже формулу и нажмите клавишу ENTER: = 24 * 60 * ([Finish Time] – [Start Time])
Примечание Не выполняйте копирование и вставку, т.к. форматирование Word изменяет формулу.
  • Эта формула преобразует разницу между временем начала и окончания в минуты и форматирует ее как число.
  • На вкладке ленты В начало в разделе Форматирование измените формат из Общий в Десятичное число.
  • Щелкните правой кнопкой мыши на Вычисляемый столбец 1 (имя по умолчанию для нового столбца) и переименуйте его в Build Duration.
4. Значение становится мерой
[ ] – Сделано
После этого значение должно стать мерой, так что она может быть агрегирована. Данные в одном только столбце рассматриваются просто как поле (хотя некоторые технологии, такие как Power View, предлагают автоматическую агрегатную функцию, т.к. это число, но не все инструменты позволяют это). Превратив поле в меру после того, как пользователь добавит командный проект в сводную таблицу и затем добавит меру Build Duration Total (будет добавлено далее), PowerPivot автоматически будет суммировать продолжительность сборки для всех сборок, связанных с выбранным командным проектом.

  • Нижняя часть таблицы данных в PowerPivot представляет собой пустую таблицу. Она называется таблицей Мер. Выберите верхнюю ячейку самой левой колонки.
    Обратите внимание, что меры могут быть размещены в любой ячейке в таблице мер, но в виде конвенции, держите все в этом месте, т.к. потом будет легко найти
  • В верхней строке формулы введите следующую формулу:
    Build Duration Total:=CALCULATE(SUM([Build Duration]))

Текст перед «: =» — это имя меры. При создании меры обычно важно, чтобы вы использовали функцию CALCULATE. Причина в том, что CALCULATE гарантирует, что когда PowerPivot вычисляет меру, то учитываются все фильтры, примененные в момент вычисления к таблице. Если CALCULATE не используется, значение будет одинаковым независимо от того, как пользователь фильтрует данные, а это не является ожидаемым поведением.

5. Добавить меры
[ ] – Сделано
  • На последнем шаге добавьте меры к таблице Work Items.
  • Добавьте следующие меры к таблице мер в таблице Work Items:
    Work Item Count:=CALCULATE(COUNTROWS(‘Work Items’))
    Revision Count:=CALCULATE(sum([Revision]))
    Completed Work:=CALCULATE(SUM([CompletedWork]))
    Remaining Work:=CALCULATE(SUM([RemainingWork]))

Таблица 7. Редактирование столбцов и мер

Финальные штрихи

На этом завершается создание модели решения. Для других проектов это будет выполняться идентично, за исключением имени пользователя и пароля. Кроме этого, модель не нужно создавать заново при подключении других проектов –модель решения может использоваться как-есть.

Чтобы подключить модель к другой внешней коллекции, сделайте следующее:

Шаги Инструкции
1. Открыть Excel
[ ] – Сделано
  • Откройте файл @EXCEL@
2. Открыть соединения
[ ] – Сделано
  • Выберите вкладку PowerPivot на ленте и нажмите кнопку Управление.

3. Обновить подключение
[ ] – Сделано
  • Для каждого существующего подключения сделать следующее:
    • Выберите подключение.
    • Нажмите Редактировать.
  • Это откроет окно Мастер импорта таблицы
    • Нажмите Дополнительно.
    • Измените поля имени пользователя и пароля, нажмите ОК и Сохранить.
  • Сделайте это для всех трех соединений и модель теперь будет работать с новым источником данных. Теперь можно просто обновить данные.
  • Нажмите Обновить все на вкладке В начало.
4. Сохранить
[ ] – Сделано
Сохраните файл @EXCEL@

Таблица 8. Подключение другого проекта

Создание отчета с использованием приведенного решения

Существует три способа для создания отчетов на основе модели данных PowerPivot: использование SharePoint для размещения модели PowerPivot (что доступно для Excel Services или Power View), сводные таблицы Excel и Power View в Excel (только в Office 2013 на данный момент).

Этот раздел описывает использование сводных таблиц в Excel и Power View и продемонстрирует основы создания отчетов из внешней службы.

Сводные таблицы

Сводные таблицы являются стандартным механизмом Excel для создания отчетов на основе табличных данных и являются основой для отчетов служб Excel, поэтому эта информация применима для многих сценариев.

В этом пошаговом руководстве предполагается, что вы используете Excel 2013 с включенным дополнением PowerPivot или Excel 2010 с помощью установленными дополнениями PowerPivot (инструкции по включению PowerPivot в Excel 2013 и загрузке дополнения для Excel 2010 можно найти здесь).

Шаги Инструкция
1. Подготовить книгу
[ ] – Сделано
  • Открыть файл @EXCEL@
2. Подготовить управление PowerPivot
[ ] – Сделано
  • Перейдите на вкладку PowerPivot на ленте и щелкните значок Управление (первый значок слева).
  • После того, как модель откроется, нажмите кнопку Сводная таблица на вкладке В начало
  • Выберите Существующий лист и нажмите кнопку OK в диалоговом окне Создать сводную таблицу.
  • Разверните измерение Work Items в списке сводной таблицы.
  • Все Меры отображаются в нижней части измерения, в котором они существуют.
  • Установите флажок рядом с Work Item Count (или перетащите меру Work Item Count в область значений).
  • Это будет отражать количество рабочих элементов в области сводной таблицы на Листе 1.
  • Разверните измерение Projects.
  • Перетащите поле Name в поле Фильтры.
  • В ячейке B1 щелкните раскрывающийся список и выберите соответствующий @AREAPATH@.
  • В измерении Work Items перетащите поле Type в область Строки.
4. Создать сводную диаграмму
[ ] – Сделано
  • Перейдите на ленте на вкладку Анализ.
  • Обратите внимание, что эта вкладка будет отражаться только если ячейка выделена в области сводной таблицы.
  • Выберите значок Сводной диаграммы.
  • Измените тип диаграммы на Круговая и нажмите кнопку ОК.
  • Результат должен быть подобным показанному на рисунке 2, который был очищен и отформатирован для более удобного чтения.

4. Готово
[ ] – Сделано
После этих шагов отчет может быть размещен на сайте SharePoint и использоваться службой Excel. Этот процесс является таким же, как отчетность в нормальном кубе локального TFS.

Таблица 9. Пошаговое руководство: PivotTables

Power View

Power View является интерактивным механизмом отчетности, который Майкрософт создал и выпустил с SQL Server 2012. Интерфейсная версия этого инструмента встроена в Excel 2013. Также доступна в SharePoint 2010 и SharePoint 2013. В этом пошаговом руководстве предполагается, что вы используете Excel 2013 и включили дополнение Power View.

В этом руководстве здесь отображены шаги для включения Power View в и демонстрируется интерактивность средства Power View.

Шаги Инструкции
1. Подготовить книгу
[ ] – Сделано
  • Открыть файл @EXCEL@
2. Добавить вкладку Power View
[ ] – Сделано
  • Выбрать вкладку Вставка на ленте и нажать Power View.
  • Это добавит новый лист Power View1 к книге
3. Создать таблицу
[ ] – Сделано
  • В правой части Поля Power View разверните измерение Projects и установите флажок рядом с Name.
  • Это создаст таблицу на диаграмме со списком проектов.
  • На вкладке Конструирование щелкните на значок Срез.
  • Это добавит точку рядом с каждым командным проектом и сделает возможным его выбор (как показано ниже);

4. Управление отображением Power View
[ ] – Сделано
  • Нажмите в любой чистой области в области отображения Power View. Это очистит текущее выделение, поэтому выбор следующего значения создаст новую таблицу…
  • В списке полей Power View отметьте AreaPath в измерении Work Items.
  • Это создаст новую таблицу со списком путей области.
  • Далее, установите флажок рядом с мерой Work Item Count в Work Items.
  • Нажмите кнопку Линейчатая диаграмма на закладке Конструирование
  • Это преобразует список в линейчатую диаграмму.
  • Перетащите поле Type из измерения Work Items в поле Условные обозначения в списке полей Power View (см. рисунок ниже):

5. Обновление отображения Power View
[ ] – Сделано
  • Щелкните в любое чистое место в области проектирования. Это очистит текущие выделения, поэтому выбор следующего значения создаст новую таблицу…
  • Отметьте поле State в Work Items.
  • Это снова создаст новую таблицу с перечнем состояний.
  • Отметьте меру Work Item Count в Work Items.
  • Щелкните Другая диаграмма в меню Конструирование и выберите Круговая диаграмма.
  • (необязательно) Добавьте название диаграммы, щелкнув на область Щелкните здесь, чтобы добавить заголовок.
  • Финальный вид должен быть приблизительно таким:

6. Использовать больше «сладостей»
[ ] – Сделано
  • Выберите один из командных проектов (@TP@) на графике и обратите внимание, что две диаграммы обновляются для отражения этого действия и отчет теперь отфильтрован по пути области.
  • Далее прокрутите вниз список областей в области команды (@TEAM@) и на линейчатой диаграмме щелкните на части диаграммы, которая показывает тип рабочего элемента Code Review Request. Обратите внимание, что отчет снова был отфильтрован.
  • Далее наведите мышь на область круговой диаграммы Closed.
  • Результат этого должен быть примерно таким:

Таблица 10. Пошаговое руководство: PowerView

<<Содержание

Posted in TFS Practical Reporting Guide | Отмечено: , , | Leave a Comment »

 
%d такие блоггеры, как: