Примеры Формул

Аналитика

Рассчитайте время, необходимое для сжигания бэклога

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


КОД

WITH velocity = 10:

  CONCAT(SUM#preceding{story_points} / velocity, "w")

Чтобы этот пример работал наиболее эффективно, структура должна быть отсортирована в зависимости от того, какую работу вы выберете для выполнения в первую очередь. Дополнительные сведения см. в разделе Генераторы сортировки.  

Подсчет дней просрочки

В этом примере выполняется проверка просроченных элементов и возвращается количество дней, в течение которых элемент просрочен.


КОД

IF dueDate < NOW():

   DAYS_BETWEEN(dueDate, NOW()) CONCAT " days late"

Сравните исходную оценку с зарегистрированной работой и оставшейся оценкой


КОД

IF originalEstimate:

    (timeSpent + remainingEstimate) / originalEstimate

ELSE:

    "not estimated"

Вычислить межквартильный диапазон оценок сюжетных баллов


КОД

WITH points = ARRAY { storyPoints } :   // Содержит все баллы истории дочерних элементов.

  QUARTILE(points, 3) - QUARTILE(points, 1)

Комментарии

Показать дату, автора и текст последнего комментария


КОД

comments.UMAX_BY($.created).map(CONCAT(

$.author.user_display_name(),

" said at ",

FORMAT_DATETIME($.created, "yyyy-MM-dd HH:mm:ss"),

": ", $.body))

Показать последний комментарий, сделанный пользователем


КОД

comments.FILTER($.author = "admin").UMAX_BY($.created)

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

Показать дату последнего комментария пользователя


КОД

comments.filter(x -> x.author = "admin").map(x -> x.created).max()

В этом примере дата соответствует последнему комментарию, сделанному пользователем «admin». Чтобы показать дату для другого пользователя, замените «admin» соответственно.

Отображать «Отвечено», если есть комментарии после моего последнего


КОД

WITH myLastCommentDate = comments.FILTER($.author = me()).MAP($.created).MAX() :

IF (comments.ANY($.created > myLastCommentDate); "Answered")

Исторические значения

Показать историческую ценность поля задачи на определенную дату

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


КОД

historical_value(this, "duedate", datetime("15/May/18 6:24 PM"))

Примечание. В этой формуле также используется функция Datetime — подробнее

 Показать количество задач добавленных с начала последнего спринта


КОД

SUM {

IF history.changes

.FILTER($.field = "sprint")

.LAST()

.changeGroup.timestamp > sprint.last().startDate: 1

}

Время отмечено: время, когда задача была отмечена флажком.


КОД

with flag_change_time(value) =

history.changes

  .filter($.field = "flagged")

  .filter($.to = value)

  .changeGroup.time :

with flag_on_time = flag_change_time("Impediment") :

with flag_off_time = flag_change_time("") :IF flag_on_time && flag_off_time : flag_off_time - flag_on_time

ELSE IF flag_on_time : now() - flag_on_time

Время в статусе за определенный месяц


КОД

WITH year = 2023:

WITH month = 1: // 1 for Jan, 12 for Dec

WITH keyStatus = "in progress": // key-insensitive

WITH calendar = "Standard work calendar 8/5": // other option is Standard calendar 24/7, the value is locale-dependant, also Gantt calendars are available

WITH startDate = MAKE_DATE(year, month, 1):

WITH finishDate = MIN(DATE_ADD(startDate, 1, "month"), NOW()):

WITH isStart(change) = change.from != keyStatus AND change.to = keyStatus:

WITH isFinish(change) = change.from = keyStatus AND change.to != keyStatus:

WITH intervalFits(start, finish)

  =  start >= startDate AND start <= finishDate

  OR finish >= startDate AND finish <= finishDate

  OR start < startDate AND finish > finishDate:

WITH statusChanges = history.changes

  .FILTER($.field = "status" AND ($.isStart() OR $.isFinish())):

WITH times = MERGE_ARRAYS(

  IF statusChanges.FIRST().isFinish(): MIN(startDate, statusChanges.FIRST().changeGroup.time),

  statusChanges.changeGroup.time,

  IF statusChanges.LAST().isStart(): MAX(finishDate, statusChanges.LAST().changeGroup.time)

):

 IF times: SEQUENCE(0, times.SIZE() - 1)

  .FILTER(MOD($, 2) == 0 AND intervalFits(times.GET($), times.GET($ + 1)))

  .MAP(CALENDAR_DURATION(MAX(times.GET($), startDate), MIN(times.GET($ + 1), finishDate), calendar))

  .SUM()

Ссылки на задачи и подзадачи

Отображает задачи, связанные с текущей задачей.


КОД

issueLinks.MAP(IF($.source = this, $.destination, $.source))

Показать ссылки на задачи

Отображает ссылки на задачи, содержащие текущую задачу.Ex. STR-006 → GANTT-002


КОД

issueLinks.MAP($.source.key CONCAT '→' CONCAT $.destination.key)

Показать задачи, блокирующие текущую задачу

Отображает ссылки на задачи для всех блокировщиков.


КОД

WITH _format(issue) = """[${issue.key}|${issue.url}]""" :

issuelinks

.FILTER($.type = 'Blocks' AND $.destination = this)

.MAP(_format($.source))

Убедитесь, что установили для столбца «Параметры» значение «Разметка вики».

ü

Хотите отобразить другой тип ссылки? Изменение: $.type = 'Blocks'

ü

Проверьте, решены ли все проблемы с блокировкой

Отображает «ОК», если все задачи, связанные с помощью типа ссылки «Блоки», помечены как решенные.


КОД

IF issueLinks.FILTER($.type = "Blocks" AND $.destination = this).ALL($.source.resolution):

"OK"

Показать процент выполненных подзадач


КОД

IF subtasks.SIZE() > 0 :

subtasks.FILTER($.status = ‘Done’).SIZE() / subtasks.SIZE()

Элементы и свойства

Доступ к свойству элемента

Используйте следующий формат: item.property

 Следующее возвращает дату выпуска для каждой версии исправления:


КОД

fixVersions.releaseDate

 Примечание: если поле версии исправления содержит несколько значений, будет возвращено несколько дат.

 Список доступных типов элементов и их свойств см. в разделе Справочник по свойствам элементов.

Получите значение пользовательского поля для этой задачи, ее эпика или подзадачи.

Вы можете сделать это несколькими способами:


КОД

this.storypoints // Использование свойств элемента. Используйте имя настраиваемого поля в нижнем регистре с пропуском пробелов.

this.ACCESS("Story Points") // Using the function. Использование функции ACCESS, ДОСТУП. Напишите настраиваемое поле точно так, как оно отображается в Jira (с пробелами).

this.customfield_###### // Использование идентификатора пользовательского поля.

Посмотрите, к скольким спринтам добавлена задача


КОД

sprint.size()

Найти наивысший приоритет подзадачи


КОД

CODE

subtasks.priority.UMAX()

Возвращает наивысший приоритет подзадач.

Найдите подзадачу с наивысшим приоритетом


КОД

with highest_priority = subtasks.priority.UMAX(): subtasks.FILTER($.priority = highest_priority)

Сравните два приоритета


КОД

IF(priority1.sequence > priority2.sequence)

Предсказывает дату окончания для эпиков


КОД

IF issueType = epic :

MAX(epicStories.sprint.endDate)

Возвращает последнюю дату окончания спринта для историй в каждом эпике, даже если эти истории не содержатся в структуре.

JQL and S-JQL

Показать совокупные баллы для определенной группы пользователей Jiraс


КОД

SUM {

IF JQL { assignee in membersOf('Group A') } :

storyPoints

}

Примечание. Замените «Группа А» названием группы, для которой вы хотите произвести расчет.

Хотите агрегировать другое значение? Просто замените «storyPoints» атрибутом, который вы хотите рассчитать.

Пользователи

Показать всех, кто работал над задачей


КОД

ARRAY(reporter, assignee, developer, tester)

Примечание. Разработчик и тестер — это настраиваемые поля. Они будут автоматически сопоставлены, только если эти настраиваемые поля существуют в вашем экземпляре Jira.

Показать всех, кто работал над любой задачей в поддереве


КОД

VALUES { ARRAY(reporter, assignee, developer, tester) }

Примечание. Разработчик и тестер — это настраиваемые поля. Они будут автоматически сопоставлены, только если эти настраиваемые поля существуют в вашем экземпляре Jira.

Подсчитайте, кто выполнил больше всего работы


КОД

worklogs

.GROUP($.author)

.MAP(ARRAY($.group, $.elements.timespent.sum()))

.UMAX_BY($.GET(1))

.GET(0)

Получите подробное описание задач, на которые пользователи потратили время

Во-первых, используйте группировщик атрибутов с формулой:


КОД

worklogs.author.UNIQUE()

Затем добавьте столбец формулы:


КОД

IF itemType = 'user':

SUM#children {

WITH user = PARENT { item } :

worklogs

.FILTER($.author = user)

.timeSpent.SUM()

}

