Я Саша Хрущев, технический директор 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.onChange({
// TODO: give the rule a human-readable title
title: 'Register_action',
guard: function(ctx) {
// TODO specify the conditions for executing the rule
return true;
},
action: function(ctx) {
var issue = ctx.issue;
// TODO: specify what to do when a change is applied to an issue
if(issue.id == 'Черновик' || issue.id == 'Issue.Draft'){//пока задача заводится - у нее миллион ревизий с ID "черновик"
console.log('No need to be logged');
return;
}
var content = {};
content.created = issue.created;
content.description = issue.description;
content.project = {id: issue.project.key, name: issue.project.name};
content.issue_id = issue.id;
content.reporter = {login: issue.reporter.login, name: issue.reporter.fullName};
if(issue.fields.Assignee) // исполнителя может и не быть
content.assignee = {login: issue.fields.Assignee.login, name: issue.fields.Assignee.fullName};
content.resolved = issue.resolved;
content.summary = issue.summary;
content.updated = issue.updated;
content.url = issue.url;
if(issue.fields["Due Date"]) // даты может и не быть
content.due_date = issue.fields["Due Date"];
if(issue.fields.Estimation){ //Estimation - объект Joda time, напрямую в JS нельзя использовать
var period = issue.fields.Estimation;
var minutes = !period ? 0 : (period.getMinutes() +
60 * (period.getHours() +
8 * period.getDays() + 40* period.getWeeks()));
content.estimation = minutes;
}
content.issue_type = {name:issue.fields.Type.name, description:issue.fields.Type.description};
content.state = {name:issue.fields.State.name, description:issue.fields.State.description};
content.workitems = [];
issue.workItems.forEach(function(dep) {
content.workitems.push({author: {login: dep.author.login, name: dep.author.fullName}, created:dep.created, date: dep.date, description: dep.description, duration: dep.duration});
});
content.comments = [];
issue.comments.forEach(function(dep) {
content.workitems.push({author: {login: dep.author.login, name: dep.author.fullName}, created:dep.created, text: dep.text});
});
// отправляем получившийся объект в REST
const connection = new http.Connection('https://xx.xx.xx.xx/registerYoutrackAction');
connection.addHeader('Content-Type', 'application/json');
connection.addHeader('Authorization', 'Bearer: xxxxxxxxxxxx');
const response = connection.postSync('', [], JSON.stringify(content));
if (response && response.code === 200) {
console.log('Change logged');
}
else
{
issue.addComment('Ошибка информирования об изменениях: '+response.code);
}
},
requirements: {
// TODO: add requirements
}
});
Ничего сложного, зато ревизия задачи теперь летит в 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 что-то еще — я постараюсь поделиться опытом. Ну и рассказывайте, как вы допиливаете трекер, чтобы решать свои задачи.
Комментарии (5)
serg-mizun
28.07.2023 10:13Скажите пожалуйста, есть там возможность автоматически (по rest, например) сформировать задачи и автоматически же отмечать их выполнение? Например, каждый понедельник должна выполниться задача task1, скрипт (или программа), который ее выполняет, проставляет код завершения. Админу остается только посмотреть, что все запланированные задачи успешно выполнены.
winfox_tech Автор
28.07.2023 10:13Да, у Ютрека есть свой API, все манипуляции с задачами возможные через него. Документация по апишке тут https://www.jetbrains.com/help/youtrack/devportal/youtrack-rest-api.html
Karbofoss
Судя по тому, как быстро джетбрейнс слили россиян, вкладывать время в YouTrack - рисковая затея
winfox_tech Автор
есть такое, но есть пожизненная standalone лицензия и перемещаться куда-то чревато космическими затратами