Epic rolled up data

Rahul Aich [Nagra]
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 1, 2013

Hi

We use Agile in our organisation. And one of the metrics that our PMs are interested in is to track the epic progress in units of time spent, time remaining and original estimate of all the stories underneath the epic.

filter results obtained by adding the metric Sigma(Time spent) and Sigma(Remaining time) is what I need but this only rolls up data for sub-tasks. Not for issue underneath the Epic.

In short, say I have two stories in my epic (story-1, 2 hrs remaining, and story-2, 4 hours remainaing) , i want to see 6 hours under the Sigma( remaining time) when I query my epic name)

Any idea how i can achieve this.

Rahul

4 answers

5 votes
Matthew Beda November 15, 2018

Late But still relevant. (uses Scriptrunner Scripted Field)

Makes a Progress bar for Percent Complete based on "Original Estimate/Logged Work to Tasks and Sub Tasks" And adds in a Details Panel showing specific breakdown for Tasks under Epic.

Epic Time Sum UP.png

import com.atlassian.jira.ComponentManager
import com.atlassian.jira.component.ComponentAccessor

def componentManager = ComponentManager.getInstance()
def issueLinkManager = ComponentAccessor.getIssueLinkManager()

def totalTimeForSubtasksEstimated = 0
def totalTimeSpentForSubs = 0
def url = ComponentAccessor.getApplicationProperties().getString("jira.baseurl")
def dialog = '<a onclick="AJS.dialog2(\'#more-details'+issue.key+'\').show();" id="show-details'+issue.key+'">Details</a><section class="aui-dialog2 aui-dialog2-medium" role="dialog" aria-hidden="true" style="display:none;" id="more-details'+issue.key+'">'+
"<header class='aui-dialog2-header'><h2 class='aui-dialog2-header-main'>Time Log Details "+issue.key+
"</header> <div class='aui-dialog2-content'><table class='aui' style='border-collapse:collapse;'><thead><th>key</th><th>summary</th><th>Time Tracking</th></thead>"
issueLinkManager.getOutwardLinks(issue.id)?.each
{
outLink ->
def firstLevelChildIssue = outLink.destinationObject

totalTimeForSubtasksEstimated += firstLevelChildIssue.originalEstimate ?: 0
totalTimeSpentForSubs += firstLevelChildIssue.timeSpent ?: 0
def origEstimateshort = ((firstLevelChildIssue.originalEstimate ?: 0) ? ComponentAccessor.getJiraDurationUtils().getFormattedDuration(firstLevelChildIssue.originalEstimate ?: 0) : 0)
def timeSpentshort = ((firstLevelChildIssue.timeSpent ?: 0) ? ComponentAccessor.getJiraDurationUtils().getFormattedDuration(firstLevelChildIssue.timeSpent ?: 0) : 0)
dialog += "<tr><td style='padding:4px; white-space:nowrap;'><a href='"+url+"/browse/"+firstLevelChildIssue.key+"'>"+firstLevelChildIssue.key+"</a></td><td style='border-right:1px solid #e0e0e0'>"+firstLevelChildIssue.summary+":</td><td><span style='white-space:nowrap;'>"+timeSpentshort +" / "+origEstimateshort+"</span></td></tr>"
if (outLink.getIssueLinkType().getName().equals("Epic-Story Link"))
{
// Process normal Epics-Story/Bug-Subtask structure
issueLinkManager.getOutwardLinks(firstLevelChildIssue.id)?.each
{
outLinkSubtask ->
def secondLevelChildIssue = outLinkSubtask.destinationObject
if (secondLevelChildIssue.isSubTask())
{
totalTimeForSubtasksEstimated += secondLevelChildIssue.originalEstimate ?: 0
totalTimeSpentForSubs += secondLevelChildIssue.timeSpent ?: 0
}
}
}
}
def origEstimate = (totalTimeForSubtasksEstimated ? ComponentAccessor.getJiraDurationUtils().getFormattedDuration(totalTimeForSubtasksEstimated) : 0)
def timeSpent = (totalTimeSpentForSubs ? ComponentAccessor.getJiraDurationUtils().getFormattedDuration(totalTimeSpentForSubs) : 0)
def precentComplete = 0
if (origEstimate != 0 && timeSpent != 0){
precentComplete = totalTimeSpentForSubs/totalTimeForSubtasksEstimated*100
} else {
precentComplete = 0.01
}
def simplePrecent = 0
String doubleAsString = String.valueOf(precentComplete);
int indexOfDecimal = doubleAsString.indexOf(".");
if (indexOfDecimal != -1){
simplePrecent = doubleAsString.substring(0, indexOfDecimal);
}