Версии

Проверить наличие конкретной версии исправления


КОД

fixVersions.CONTAINS("v1")

Если задача содержит этот fixVersion, возвращает 1 (true). В противном случае возвращает 0 (ложь).

 

Получить последнюю/самую раннюю версию исправления


КОД

fixVersions.UMAX_BY($.releaseDate) // последняя

fixVersions.UMIN_BY($.releaseDate) // самая ранняя

Найдите самый большой промежуток времени затронутой версии


КОД

affectedVersions.MAP(IF $.releaseDate AND $.startDate: $.releaseDate - $.startDate).MAX()

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

Хотите кратчайший результат? Измените MAX на MIN.

Узнать больше: функция MAP

 

Показать все версии, на которые есть ссылки в поддереве


КОД

VALUES { ARRAY(fixVersions, affectedVersions).FLATTEN().UNIQUE() }

Получить все версии исправлений с будущими датами выпуска


КОД

fixVersions.FILTER($.releaseDate AND $.releaseDate > NOW())

Показать все выпущенные затронутые версии


КОД

affectedVersions.FILTER($.isreleased)

Показать все задачи, выпущенные за установленный период времени

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


КОД

DATE(“0/Jan/2021”) < fixVersion.releaseDate

   AND fixVersion.releaseDate < DATE (“31/Mar/2021”)

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

 


