В этом посте я продолжаю описание своего опыта внедрения
TFS (Team Foundation Server), начатое
здесь,
здесь и
здесь.
Некоторое время назад появилась необходимость составить отчетность о трудозатратах, которые мы осуществляем при разработке и тестировании задач, выполняемых в рамках работы над выпуском новых версий нашего продукта.
Деятельность наша продукто-ориентированная, а не проектно-ориентированная, поэтому не было необходимости учета и сравнения плановых и фактических трудозатрат - речь шла только о фактических трудозатратах.
На выходе необходимо было иметь отчет следующего вида:
Task name | Development hrs | Testing hrs | Management 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)