dialog += "<tr style='font-weight:bold !important;'><td colspan='2' style='border-top:1px solid #ccc;text-align:right;'>Totals:</td><td style='border-top:1px solid #ccc;white-space:nowrap;'><span>"+timeSpent+"</span> / <span>"+origEstimate+"</span></td><tr></table>"
dialog += '</div><footer class="aui-dialog2-footer"><button id="close-details'+issue.key+'" onclick="AJS.dialog2(\'#more-details'+issue.key+'\').hide();" class="aui-button aui-button-primary">Close</button></section>'
def table = "<div style='display:inline-block; width:50%;max-width:100px;'>"+simplePrecent+"% Complete</div>"
table += "<div style='display:inline-block;font-size:12px;font-weight:bold;text-align:right;max-width:150px; width:50%;'><span>"+timeSpent+"</span> / <span>"+origEstimate+"</span></div>"
table += "<table style='background:#ffffff;text-align:center;border-radius:8px;overflow:hidden; width:250px; border:1px solid #ccc;'><tr>"

table += "<td style='padding:2px 6px;height:8px;border-radius:8px;color: white; overflow:hidden;background-color:#A0D201;width:"+precentComplete+"%;'></td>"
table += "<td></td>"
table += "</tr></table>"
return table + dialog
lujmo December 18, 2018

thanks for post @Matthew Beda. I tried it out and it looks good. just one question: it seems to be including linked blocked tasks in the roll up. Is there any way to make it ignore linked tasks and only sum up based on issues in epic and subtasks of those issues ? 

jira epic sum up example.png

Matthew Beda December 18, 2018

you would need to create an exception using the "outLink.getIssueLinkType()" and trap for anything that is "equals("Blockers")"

Like # people like this
Justin Alex Paramanandan
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
May 28, 2019

Works wonders, @Matthew Beda ! Thanks for this.

Do you know how to sort the issues in epic? I noticed that the result is random:

2019-05-28_21-44-50.png

Matthew Beda July 11, 2019

You can probably just run sort() on the list for the child issues.

https://stackoverflow.com/questions/13686659/groovy-custom-sort-a-map-by-value

You could also look into a sort function after the table is generated using some JS

https://stackoverflow.com/questions/22906760/jquery-sort-table-data

Tristan_Willis March 19, 2020

@Matthew Beda I was looking at your script to use for my Jira instance, however for my purposes I was looking at using it to sum up all estimations for a group of epics for another parent issue we created above Epics. What do you think I should do to work  towards this?

2 votes
Steven F Behnke
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
June 5, 2015

I've been looking into this for a while and I just found an add-on that does it as well, and looks customizable to boot.

https://marketplace.atlassian.com/plugins/aptis.plugins.epicSumUp

Or a simpler free one.

https://marketplace.atlassian.com/plugins/aptis.plugins.epicSumUpFree

QMS Icreon December 20, 2016

Thanks a lot. It helped me.

0 votes
Saleem Alikhan December 29, 2014

Hi guys, apologies on the really late answer. Please find the Groovy Field definition for the bits required to enable a custom field that will display the total estimate time for an epic's sub-issues/sub tasks. They have to be linked to the Epic via the Epic-Story link field.

