Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Я Саша Хрущев, технический директор IT-компании WINFOX. Рассказываю о своем опыте освоения скриптинга в YouTrack и о том, как при помощи этого можно делать крутые отчеты.
YouTrack — достаточно мощный и удобный трекер, но встроенная аналитика и отчеты там слабоваты. Чтобы считать рентабельность, детектить проблемы на проектах, командах и у конкретных исполнителей, а также измерять в цифрах все и вся, как мы любим, возможностей трекера недостаточно.
Пользуемся YouTrack уже четыре года, и сейчас самое время осваивать скриптинг в этом трекере.
Автоматическое определение просрочки задач
Начнем с определения просрочки задач. Казалось бы, что в этом сложного? Просто сделайте фильтр по задачам, у которых планируемая дата исполнения раньше текущей, и готово. Сказано — сделано.
Сначала создаем рабочий процесс.
А после создаем в этом процессе модуль, который срабатывает по таймеру. Для этого нам нужен вот такой код:
var entities = require('@jetbrains/youtrack-scripting-api/entities');
var workflow = require('@jetbrains/youtrack-scripting-api/workflow');
var dateTime = require('@jetbrains/youtrack-scripting-api/date-time');
var http = require('@jetbrains/youtrack-scripting-api/http');
exports.rule = entities.Issue.onSchedule({
title: workflow.i18n('Notify assignee about overdue issues'),
search: '#Unresolved has: {Due Date}',
cron: '* 0/1 * ? * * *',
guard: function(ctx) {
return ctx.issue.fields.DueDate < Date.now();
},
action: function(ctx) {
var issue = ctx.issue;
if(!issue.hasTag('Просрочена')){
issue.addTag('Просрочена');
var formattedDate = dateTime.format(issue.fields.DueDate);
var notificationText = workflow.i18n('Issue became overdue on <i>{0}</i>:', formattedDate) +
' <a href="' + issue.url + '">' + issue.summary + '</a><p style="color: gray;font-size: 12px;margin-top: 1em;border-top: 1px solid #D4D5D6">' +
workflow.i18n('Sincerely yours, Angry Fox') + '</p>';
// если вдруг решили добавить информирование в дискорде через вебхук, мы побаловались и отключили
var connection = new http.Connection('https://discord.com/api/webhooks/********/***************');
connection.addHeader('Content-Type', 'application/json');
var content =
{
"username": "AngryFox",
"avatar_url": "https://i.imgur.com/4M34hi2.png",
"content": notificationText
};
var response = connection.postSync('', [], JSON.stringify(content));
if (response && response.code === 200) {
issue.addComment('О просрочке задачи доложено в соответствующие инстанции');
}
else
{
issue.addComment('Ошибка информирования о просрочке: '+response.code);
}
}
},
requirements: {
DueDate: {
type: entities.Field.dateType,
name: "Срок"
},
Assignee: {
type: entities.User.fieldType
}
}
});
Нам удобнее запускать модуль рано утром, так как команда распределенная, и есть любители поработать поздно вечером или даже ночью.
Мы попробовали добавить информирование в Discord. Жутко неудобно и надоедает, но для примера в коде этот кусок оставили.
Размяли лапки на простейшем скрипте, а теперь что-то посерьезней)
Аналитика
Представим, что нам нужно настроить сложную аналитику, например, посчитать рентабельность проектов или отдельных разработчиков, оценить результативность команды. Что нам мешает сделать это стандартными средствами?
Дело в том, что самое слабое место встроенной аналитики в YouTrack — переходы по состояниям. Вот для примера стандартный флоу работы с задачей, принятый у нас в команде:
Регистрируем задачу в статусе «Зарегистрирована».
Оцениваем задачу (Estimate) и ставим дату исполнения согласно проектному плану.
Задача переходит на исполнителя, мы меняем статус на «Открыта», а после корректируем его исходя из рабочего процесса. Например, задача падает на джуна и Estimate увеличивается. Или план поехал, тогда меняется DueDate.
Исполнитель переводит задачу в статус «В работе».
Исполнитель трекает время в задаче.
Исполнитель решает задачу и переводит ее в статус «Решена».
После сборки исполнителем назначается тестировщик и задача переходит в статус «Передано в тестирование».
Тестировщик проводит тесты и трекает затраченное время в задаче.
Если всплывают проблемы по части функциональности, задача переходит в статус «Открыта повторно».
Переходим к пункту 4 и повторяем все до победного.
Стандартные средства, то есть отчеты YouTrack, не показывают нам всю картину ни по затраченному времени, ни по качеству, ни по производительности команды разработки и тестирования. Ну, по крайней мере этого не могут версии YouTrack, которые стоят у нас — 2020-й год с каким-то апдейтом в 2021-м. Поговаривают, что в версии 2023 года все точно так же.
Смена исполнителей, многократная смена и откат статусов (а это мы еще самый простой вариант разобрали) не дает нам никаких шансов собирать инфу традиционным способом. При этом все необходимые данные в трекере есть!
Чтобы решить проблему, мы ввели новый рабочий процесс Register Youtrack Actions. По факту мы просто добавили логгер всех изменений задач через REST в дополнительную базу данных.
Теперь каждая ревизия задачи за исключением аттачей летит в REST, на основе которого мы уже собираем аналитику. Вот как это сделать.
Для начала создаем новый рабочий процесс и прикрепляем к нему модуль, который работает при изменении.
После этого пишем скрипт модуля, который будет логировать все данные по задаче:
var entities = require('@jetbrains/youtrack-scripting-api/entities');
var workflow = require('@jetbrains/youtrack-scripting-api/workflow');
var dateTime = require('@jetbrains/youtrack-scripting-api/date-time');
var http = require('@jetbrains/youtrack-scripting-api/http');
exports.rule = entities.Issue.onSchedule({
title: workflow.i18n('Notify assignee about overdue issues'),
search: '#Unresolved has: {Due Date}',
cron: '* 0/1 * ? * * *',
guard: function(ctx) {
return ctx.issue.fields.DueDate < Date.now();
},
action: function(ctx) {
var issue = ctx.issue;
if(!issue.hasTag('Просрочена')){
issue.addTag('Просрочена');
var formattedDate = dateTime.format(issue.fields.DueDate);
var notificationText = workflow.i18n('Issue became overdue on <i>{0}</i>:', formattedDate) +
' <a href="' + issue.url + '">' + issue.summary + '</a><p style="color: gray;font-size: 12px;margin-top: 1em;border-top: 1px solid #D4D5D6">' +
workflow.i18n('Sincerely yours, Angry Fox') + '</p>';
// если вдруг решили добавить информирование в дискорде через вебхук, мы побаловались и отключили
var connection = new http.Connection('https://discord.com/api/webhooks/********/***************');
connection.addHeader('Content-Type', 'application/json');
var content =
{
"username": "AngryFox",
"avatar_url": "https://i.imgur.com/4M34hi2.png",
"content": notificationText
};
var response = connection.postSync('', [], JSON.stringify(content));
if (response && response.code === 200) {
issue.addComment('О просрочке задачи доложено в соответствующие инстанции');
}
else
{
issue.addComment('Ошибка информирования о просрочке: '+response.code);
}
}
},
requirements: {
DueDate: {
type: entities.Field.dateType,
name: "Срок"
},
Assignee: {
type: entities.User.fieldType
}
}
});
Ничего сложного, зато ревизия задачи теперь летит в REST и ложится в таблицу. В результате такого преобразования получаем таблицу ревизий.
Поле | Тип | Описание |
ID | String | Идентификатор ревизии, генерится при регистрации |
ISSUE_ID | String | Идентификатор задачи в YouTrack |
PROJECT | String | Идентификатор проекта |
TITLE | String | Заголовок (summary) задачи |
DESCRIPTION | String | Описание задачи |
REPORTER | String | Ссылка на автора задачи |
ASSIGNEE | String | Ссылка на исполнителя задачи |
LINK | String | Ссылка на задачу в трекере |
CREATED | Unixtime | Дата создания задачи |
UPDATED | Unixtime | Дата ревизии задачи |
DUE_DATE | Unixtime | Due date, плановая дата исполнения |
ESTIMATION | Int | Плановая оценка в минутах |
TYPE | String | Тип задачи (Bug/Task) |
STATE | String | Статус задачи |
WORKITEMS | Array | Записи о затреканном времени (автор, длительность, дата) |
COMMENTS | Array | Записи о комментариях (автор, содержимое, дата) |
Выглядит это примерно так:
Когда у нас есть все ревизии каждой задачи, мы можем собрать полную историю модификации задачи.
Написав нехитрый скрипт аналитики, мы получили краткую сводку по каждой задаче.
Поле | Тип | Описание |
Идентификатор задачи | String | Идентификатор задачи в YouTrack |
Дата обновления | Unixtime | Дата последней ревизии задачи |
Первый исполнитель | String | Разработчик, на которого первый раз была назначена задача |
Дата постановки | Unixtime | Дата первого назначения на разработчика |
Первый трекер | String | Первый разработчик, затрекавший время на задачу |
Дата/время первого трека | Unixtime | Дата первого трекинга разработчиком |
Дата возврата задачи | Unixtime | Дата, когда задача была первый раз переоткрыта (Reopen) = точка с которой время становится бесполезным |
Кому была возвращена задача | String | На кого задача была переоткрыта |
Последний трекер | String | Разработчик, последним трекавший время на задачу |
Дата последнего трека | Unixtime | Дата последнего трекинга разработчиком |
Тип задачи | String | Bug/Task |
Изначальная оценка | Float | Первая оценка (в задачу она приходит из сметы и первоначального плана) |
Трекинг времени | Array | Последняя информация по трекингу времени |
Дата решения задачи | Unixtime | Время, когда задача или баг перешли в статус Verified |
С такой промежуточной таблицей гораздо проще отлаживать и отслеживать аномалии, продумывать алгоритмы и фиксить баги в скрипте.
Например, после того, как мы собрали данные в таблицу, мы смогли выявить случаи, когда разработчик, на которого заведена задача, первый трекер, тот, на кого задача возвращена, и последний трекер — четыре разных человека. Мы также можем отследить, когда последним трекает время тот, кто смотрел пулреквест, но не относится к фактическим работам в задаче.
Вариантов и флуктуаций — масса, но в целом такие данные позволяют строить более-менее адекватную аналитику.
На основе этих данных мы можем формировать два нужных отчета: отчет план/факт и отчет по разработчикам.
Отчет план-факт
Имея статистику по всем задачам, мы можем генерить хороший отчет в разрезе проектов.
Чтобы получать такие отчеты, мы разделили все переходы на «хорошие» и «плохие». Пока задача идет по «хорошим» переходам, все затраченное на нее время считается полезным. Если хотя бы один переход задачи идет по «плохому» пути, это означает, что в задаче что-то пошло не так в сравнении с идеальным вариантом, и мы должны учитывать все оставшееся время как бесполезное. Баги — изначально бесполезное время.
Вот как выглядит отчет план-факт.
Это не тот отчет, где мы просто считаем планируемое и затраченное время и уходим плакать. Здесь мы точно знаем, где ковырялись с задачами дольше планируемого, где тратили время на исправление багов, а где — на недоделанные и неисправленные баги и задачи.
Боже, почему мы не сделали этого раньше?
Отчет по разработчикам
Этот детальный отчет с разбивкой по разработчикам.
Нас интересуют следующие показатели:
сколько задач было в работе;
сколько задач закрыто;
полезные часы;
бесполезные часы;
сколько задач вернулось к разработчику;
сколько багов было заведено;
сколько багов было закрыто;
сколько полезных часов подтверждено тестировщиками;
сколько бесполезных часов подтверждено тестировщиками;
соотношение продуктивного и непродуктивного времени.
Теперь мы знаем, сколько часов реально тратит каждый разработчик в среднем на один час полезной работы, и можем подсчитать корректную юнит-экономику. А еще можем измерить качество работы отдельных разработчиков.
У нас за стандарт принято 20% непродуктивного времени. Разработчики, которые показывают худший результат, должны улучшать свои показатели. А те, у кого непродуктивное время заняло меньше 20% рабочего, претендуют на премию.
У внимательного читателя наверняка появились вопросы к таблице. Почему есть разработчики с 0% бесполезного времени? Почему у кого-то проверено больше часов, чем отработано? Дело вот в чем.
Показатель «Проверено» говорит лишь о том, подтверждаются часы отдельно взятого разработчика или нет. Например, сотрудник может пилить чудовищный функционал полтора-два месяца, и за это время ни одна его задача не уйдет в тесты. Это маркер того, что показатель текущего месяца для этого разработчика может быть неактуален, и его результаты стоит рассматривать в разрезе статистики за два-три месяца.
Соответственно, в следующем месяце по таким разработчикам проверенных часов будет значительно больше, чем отработанных.
Что дальше
Мы написали и другие полезные скрипты для YouTrack. Вот несколько задач, которые они решают:
Невозможность трекать время в задачу без Estimation.
Невозможность закрывать задачи по фронту без приложения аттачей. У нас правило: если что-то сделано по фронту, нужен скрин или видео.
Напоминания о забытых задачах.
Останавливаться на этом не собираемся. В будущем хотим подключить к аналитике задач ChatGPT и доделать-таки наше приложение для работы с трекером.
Пишите в комментариях, если вам интересно узнать про скриптинг в YouTrack что-то еще — я постараюсь поделиться опытом. Ну и рассказывайте, как вы допиливаете трекер, чтобы решать свои задачи.