КОД

with parentVersion = PARENT{FixVersion}:

  if(parentVersion and !parentVersion.contains(fixVersion); "version mismatch")

Вики-разметка

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

Мы собрали несколько расширенных, настраиваемых примеров использования вики-разметки:

Отображать подзадачи в виде ссылок


КОД

subtasks.MAP("""[${$.key}|${$.url}]""")

Создайте фон без полей за значением


КОД

WITH addBackground(value, color) =

"""{panel:bgColor=$color|borderWidth=0px}$value{panel}""":

addBackground(summary, "#ADFF2F")

Настраиваемый индикатор выполнения

В этом простом примере мы использовали Wiki Markup для создания пользовательского индикатора выполнения. В левом столбце вы можете увидеть встроенный столбец прогресса. В правом мы построили индикатор выполнения, который разделен на 10% разделов.

Мы использовали следующую формулу для создания пользовательского индикатора выполнения:

Простой индикатор выполнения


КОД

WITH simpleProgressBar(progress, maxProgress, stepCount) = (

WITH _bars(count, color) = """{color:$color}${REPEAT("■", count)}{color}""":

WITH doneBarsCount = FLOOR(progress / maxProgress * stepCount):

_bars(doneBarsCount, "green") CONCAT _bars(stepCount - doneBarsCount, "gray")

):

 simpleProgressBar(customProgress, 1, 10)

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

 Цвета можно легко настроить, изменив значения «цвета» — в этом случае мы использовали зеленые и серые квадраты.

  • Расчет прогресса может основываться на любом процентном значении. В следующем примере мы использовали произвольное процентное поле и агрегировали его по иерархии.

Простой индикатор выполнения


КОД

WITH simpleProgressBar(progress, maxProgress, stepCount) = (

WITH _bars(count, color) = """{color:$color}${REPEAT("■", count)}{color}""":

WITH doneBarsCount = FLOOR(progress / maxProgress * stepCount):

_bars(doneBarsCount, "green") CONCAT _bars(stepCount - doneBarsCount, "gray")

): 

