Аналитика в Redmine. Computed Custom Field.

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

Custom Report

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

Переход в состояние

В случае, если работа над задачей носит циклический характер, например, дизайн-верстка-тестирование — важной характеристикой для оценки качества работы отдела может быть количество переходов в определенное состояние, показывающая сколько раз задача возвращалась на доработку. Например, нужно отследить насколько хорошо тестировщики тестируют верстку. В этом случае смотрим количество переходов из любых статусов отличных от статуса «Тестирование».

В компоненте Computed custom field добавляем вычисляемое custom field с типом int. В моих предыдущих статьях подробно написано, что и как нужно делать. Код на Ruby будет следующий:

# ID for status "Тестирование" - 10
if (cfs[53].blank?)
   cfs[53] = 0
end

if (self.status_id == 10 && self.status_id_was != 10)
   cfs[53] + 1;
else
   cfs[53]
end

Время этапа

Интересно посмотреть сколько времени занимает определенный этап разработки. Например, сколько при выполнении задачи было затрачено суммарно времени на этап «Тестирование».

Создадим переменную дата последнего этапа с типом DateTime, куда будем заносить дату и время когда был начат предыдущий этап в процессе:

#Переменная в списке custom fields должна быть в самом конце. 
if (self.status_id != self.status_id_was)
  cfs[55] = DateTime.now;
else
  cfs[55];
end

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

Создадим ещё один computed custom field с типом Float. Для статуса «Тестирование» скрипт будет такой:

# ID for status "Тестирование" - 10
if (cfs[56].blank?)
   cfs[56] = 0
end

if (cfs[55].blank? || !cfs[55].present?)
   cfs[55] = DateTime.now;
end

if ((self.status_id == 10 && self.status_id_was != 10) ||
   (self.status_id_was == 10 && self.status_id != 10))
   diff = DateTime.now.to_time - cfs[55].to_time;
   #self.description = (diff).to_s + "\r\n";
   cfs[56] = (cfs[56] + diff)/60; #In minutes 
else
   cfs[56]
end

Ключевй статус по которому производится отсечка времени — «Тестирование». Соответственно, с момента, когда другие отделы перевели с любого другого статуса на статус «Начать тестирование» происходит сохранение даты и времени перехода на этот статус. Как только статус будет изменен, вычисляется время затраченное на переход со статуса «Начать тестирование» на статус «Тестирование».

Со статуса «Тестирования» задача может уйти на разные статусы, но любой из таких переходов обозначает, что этап тестирования завершен. Нужно опять зафиксировать время затраченное на этап и добавить к ранее сохраненному.

Понятно, что такой простой вариант рассчета времени этапа дает суммарное значение которое потребовалось на этап. Если статус был переведен перед окончанием рабочего дня, то нерабочее время также быдет учтено. Корректно вычленить нерабочее время — штука довольно трудоемкая для случая, когда график относительно свободный. Скажем, сотрудник может приехать на пару часов позже остальных и задержаться на это время.

В простейшем случае, если более или менее выдерживается время на отдых, скажем, сотрудник ушел с работы в 18.00 и пришел в 9.00, а если ушел в 20.00, то и пришел на работу в 11.00, то код для корректировки будет несложным:

    numofdays = (DateTime.now.to_date - cfs[55].to_date).to_i;
    excludetime = numofdays * 15*3600;
    cfs[56] = (cfs[56] + diff - excludetime)/60; #In minutes 

15 — это примерное количество нерабочих часов. Соответственно, если оба статуса переводились в один день, то разница между датами будет равна 0 и поправки не будет. Если между сменой статусов прошли 1 сутки, то из рассчитанного суммарного кол-ва времени в секундах будет вычтена 15-ти часовая поправка.

Ответственный сотрудник

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

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

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

По идее, попадание задачи в два обозначенных статуса — это гарантия правильного заполнения поля «Дизайнер». Для подстраховки можно добавить, что при уходе на любой другой статус с статусов «Разработка дизайна» и «Редизайн» также происходит смена ответственного. По идее можно было бы ограничится только последними условиями, но в этом случае при построении аналитики до момента завершения работы над дизайном, поле отвественного было бы не заполненным.

Соответственно, код будет выглядеть следующим образом:

# ID для роли "Дизайнер" - 10
# ID для статуса "Редизайн" - 27
# ID для статуса "Разработка дизайна" - 18 

# ID для статуса "Дизайн разработан" - 17
# ID для статуса "Согласование дизайна" - 19
# ID для статуса "Начать верстку" - 20
# ID custom field для текущего поля - 34
 
if (User.current.roles_for_project(project).map(&:id).include? 10) &&
   ((self.status_id == 17) || #Дизайн разработан
    (self.status_id == 18) || #Разработка дизайна
    (self.status_id == 27) || #Редизайн
    (self.status_id_was == 18) || #Разработка дизайна 
    (self.status_id_was == 27) #Редизайн
   )          
   User.current.id
else
   cfs[34]
end

Если пользователь входит в группу дизайнеров (проверяем строчкой (User.current.roles_for_project(project).map(&:id).include? 10)) , то простановка им любого из 5 ранее озвученных статусов приведет к тому, что в поле с ID = 34 будет прописан текущий пользователь. Соответственно, появится возможность стандартными средствами Redmine выгружать отчет, например, в csv и анализировать сводными таблицами в Excel, например, по сколько задач сделал каждый из сотрудников отдела дизайна.

В данном случае крайне важно для корректной работы кода, чтобы дизайнеру задачи передавались сменой статуса на «Разработка дизайна» или «Редизайн». В противном случае нужно добавлять в условия все статусы на которые дизайнер может перевести задачу.

Дата завершения

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

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

Учитывая, что в Redmine для анализа дата должна быть вынесена в колонку, необходимо добавить вычисляемое поле в которую будет  прописываться дата, когда этап был окончательно завершен дизайнером. По-сути, достаточно добавить некоторое вычисляемое поле типа DateTime и записывать в него значение каждый раз, когда дизайнер меняет статус с значений «Редизайн», «Разработка дизайна», «Дизайн разработан» на любой другой, фактически указывая, что его работа завершена.

# ID для роли "Дизайнер" - 10
# ID для статуса "Редизайн" - 27
# ID для статуса "Разработка дизайна" - 18 
# ID для статуса "Дизайн разработан" - 17
# ID custom field для текущего поля - 97
 
if (User.current.roles_for_project(project).map(&:id).include? 10)
   if  (
          #(self.status_id != self.status_id_was) &&
          (self.status_id_was == 18) || #Разработка дизайна
          (self.status_id_was == 27)    #Редизайн
        )       
          cfs[97] = DateTime.now;
    else
          cfs[97]
    end
else
   cfs[97]
end

В данном случае если сотрудник из группы дизайнеров уходит со статуса «Разработка дизайна» или «Редизайн» на любой другой, то в поле прописывается текущая дата.

Отображение аналитических полей

Рядовым сотрудникам отображать аналитические поля не нужно, поэтому в настройках полей нужно проставить Visibility («Видимое») только для отдельных ролей. Например, менеджеров.

Spread the love
Запись опубликована в рубрике IT tools, IT решения для бизнеса с метками . Добавьте в закладки постоянную ссылку.