Change the .originalEstimate appropriately to reflect either remaining time or actual time.

Hit me back if you have any further questions. The add-on method uses basically the same query but packaged up nicely in a panel etc with the breakdown.

cheers,

saleem

import com.atlassian.jira.ComponentManager

def componentManager = ComponentManager.getInstance()
def issueLinkManager = componentManager.getIssueLinkManager()

def totalTimeForSubtasksEstimated = 0
issueLinkManager.getOutwardLinks(issue.id)?.each
{
outLink ->
def firstLevelChildIssue = outLink.destinationObject

   if (outLink.getIssueLinkType().getName().equals("Epic-Story Link"))
   {
// Process normal Epics-Story/Bug-Subtask structure
issueLinkManager.getOutwardLinks(firstLevelChildIssue.id)?.each
{
outLinkSubtask ->
def secondLevelChildIssue = outLinkSubtask.destinationObject
    if (secondLevelChildIssue.isSubTask())
{
totalTimeForSubtasksEstimated += secondLevelChildIssue.originalEstimate ?: 0
}
}
}
}
return (totalTimeForSubtasksEstimated ? componentManager.getJiraDurationUtils().getFormattedDuration(totalTimeForSubtasksEstimated) : null)
0 votes
Saleem Alikhan December 2, 2013

Hi Rahul,

We have a similar requirement, but I wanted to clarify if you meant you need this information exposed within searches or just for viewing. Currently we use both approaches but they require independant solutions.

For just viewing the relevant data on an Epic when you view it, we ended up creating a custom add-in that displays extra information with the information you ask on the Epic screen. For actually searching, we created some custom Groovy (Script Runner) fields that roll-up the same information and expose it so that it's available in the JIRA Queries.

I can ping you back with more details, but let me know if either of the two scenarios we have match yours. (The Costs are just blanked out for the screenshot)

cheers,

saleem

Paul Clais December 19, 2013

Hello Saleem

I have the exact same question as Rahul, and I guess what we want here is the ability to see the aggregation on the Epic screen but also on any search. So I guess you have this Subtasks Estimate column in each search result. Could you share with us your customizations? I am using JIRA ON DEMAND.

Thanks in advance;
Paul

Saleem Alikhan December 19, 2013

Hi Paul,

Yes, sure, I can send you some more info early next week. I am not super familiar with JIRA On Demand, but I think it has the ability to run the Groovy Script Runner plugin. If you can confirm that, then it's just a matter of setting up the field definitions.

Chat next week!

saleem

ROGER PICKETT January 13, 2014

Hello Saleem,

You reference sub tasks in the example provided. Does this aslo include issues in an epic?

Saleem Alikhan January 27, 2014

Hi Roger, sorry about the delay in getting back to you. Yes, this can include sub-tasks within the Epic itself. We don't allow sub-tasks on Epics as part of our setup, so the screenshot above does not show any, but the code snippet still will work.

Rahul Aich [Nagra]
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
March 9, 2014

HI Saleem

Sorry in replying late to you, yes both these scenarios apply to us and I will be very much interested in the solutions that you have adopted. Can you please send me more information either on this post or directly s rahul.aich@nagra.com

Lasse Kaiser September 17, 2014

Hi Saleem, We also need to be able to aggregate estimated time and spent time at epic level (in views and in searches, particularly in searches). Unless a new and easier solution has been made since March (pls. let me know if that is the case), we are ready to install the Groovy Script Runner, if you are willing to post your field definitions here. Thanks /Lasse

Ram Muthiah December 29, 2014

Hi Saleem, I need the epic level time spent just for viewing. Can you please elaborate on the custom add-in? I can add a custom field to epic level. But, not sure how to make issue level estimates/time-spent to Epic level. Thanks Ram

Suggest an answer

Log in or Sign up to answer