simpleProgressBar(SUM { progressField }, SUM { 1 }, 10)

Это может быть особенно полезно, если вы хотите отображать ход выполнения на основе некоторых сложных полей, таких как поле сценария ScriptRunner, которое в настоящее время не поддерживается стандартным столбцом формулы.

Настраиваемые строки состояния

Вики-разметку также можно использовать для создания более сложных вычислений прогресса на основе нескольких статусов задач.

В следующем примере мы создали несколько настраиваемых строк состояния, отслеживающих следующие состояния: 

  • Сделать = красный
  • В процессе = оранжевый
  • Готово = зеленый
  • Все остальные статусы = серый

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

Мультибар

Мы использовали следующий код для создания строки состояния с несколькими полосами.

Многоуровневый индикатор выполнения


КОД

//stepCount - length of the bar chart in characters

WITH multiProgressBar(progressArray, maxProgress, colorsArray, colorForRemaining, stepCount) = (

WITH _bars(count, color) = (IF count > 0: """{color:$color}${REPEAT("▮", count)}{color}""" ELSE ""):

WITH barCounts = progressArray.MAP(FLOOR($ / maxProgress * stepCount)):

progressArray.INDEXES()

.MAP(_bars(barCounts.GET($), colorsArray.GET($)))

.MERGE_ARRAYS(_bars(MAX(0, stepCount - barCounts.SUM()), colorForRemaining))

.JOIN("", "", "")

):

 WITH todo = COUNT#truthy { status = "To Do" }:

WITH inProgress = COUNT#truthy { status = "In Progress" }:

WITH done = COUNT#truthy { status = "Done" }:

 multiProgressBar(

ARRAY(todo, inProgress, done), COUNT { 1 },

ARRAY("red", "orange", "green"), "gray",

20

)

КОД

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

В то время как символам ■ или ▮ может не хватать твердости, символ █ все же создает легкий эффект кирпичной кладки.

Мультибар с изображением

В этом примере мы использовали простые монохромные изображения (достаточно размера 1x1 пиксель), чтобы строка состояния выглядела более цельной. Если вы решите попробовать это, мы настоятельно рекомендуем использовать локально размещенный образ, а не образ, взятый из общедоступных источников, потому что некоторые хосты могут блокировать несколько последовательных запросов изображения.

Многоуровневый индикатор выполнения на основе изображений


КОД

//Зернистость - длина гистограммы в пикселях

WITH multiProgressBarWithImage(progressArray, maxProgress, imagesArray, imageForRemaining, granularity) = (

WITH bar(width, image) = (IF width > 0: """!$image|height=20,width=$width!""" ELSE ""):

WITH barCounts = progressArray.MAP(FLOOR($ / maxProgress * granularity)):

progressArray.INDEXES()

.MAP(bar(barCounts.GET($), imagesArray.GET($)))

.MERGE_ARRAYS(bar(MAX(0, granularity - barCounts.SUM()), imageForRemaining))

.JOIN("", "", "")

):

WITH todo = COUNT#truthy {status = "to do"}:

WITH inProgress = COUNT#truthy {status = "in progress"}:

WITH done = COUNT#truthy {status = "done"}:

 WITH link(name) = """https://www.example.com/images/$name.png""":

 multiProgressBarWithImage(

ARRAY(todo, inProgress, done), COUNT{1},

ARRAY("Red", "Orange", "Green").MAP(link), link("Gray"),

200

)

Мультибар с цифрами

В этом последнем примере строка состояния отображает количество проблем для каждого состояния, если позволяет ширина строки. Этот код можно легко настроить для отображения фактического количества проблем или их процента.

 Полоса прогресса с цифрами


КОД

//Параметры: granularity - длина гистограммы в символах; bar - заполнитель гистограммы

