четверг, 7 октября 2010 г. - www.msmirnov.ru

Учет трудозатрат, времени сотрудников и отчетность в TFS (Team Foundation Server)


В этом посте я продолжаю описание своего опыта внедрения TFS (Team Foundation Server), начатое здесь, здесь и здесь.


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

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

На выходе необходимо было иметь отчет следующего вида:

Task nameDevelopment hrsTesting hrsManagement hrs
Task 1.........
Task 2.........
Task 3.........

Лирическое отсупление

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

Ответы были следующие:
- Учет трудозатрат не ведется.
- Учет трудозатрат ведется суммарно по двум направлениям - разработка и исправление багов. Трудозатраты по отдельным задачам и трудозатраты на тестирование не учитываются.
- Учет трудозатрат ведется по компонентам разрабатываемого продукта. Трудозатраты по отдельным задачам на учитываются.

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


Для того, чтобы организовать учет времени по выполняемым задачам мне пришлось добавить в task два поля - DevelopmentHours и TestingHours.



Эти поля были предназначены для учета фактических трудозатрат на разработку и тестирование и при создании задач автоматически устанавливались равными нулю.

Требовалось сделать так, чтобы при переходе task'а из состояния Active в Completed разработчики должны были указать задать значение поля DevelopmentHours.
Аналогично при переходе из состояния Completed в Closed или обратно в Active тестировщики должны были указать значение TestingHours.

При чем, просто добавить правило REQUIRED было бы недостаточно, потому что задача могла многократно переходить из одного состояния в другое в процессе тестирования и исправления и каждый необходимо было указывать новое значение DevelopmentHours или TestingHours, а правило REQUIRED не могло бы этого обеспечить - оно бы стало только лишь гарантировать присутствие значение как такового, даже предыдущего, а не нового.

Для решения задачи пришлось использовать правило NOTSAMEAS, которое сравнивает значение поля со значением другого поля и если они совпадают, то не позволяет сохранять задачу. В качестве второго поля должно было бы выступать предыдущее значение DevelopmentHours или TestingHours в зависимости от ситуации. Поскольку в задаче сами по себе предыдущие значения не хранятся, пришлось добавить еще два поля, которые пользователю не видны и которые используются для хранения предыдущих значений - DevelopmentHours2 и TestingHours2.

Эти поля автоматически заполняются с использованием правила COPY значениями полей DevelopmentHours и TestingHours при сохранении задачи.

Таким образом, когда у разработчика возникает необходимость изменить статус задачи с Active на Completed у него значения полей DevelopmentHours и DevelopmentHours2 совпадают и он не может сохранить задачу, пока не изменит поле DevelopmentHours. При сохранении задачи новое значение DevelopmentHours копируется в DevelopmentHours2 и они снова становятся одинаковыми, чтобы в следующий раз вновь обеспечить необходмость изменения поля DevelopmentHours.

Выглядит это так:



Для TestingHours и TestingHours2  все работает абсолютно аналогично, но только при переходе из состояния Completed в Closed или обратно в Active.

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

При переходе из состояние Active в Closed никаких проверок я добавлять не стал - т.е. задачу можно просто закрыть и все, если она, например, была отменена.
Учет времени по ошибкам я организовывать не стал, поскольку это, как мне кажется, стало бы отнимать слишком много времени само по себе.

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

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

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


Отчетность.

Для организации отчетность в TFS используется технология Reporting Services.
Открыть Диспетчер отчетов можно через портал проекта, а можно прямо из Visual Studio:



Для создания отчета пришлось использовать Visual Studio 2008 - в Visual Studio 2010 подходящего типа проекта почему-то не оказалось.

Для создания отчета необходимо создать проект типа Report Server Project, в котором создать .rdl-файл, являющийся описанием требуемого отчета.

В качестве источника данных была указана хранимая процедура, которая возвращала список work item'ов с типом task, так как только для них был организован учет трудозатрат.




При написании запроса к базе TFS было установлено следующее:
1. Все work item'ы хранятся в таблице WorkItemsAre. Тип при этом хранится в поле [Work item Type]. Т.е. если вы хотите выбрать все задачи, то можно написать такой запрос:

select *
from WorkItemsAre
where [Work item Type] = 'task'
 
2. Все поля (в том числе и добавляемые дополнительно) хранятся в таблице Fields. Дополнительно создаваемые поля имеют в данной таблице FldID начиная с 10 000.

При этом имя поля из таблицы WorkItemsAre составляется так - Fld + FldID поля из таблицы Fields.

Для примера, :
Поле DevelopmentHours в таблице Fields имеет FldID = 10133.
Соответственно его значение хранится в таблице WorkItemsAre в поле Fld10133.

Таким образом сначала определяем имя поля из таблицы Fields, а потом делаем составной запрос к таблице WorkItemsAre.

Т.е. примерно так:

declare @FieldName varchar (255), @q nvarchar (4000)

select @FieldName = 'Fld' + CONVERT (varchar (255), FldID)
from Fields
where ReferenceName = 'ROI.DevelopmentHours'

set @q = 'select ' + @FieldName + '
from WorkItemsAre
where [Work item Type] = ''task'''

exec sp_executesql @q
 
 
Таким образом можно определить и выбрать практически все интересующие поля.
Тип полей при этом совпадает с типом поля, заданном в TFS.

Используя хранимые процедуры можно написать практически любой запрос на получение данных из таблицы work item'ов, что и было сделано.

P.S.
Другие мои посты на тему TFS:
1. Миграция в TFS (Team Foundation Server)
2. Кто ошибку создает - тот ее и проверяет?
3. Нумерация версий продукта в TFS (Team Foundation Server)
4. Учет трудозатрат и отчетность в TFS (Team Foundation Server)
5. Как создать work item в TFS (Team Foundation Server) из письма в Outlook
6. Как поместить свой control на форму Work Item в TFS (Team Foundation Server)
7. Как настроить номер итерации по умолчанию в TFS (Team Foundation Server)
Мой сайт - www.msmirnov.ru