Hi all! i am trying to run the code from the link
https://library.adaptavist.com/entity/count-the-time-an-issue-was-in-a-particular-status
But I am getting time "0"
Hi @Alex
I've tested the code in my environment and do not seem to be encountering any issues. I can get the expected result, as shown in the image below:-
I've used the same code to test, with a minor modification to the Issue Type as shown below:-
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.history.ChangeItemBean
// Status to be counted
final statusName = 'In Progress'
def changeHistoryManager = ComponentAccessor.changeHistoryManager
def totalStatusTime = [0L]
// Every status change is checked
def changeItems = changeHistoryManager.getChangeItemsForField(issue, 'status')
changeItems.reverse().each { ChangeItemBean item ->
def timeDiff = System.currentTimeMillis() - item.created.time
// Subtract time if the "from" status is equal to the status to be checked and from and to statuses are different.
// This allows to count the time the issue is in the state for the first time
if (item.fromString == statusName && item.fromString != item.toString) {
totalStatusTime << -timeDiff
}
// Add time if the "to" status is equal to the status to be checked
if (item.toString == statusName) {
totalStatusTime << timeDiff
}
}
def total = totalStatusTime.sum() as Long
// Every time (added or subtracted) is summed and divided by 1000 to get seconds
(total / 1000) as long ?: 0L
Could you please provide a screenshot of the issue history as shown below:-
Thank you and Kind regards,
Ram
@Ram Kumar Aravindakshan _Adaptavist_ hi! Thank you ! it's worked!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Ram Kumar Aravindakshan _Adaptavist_ hello , I improved my counter a little , but now unfortunately I can not understand why tasks are not searched through jql search . by the "Fail" flag. Please could you help me understand what could be wrong here. Thank you.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Are you setting the Counter Status field in your improved code?
Could you share the improved code?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Tom Lister hi! Yes of course!
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue
import com.custom.SLA
//sla
slaConstraint = Date.parse('yyyy-MM-dd HH:mm:ss.SSS', '2015-11-16 00:00:00.000').toTimestamp()
created = issue.getCreated()
if (created < slaConstraint) return null;
switch (issue.getProjectObject().key){
// case ['MRCHNT']:
// lim = 90
// break
// case ['TSP']:
// lim = 180
// break
case ['TEST']:
lim = 5
break
}
statusHistory = getStatusHistory(issue)
inProgressExistance = statusHistory.findAll{it.toString == 'In Progress'}.created
if (!lim || !inProgressExistance) return null;
now = new Date().toTimestamp()
switch (issue.getProjectObject().key){
// case ['MRCHNT']: //8-20 msk
// startOfDay = 8
// endOfDay = 20
// break
// case ['TSP']: //8-20 msk
// startOfDay = 9
// endOfDay = 18
// break
case ['TEST']: //8-20 msk
startOfDay = 9
endOfDay = 20
break
}
open = []
started = 0
//open += issue.getCreated()
closed = []
//parse history
for (curstep in statusHistory) {
switch (curstep.getToString().toString()) {
case ["In Progress"]:
if (started == 1) closed.push(curstep.getCreated());
started = 1
open.push(curstep.getCreated())
break
default:
if (started == 1) {
started = 0
closed.push(curstep.getCreated())
}
break
}
}
if (open.size - closed.size == 1) closed += now
workTime = [0]
arrayCounter = 0
open = open.sort()
closed=closed.sort()
for (openTime in open) {
sla = new SLA()
workTime[0] += sla.get_work_mins(openTime,closed[arrayCounter],startOfDay,endOfDay,issue.getKey())
arrayCounter++
sla = null
}
//if time is over - fail
if (isNegative(workTime,lim)) setSLA(issue,'Fail');
else setSLA(issue,'Ok');
//показать html
return getDisplayText(workTime,lim)
def getStatusHistory(issue) {
changeHistoryManager = ComponentAccessor.getChangeHistoryManager();
history = changeHistoryManager.getChangeItemsForField(issue, 'status');
return history;
}
def getFormattedTime(time) {
hoursInDay = 12
res = '';
int days = 0;
int hrs = 0;
int mns = 0;
for (one_time in time) {
hrs = one_time/60
mns = one_time - hrs*60
if (one_time >= 0) res += '[' + hrs + 'h ' + mns + 'm]';
else res += '[-' + Math.abs(hrs) + 'h ' + Math.abs(mns) + 'm]';
}
return res
}
def getDisplayText(timeSpent,lim) {
timeLeft = []
for (interation in timeSpent) {
timeLeft += lim - interation
}
if (isNegative(timeLeft)) {
fontColor = 'red'
slaStatus = 'Fail'
}
else {
fontColor = 'green'
slaStatus = 'Ok'
}
displayText = '<font color=' + fontColor + '><b>' + slaStatus + ': ' + getFormattedTime(timeSpent) + '</b><br /></font>Time left: ' + getFormattedTime(timeLeft)
return displayText
}
def isNegative(values) {
for (value in values) {
if (value < 0) return true;
}
return false;
}
def isNegative(values,lim) {
timeLeft = []
for (interation in values) {
timeLeft += lim - interation
}
return isNegative(timeLeft);
}
def setSLA(issue,value) {
fieldName = 'Counter status'
customFieldManager = ComponentAccessor.getCustomFieldManager();
customField = customFieldManager.getCustomFieldObjectByName(fieldName);
if (customField.getValue(issue).toString() != value) {
fieldConfig = customField.getRelevantConfig(issue)
optionsManager = ComponentAccessor.getOptionsManager()
option = optionsManager.getOptions(fieldConfig).find {it.value == value}
changeHolder = new DefaultIssueChangeHolder();
customField.updateValue(null, issue,new ModifiedValue(issue.getCustomFieldValue(customField),option), changeHolder);
}
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Ram Kumar Aravindakshan _Adaptavist_ Perhaps there is a problem with updating this field in jira ? Because when I put the search on "Ok" (the choice in the field Counter status - "Ok" is the default) , then the search is performed and finds this ticket.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Alex
Have you tried re-indexing your instance before running the JQL Query? If not, please give it a try and see if there is any improvement.
Thank you and Kind regards,
Ram
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Ram Kumar Aravindakshan _Adaptavist_ re-indexing did its job, but I can't start indexing with every new task. Now I created a new ticket, and it no longer worked in the search results
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Alex
are you running this in a workflow post function?
If so, ensure it is not the last step. It should before any final update and issue reindex actions
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Tom Lister no, re-indexing the whole jira application. In the workflow, everything is by default
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
So you are not running within a workflow transition?
If you are running in the console, try adding this code in to reindex the issue
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.component.ComponentAccessor
import org.apache.log4j.Category
def issueIndexingService = ComponentAccessor.getComponent(IssueIndexingService)
Issue issue = issueManager.getIssueObject("KEY-999");
issueIndexingService.reIndex(issueManager.getIssueObject(issue.id));
There may be an issue if indexing is already running. There is a test for that, can't remember the exact call.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Alex
I compared your latest code against the code you took from the Adaptavist Library, and both are very different.
The first example is for a Scripted Field. Can you please confirm if the latest code you are using is also for a Scripted Field or for a Post-Function?
If it is for the latter, can you please share a screenshot of your Post-Function configuration?
Thank you and Kind regards,
Ram
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Ram Kumar Aravindakshan _Adaptavist_
this issue (test-34) finds
JQL = project = TEST AND "Counter status" = Ok
(default value for cF "Counter status" - "Ok")
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Ram Kumar Aravindakshan _Adaptavist_ i assume jira itself is not updating..
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Alex
If you can see the expected value when you view the ticket then it has updated the issue.
When you perform a JQL search the data read is from the Lucene indexes. Normal operations keep the indexes in sync but they can get out of alignment when you are running your own operations. As you are finding with your script.
We are still not clear where you are running your script from. Or where you will finally run it. Different contexts behave differently.
Try triggering the indexing within your code using :
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.component.ComponentAccessor
import org.apache.log4j.Category
/// all your existing code
def issueIndexingService = ComponentAccessor.getComponent(IssueIndexingService)
Issue issue = issueManager.getIssueObject("KEY-999");
issueIndexingService.reIndex(issueManager.getIssueObject(issue.id));
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Oliver
I've just seen the screenshot showing you are running the code in a scripted field.
The field is time in status and you are setting the calculated value.
The Closed Status is being set as an additional process. I don't advise this approach as it pushes the envelope on the purpose of scripted fields.
Your value for Time in Status and Closed Status are only valid when the field is requested and the script is triggered. So the Closed Status may not be up to date when used in any filters.
Also you will need to 'force' the indexing on the updated issue in this context. Jira will not be aware of the need to sync the index.
Have you considered using an SLA plugin to track and warn on time in status?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Alex
I have gone through the latest code you shared, and I have noticed many mistakes in it.
Firstly there are many variables that you have declared without defining the type. For example:-
slaConstraint = Date.parse('yyyy-MM-dd HH:mm:ss.SSS', '2015-11-16 00:00:00.000').toTimestamp()
Instead, it should be declared as follows:-
def slaConstraint = Date.parse('yyyy-MM-dd HH:mm:ss.SSS', '2015-11-16 00:00:00.000').toTimestamp()
The language used is not Python, where you can just declare a variable without specifying the type.
It is Groovy, where you need to either specify the type for the variable or use the def keyword and let Groovy set the type.
Secondly, there is no need to use semicolons. In Groovy, semicolons are ignored.
Also, in this code:-
switch (issue.getProjectObject().key){
case ['TEST']:
lim = 5
break
}
....
....
if (!lim || !inProgressExistance)
return null;
I see the usage of the variable lim. Where has this been declared?
In addition to that, where have the variables fontColor, and slaStatus been declared, i.e.:-
def displayText = '<font color=' + fontColor + '><b>' + slaStatus + ': ' + getFormattedTime(timeSpent) + '</b><br /></font>Time left: ' + getFormattedTime(timeLeft)
return displayText
These variables cannot be found, which will cause an error, eventually resulting in a problem in the Scripted Field.
Even if these variables are added to the class you are importing, as shown below, you still need to declare them before you can use them.
import com.custom.SLA
Another point to note, when you add your custom methods, it is best to set them as static type.
I suggest that you clean up your code first and retest it.
Thank you and Kind regards,
Ram
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Ram Kumar Aravindakshan _Adaptavist_ Hi! Thank you for the tip! Could you advise me, how do i add "re-index function" to my code so that i don't run it from window every time .the solution in this link worked for me. Tasks were re-indexed and began to appear in the search JQL . Can this be added to my code somehow?
https://docs.adaptavist.com/sr4js/latest/features/built-in-scripts/re-index-issues
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
import com.atlassian.jira.issue.index.IssueIndexingService
import com.atlassian.jira.component.ComponentAccessor
import org.apache.log4j.Category
/// all your existing code
def issueIndexingService = ComponentAccessor.getComponent(IssueIndexingService)
Issue issue = issueManager.getIssueObject("KEY-999");
issueIndexingService.reIndex(issueManager.getIssueObject(issue.id));
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Tom Lister Hi!! Thank you, I added the code that you attached, but for some reason I get an error
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Alex
It's because there is no explicit import for class Issue.
But you can remove that line and ignore it. You already have access to the 'issue' object in the context for this script.
so add the request before you end and return the field value
def issueIndexingService = ComponentAccessor.getComponent(IssueIndexingService)
issueIndexingService.reIndex(issueManager.getIssueObject(issue.id));
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Tom Lister unfortunately no change. The task also did not change in the search.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
This screen looks like you have updated Counter Status to Fail but the indexes have still not updated so find the issue as value 'Ok'.
Where are you triggering the indexing in your code? Can you attach it as a text file so it's easier for me to read in an IDE?
Indexing may not run if another indexing process is already running. Is that the case?
Tom
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Tom Lister I added the code to google drive https://drive.google.com/drive/folders/1xPmWrKHTCv-SUh7vO6J0hzdwmYXJoOFA?usp=sharing
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
HI @Alex
The reindexing is at the end of the code after the function definitions. I don't think it can be reached there as your code execution will finish at the return
return getDisplayText(workTime,lim)
Try placing the reindex request just before that statement, before you end and return the field value
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
alternatively place at the end of the setSLA(issue,value) function
e.g.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Tom Lister add code in setSLA(issue,value) function
log
2022-10-25 15:37:38,569 ERROR [customfield.GroovyCustomField]: *************************************************************************************
2022-10-25 15:37:38,571 ERROR [customfield.GroovyCustomField]: Script field failed on issue: TEST-40, field: Time in status
groovy.lang.MissingPropertyException: No such property: issueManager for class: Script974
at Script974.setSLA(Script974.groovy:148)
at Script974.run(Script974.groovy:82)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Alex
You probably don't need that as you have the issue already
issueIndexingService.reIndex(issue)
def issueManager = ComponentAccessor.getIssueManager()
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Tom Lister the final setSLA() function should be like this ?
def setSLA(issue,value) {
fieldName = 'Counter status'
customFieldManager = ComponentAccessor.getCustomFieldManager()
customField = customFieldManager.getCustomFieldObjectByName(fieldName)
if (customField.getValue(issue).toString() != value) {
fieldConfig = customField.getRelevantConfig(issue)
optionsManager = ComponentAccessor.getOptionsManager()
option = optionsManager.getOptions(fieldConfig).find {it.value == value}
changeHolder = new DefaultIssueChangeHolder()
customField.updateValue(null, issue,new ModifiedValue(issue.getCustomFieldValue(customField),option), changeHolder)
issueIndexingService.reIndex(issue)
def issueManager = ComponentAccessor.getIssueManager()
}
if so then I get an error and after two minutes (the time given is "ok" 2 min) the counter disappears completely from the ticket
log
2022-10-25 16:09:54,904 ERROR [customfield.GroovyCustomField]: ************************************************************************************* 2022-10-25 16:09:54,909 ERROR [customfield.GroovyCustomField]: Script field failed on issue: TEST-41, field: Time in status groovy.lang.MissingPropertyException: No such property: issueIndexingService for class: Script987 at Script987.setSLA(Script987.groovy:147) at Script987.run(Script987.groovy:82)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
you have omitted the line
def issueIndexingService = ComponentAccessor.getComponent(IssueIndexingService)
the line
def issueManager = ComponentAccessor.getIssueManager()
has no effect on any processing in this case
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
HI @Alex
It may help to put in some log statements to show the values being obtained during the process .
What is the history of your TEST-10 item with status TO DO?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Tom Lister hi!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi @Alex
The field type template should be Duration but that isn't the issue.
If I dry run this in my head, Your loop should process two events. Each of which will match one of the given conditions. So the net effect is one pass will add -timeDiff and the other will add +timeDiff. So giving a zero result.
As I mentioned you would have to add log statements to see some confirming debug info. I don't think this logic will work for this test case if any.
I've tagged adaptavist so hopefully they will jump in. If I had a running server instance I would try to recreate it.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Online forums and learning are now in one easy-to-use experience.
By continuing, you accept the updated Community Terms of Use and acknowledge the Privacy Policy. Your public name, photo, and achievements may be publicly visible and available in search engines.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.