Come for the products,
stay for the community

The Atlassian Community can help you and your team get more value out of Atlassian products and practices.

Atlassian Community about banner
4,298,488
Community Members
 
Community Events
165
Community Groups

ScriptRunner - Get name of user who made last transition

Hi,

I found a script that shows the amount of time an issue was in a status, i.e. 21 minutes "On Hold", 1 week "In Progress".

This is a duration template used.

I am wondering if I can modify this script OR would it be easier to create a new field and add a script to display the name of the user who made the last transition?

I am not sure what that script would look like if someone could assist with that?

1 answer

1 accepted

0 votes
Answer accepted

If the current field returns "duration", then you cannot make it return anything other than that - time. Notice that the script probably returns a number (long). The 'duration' template then takes your script's value, and transforms it into a human-readable format.

You could switch it from duration to something generic such as text - and then format your current duration to a specific date format, alongside the user who did the last transition; OR, as you say, have 2 separate fields, one for duration, another for the user.

I am a bit confused though because if you have "duration", then it can only show a single duration. At the same time, you mention multiple statuses.

Can you paste the script in a code block so we know what it actually does?

Getting the user doing the last transition is not hard at all. It probably already traverses issue history, in a sorted way, so all you'd need to do is get the first(or last) changehistory record from the collection, depending on how it's sorted.

Hi @Radek Dostál ,

 

So it is multiple custom fields I created and shows the time an issue was in that status. I.e. - the script below shows how long it was "in progress" and I have a few others and just change the def inProgressName =

I did a few custom fields as I was not sure if you could combine it?

Script:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.history.ChangeItemBean

def changeHistoryManager = ComponentAccessor.getChangeHistoryManager()

def inProgressName = "In Progress"

List<Long> rt = [0L]
def changeItems = changeHistoryManager.getChangeItemsForField(issue, "status")
changeItems.reverse().each { ChangeItemBean item ->
def timeDiff = System.currentTimeMillis() - item.created.getTime()
if (item.fromString == inProgressName) {
rt << -timeDiff
}
if (item.toString == inProgressName) {
rt << timeDiff
}
}

def total = rt.sum() as Long
return (total / 1000) as long ?: 0L

 

What I would like is to show the name of the user who made that transition and for how long it was in that status.

 

I know that you can see that on the Transitions tab on the screen but I cant export that information 

Well, okay, I've tried to combine this so that it returns the "same" duration (a little different way to get there but it's the same data at the end of the day), along with who the first user to transition into the status is.

 

The main difference is that this returns String, therefore the field output template must be Text.

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.changehistory.ChangeHistory
import com.atlassian.jira.issue.changehistory.ChangeHistoryManager
import com.atlassian.jira.issue.history.ChangeItemBean
import java.time.Duration

ChangeHistoryManager changeHistoryManager = ComponentAccessor.getChangeHistoryManager()

final String STATUS_NAME = "In Progress"

List<Long> rt = new LinkedList<>()
rt.add(0L) //So that the list is not empty as we're going to sum the elements at the end

//The username variable for when we find it in ChangeHistory
String username = null

/*
Previously, this was using changeHistoryManager.getChangeItemBeans(issue, "status")
-- this returns ChangeItemBeans, however they do not have the "author" data on it
For this reason this is instead using changeHistoryManager.getChangeHistories(issue)
-- this returns us the history records, including the author, we just need to traverse the ChangeItemBeans per ChangeHistory
*/

List<ChangeHistory> changeHistories = changeHistoryManager.getChangeHistories(issue)
for (ChangeHistory changeHistory : changeHistories.reverse()) { //reversed order going from most recent to oldest
List<ChangeItemBean> changeItemBeanList = changeHistory.getChangeItemBeans()
for (ChangeItemBean changeItemBean : changeItemBeanList) {
if (changeItemBean.getField() == "status") {
long timeDiff = System.currentTimeMillis() - changeItemBean.getCreated().getTime()
if (changeItemBean.getFromString() == STATUS_NAME) rt.add(-timeDiff)
if (changeItemBean.getToString() == STATUS_NAME) {
rt.add(timeDiff)
username = changeHistory.getUsername() //Not ideal as this var constantly gets updated, but.. we are afterall traversing all change histories in reverse, so
}
}
}
}

long total = (long) rt.sum()
Duration duration = Duration.ofMillis(total)

return total != 0L ? reformatDurationToString(duration) + ", "+username : null

String reformatDurationToString(Duration duration) {
// Normally, we don't need anything with dates. However, Jira by default uses 8h = 1d conversion, so if we are
// staying true to that, we just multiply the days by 3, as long as we also apply the same logic with hours
long days = duration.toDays() *3

// Again, using Jira's default 8h = 1d logic, we cannot just return e.g. 17 hours, because this effectively for Jira means, 2 days, 1 hour
// If we use just .toHours(), we can get e.g. 255 hours
// If we use .toHoursPart(), it effectively does % 24; meaning 15 hours; and we use this because we already have days so we just want remaining hours
int hours = duration.toHoursPart()
if (hours > 16) {
days = days +2
hours = hours % 8
}
else if (hours == 16) {
days = days +2
hours = 0
}
else if (hours > 8) {
days++
hours = hours % 8
}
else if (hours == 8) {
days++
hours = 0
}
else {
//nothing, if we're 0-8 then we are within 8h/1d time tracking
}

int minutes = duration.toMinutesPart()

if (days != 0L) return days+" days, "+hours+" hours, "+minutes+" minutes"
else if (hours != 0) return hours+" hours, "+minutes+" minutes"
else return minutes+" minutes"
}

 

And what this results in in the end is something along the lines of:

image.png

or

image.png

 

It can use maybe a change or two but, it does both, the duration and the user, as perhaps an example, albeit I think it can be optimized and shortened a bit.

If you prefer the 24hour per day format then it's just one function to modify a bit.

 

Alternatively, if you decide you want just the user, as a separate field, then we can optimize the ChangeHistory lookup a little, because it no longer requires to be reversed going from recent to old, so it can just find the first match instead:

String username = null
List<ChangeHistory> changeHistories = changeHistoryManager.getChangeHistories(issue)
for (ChangeHistory changeHistory : changeHistories) { //no longer need to reverse, we need first occurrence only
List<ChangeItemBean> changeItemBeanList = changeHistory.getChangeItemBeans()
for (ChangeItemBean changeItemBean : changeItemBeanList) {
if (changeItemBean.getField() == "status") {
if (changeItemBean.getToString() == STATUS_NAME) {
username = changeHistory.getUsername()
break
}
}
}
if (username != null) break
}
return username

 

I hope I'm not totally off here, if I understood this, or at least it's a step closer.

Hi @Radek Dostál , you have understood it perfectly...

Thank you so much! 

 

Hi @Radek Dostál , sorry to bother you again but can it be shortened to display the days and hours?

Minutes is not really needed. Thanks

Suggest an answer

Log in or Sign up to answer
TAGS
Community showcase
Published in Confluence

An update on Confluence Cloud customer feedback – June 2022

Hi everyone, We’re always looking at how to improve Confluence and customer feedback plays an important role in making sure we're investing in the areas that will bring the most value to the most c...

188 views 1 3
Read article

Community Events

Connect with like-minded Atlassian users at free events near you!

Find an event

Connect with like-minded Atlassian users at free events near you!

Unfortunately there are no Community Events near you at the moment.

Host an event

You're one step closer to meeting fellow Atlassian users at your local event. Learn more about Community Events

Events near you