WITH multiProgressBarWithNumbers(progressArray, maxProgress, colorsArray, colorForRemaining, granularity, bar) = (

WITH bars(count, value, color) = (

IF count <= 0:

""

ELSE:

WITH bars = (

WITH charsForValue = LEN(value):

IF count >= charsForValue + 2:

WITH charsBeforeValue = FLOOR((count - charsForValue) / 2):

REPEAT(bar, charsBeforeValue)

CONCAT value

CONCAT REPEAT(bar, count - charsBeforeValue - charsForValue)

ELSE:

REPEAT(bar, count)

):

"""{color:$color}$bars{color}"""

):

WITH barCounts = progressArray.MAP(FLOOR($ / maxProgress * granularity)):

progressArray.INDEXES()

.MAP(bars(barCounts.GET($), progressArray.GET($), colorsArray.GET($)))

.MERGE_ARRAYS(bars(MAX(0, granularity - barCounts.SUM()), MAX(0, maxProgress - progressArray.SUM()), colorForRemaining))

.JOIN("", "", "")

):

WITH todo = COUNT#truthy {status = "to do"}:

WITH inProgress = COUNT#truthy {status = "in progress"}:

WITH done = COUNT#truthy {status = "done"}:

multiProgressBarWithNumbers(

ARRAY(todo, inProgress, done), COUNT{1},

ARRAY("red", "orange", "green"), "gray",

20, "▮"

)

Простая диаграмма выгорания

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

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

Диаграмма выгорания


КОД

WITH dataArray = ARRAY(

COUNT#truthy {DATE_SUBTRACT(NOW(),6,"days") <= created and DATE_SUBTRACT(NOW(),5,"days") > created},

COUNT#truthy {DATE_SUBTRACT(NOW(),6,"days") <= resolved and DATE_SUBTRACT(NOW(),5,"days") > resolved},

COUNT#truthy {DATE_SUBTRACT(NOW(),5,"days") <= created and DATE_SUBTRACT(NOW(),4,"days") > created},

COUNT#truthy {DATE_SUBTRACT(NOW(),5,"days") <= resolved and DATE_SUBTRACT(NOW(),4,"days") > resolved},

COUNT#truthy {DATE_SUBTRACT(NOW(),4,"days") <= created and DATE_SUBTRACT(NOW(),3,"days") > created},

COUNT#truthy {DATE_SUBTRACT(NOW(),4,"days") <= resolved and DATE_SUBTRACT(NOW(),3,"days") > resolved},

COUNT#truthy {DATE_SUBTRACT(NOW(),3,"days") <= created and DATE_SUBTRACT(NOW(),2,"days") > created},

COUNT#truthy {DATE_SUBTRACT(NOW(),3,"days") <= resolved and DATE_SUBTRACT(NOW(),2,"days") > resolved},

COUNT#truthy {DATE_SUBTRACT(NOW(),2,"days") <= created and DATE_SUBTRACT(NOW(),1,"days") > created},

COUNT#truthy {DATE_SUBTRACT(NOW(),2,"days") <= resolved and DATE_SUBTRACT(NOW(),1,"days") > resolved},

COUNT#truthy {DATE_SUBTRACT(NOW(),1,"days") <= created and DATE_SUBTRACT(NOW(),8,"hours") > created},

COUNT#truthy {DATE_SUBTRACT(NOW(),1,"days") <= resolved and DATE_SUBTRACT(NOW(),8,"hours") > resolved},

COUNT#truthy {DATE_SUBTRACT(NOW(),8,"hours") <= created},

COUNT#truthy {DATE_SUBTRACT(NOW(),8,"hours") <= resolved}

):

//25 — максимальная рабочая высота

WITH maxHeight = 25:

WITH maxValue = dataArray.MAX():

WITH getPicture(index) = (IF index.MOD(2) == 0: "https://www.example.com/images/Red.png" ELSE: "https://www.example.com/images/Green.png"):

WITH getHeight(index) = IF maxValue : FLOOR(dataArray.GET(index) / maxValue * maxHeight) ELSE : 0 :

IF itemtype != "issue":

dataArray

.INDEXES()

.MAP("""!${getPicture($)}|height=${getHeight($)},width=5!""")

.JOIN("", "", "")

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

По материалам Atlassian JIRA Structure: Sample Formulas