Hi,
one of my company s PO asked me if there is a way to sum up all efforts for a certain epic and all linked stories and sub-tasks.
I searched in the report area of the projects but there are only reports on a user or version basis available. I also tried to find a plugin but found nothing obviuos. I know I could probably do with JQL and MS Excel export, but the PO definetely wants an integrated solution.
We are using Jira Agile and Tempo. I hope that somebody can point me in the right direction. Any help is very appreciated.
Best Regards,
Christian
Hi All,
I actually found a solution myself. I played around with the Script Runner Plugin/Scripted Custom Field - thank you Jamie for this Swiss Army Knife for JIRA :-)
I just wrote a script that crawls over a specific link type ("has Story") linked issues and their sub tasks summing up the time spent fields if any work logs have been provided.
It is a little bit hard-coded at the moment so there still is room for improvement:
import com.atlassian.jira.ComponentManager
def componentManager = ComponentManager.getInstance()
def issueLinkManager = componentManager.getIssueLinkManager()
def totalTimeSpent = 0
def resultHTML = "<ul>"
def epicTimeSpentString = "No Work Logs"
def epicTimeSpent = issue.timeSpent
if(epicTimeSpent) {
epicTimeSpentString = componentManager.getJiraDurationUtils().getFormattedDuration(epicTimeSpent)
totalTimeSpent += epicTimeSpent
}
resultHTML += "<li><b>This " + issue.issueTypeObject.name + ": " + epicTimeSpentString + "</b></li>"
if(issue.getSubTaskObjects().size > 0) {
resultHTML += "<ul>"
issue.getSubTaskObjects().each {epicSubTask ->
def epicSubTaskTimeSpentString = "No Work Logs"
def epicSubTaskTimeSpent = epicSubTask.timeSpent
if(epicSubTaskTimeSpent) {
epicSubTaskTimeSpentString = componentManager.getJiraDurationUtils().getFormattedDuration(epicSubTaskTimeSpent)
totalTimeSpent += epicSubTaskTimeSpent
}
resultHTML += "<li>" + epicSubTask.issueTypeObject.name + " - " + epicSubTask.key + " - " + epicSubTask.summary + " - <b>" + epicSubTaskTimeSpentString + "</b></li>"
}
resultHTML += "</ul>"
}
issueLinkManager.getOutwardLinks(issue.id).each {issueLink ->
if (issueLink.issueLinkType.name == "Epic to User Story") {
def linkedIssue = issueLink.destinationObject
def linkedIssueTimeSpent = linkedIssue.timeSpent
def linkedIssueTimeSpentString = "No Work Logs"
if(linkedIssueTimeSpent) {
linkedIssueTimeSpentString = componentManager.getJiraDurationUtils().getFormattedDuration(linkedIssueTimeSpent)
totalTimeSpent += linkedIssueTimeSpent
}
//resultHTML += "<li>" + issueLink.issueLinkType.name + " - " + linkedIssue.key + " - " + linkedIssueTimeSpentString +"<br/></li>"
resultHTML += "<li>" + linkedIssue.issueTypeObject.name + " - " + linkedIssue.key + " - " + linkedIssue.summary + " - <b>" + linkedIssueTimeSpentString +"</b></li>"
if(linkedIssue.getSubTaskObjects().size > 0) {
resultHTML += "<ul>"
linkedIssue.getSubTaskObjects().each {linkedIssueSubTask ->
def linkedIssueSubTaskTimeSpentString = "No Work Logs"
def linkedIssueSubTaskTimeSpent = linkedIssueSubTask.timeSpent
if(linkedIssueSubTaskTimeSpent) {
linkedIssueSubTaskTimeSpentString = componentManager.getJiraDurationUtils().getFormattedDuration(linkedIssueSubTaskTimeSpent)
totalTimeSpent += linkedIssueSubTaskTimeSpent
}
resultHTML += "<li>" + linkedIssueSubTask.issueTypeObject.name + " - " + linkedIssueSubTask.key + " - " + linkedIssueSubTask.summary + " - <b>" + linkedIssueSubTaskTimeSpentString + "</b></li>"
}
resultHTML += "</ul>"
}
}
}
resultHTML += "</ul><br/>"
def totalTimeSpentString = totalTimeSpent ? componentManager.getJiraDurationUtils().getFormattedDuration(totalTimeSpent) : "No Work Logs"
resultHTML += "<h4>" + totalTimeSpentString + "</h4>"
return resultHTML
I use two variants of this full output script to just provide a formatted string with the calculated sum and a plain number output so that further calculations are possible with escel exports.
I realize that this could produce a lot of output for complex relationships (one epic with a lot of stories and a lot of sub tasks) and that this could be quite permormance consuming. But in general it is doing what was requested and I will have an eye on performance issues. My PO is happy now :-)
Please feel free to re-use and let me know of any improvements! :-)
Best Regards,
Christian
Perfect! I am glad this helped you! :-)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Uh Thanks very much that was fast.
Yeah i figured that this link traversing might have performance issues. But having to count the worklog manually is a even bigger performance issue ;-)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Btw: Worked for me except that our Epic Links have different name. So i had to use the line
issueLink.issueLinkType.name == "Epic-Story Link"
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Perfect! I am glad this helped you! :-)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Riad,
this would be the version with the plain number:
import com.atlassian.jira.ComponentManager
import java.text.DecimalFormat
def componentManager = ComponentManager.getInstance()
def issueLinkManager = componentManager.getIssueLinkManager()
def totalTimeSpent = 0.0d
def epicTimeSpent = issue.timeSpent
if(epicTimeSpent) {
totalTimeSpent += epicTimeSpent
}
if(issue.getSubTaskObjects() != []) {
issue.getSubTaskObjects().each {epicSubTask ->
def epicSubTaskTimeSpent = epicSubTask.timeSpent
if(epicSubTaskTimeSpent) {
totalTimeSpent += epicSubTaskTimeSpent
}
}
}
if(issueLinkManager.getOutwardLinks(issue.id) != []) {
issueLinkManager.getOutwardLinks(issue.id).each {issueLink ->
if (issueLink.issueLinkType.name == "Epic to User Story") {
def linkedIssue = issueLink.destinationObject
def linkedIssueTimeSpent = linkedIssue.timeSpent
if(linkedIssueTimeSpent) {
totalTimeSpent += linkedIssueTimeSpent
}
//if(linkedIssue.getSubTaskObjects().size > 0) {
if(linkedIssue.getSubTaskObjects() != []) {
linkedIssue.getSubTaskObjects().each {linkedIssueSubTask ->
def linkedIssueSubTaskTimeSpent = linkedIssueSubTask.timeSpent
if(linkedIssueSubTaskTimeSpent) {
totalTimeSpent += linkedIssueSubTaskTimeSpent
}
}
}
}
}
}
if(totalTimeSpent > 0) {
totalTimeSpent = totalTimeSpent / 60.0d / 60.0d / 8.0d //Seconds to Hours
}
//formatNumber(number: totalTimeSpent, Locale.ENGLISH, format: '##0.0')
//new DecimalFormat("#.#").format(totalTimeSpent);
totalTimeSpent
Please keep in mind that this can cause high load on your jira instance - so handle with care! Meanwhile I would recomment evaluating the Structure-Plugin or EazyBI.
Kind Regards,
Christian
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
This might excatly what i was looking for. I try it out and give you some feedback how it worked for me.
Any chance you could post your second variant? Since i am lazy ;-)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Riad,
this would be the version with the plain number:
import com.atlassian.jira.ComponentManager
import java.text.DecimalFormat
def componentManager = ComponentManager.getInstance()
def issueLinkManager = componentManager.getIssueLinkManager()
def totalTimeSpent = 0.0d
def epicTimeSpent = issue.timeSpent
if(epicTimeSpent) {
totalTimeSpent += epicTimeSpent
}
if(issue.getSubTaskObjects() != []) {
issue.getSubTaskObjects().each {epicSubTask ->
def epicSubTaskTimeSpent = epicSubTask.timeSpent
if(epicSubTaskTimeSpent) {
totalTimeSpent += epicSubTaskTimeSpent
}
}
}
if(issueLinkManager.getOutwardLinks(issue.id) != []) {
issueLinkManager.getOutwardLinks(issue.id).each {issueLink ->
if (issueLink.issueLinkType.name == "Epic to User Story") {
def linkedIssue = issueLink.destinationObject
def linkedIssueTimeSpent = linkedIssue.timeSpent
if(linkedIssueTimeSpent) {
totalTimeSpent += linkedIssueTimeSpent
}
//if(linkedIssue.getSubTaskObjects().size > 0) {
if(linkedIssue.getSubTaskObjects() != []) {
linkedIssue.getSubTaskObjects().each {linkedIssueSubTask ->
def linkedIssueSubTaskTimeSpent = linkedIssueSubTask.timeSpent
if(linkedIssueSubTaskTimeSpent) {
totalTimeSpent += linkedIssueSubTaskTimeSpent
}
}
}
}
}
}
if(totalTimeSpent > 0) {
totalTimeSpent = totalTimeSpent / 60.0d / 60.0d / 8.0d //Seconds to Hours
}
//formatNumber(number: totalTimeSpent, Locale.ENGLISH, format: '##0.0')
//new DecimalFormat("#.#").format(totalTimeSpent);
totalTimeSpent
Please keep in mind that this can cause high load on your jira instance - so handle with care! Meanwhile I would recomment evaluating the Structure-Plugin or EazyBI.
Kind Regards,
Christian
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Ah - and the Script Runner Plugin is also available ;-)
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.