Skip to main content
Skip table of contents

How to use Scriptrunner to access EpicSumUp Data

Epic Sum Up provides its user with a Java API. You can use this API to extract data from Epic Sum Up to use with other business components however needed. 

This enables vast varieties of customisation.

To access this data you can use a script extension like ScriptRunner within your Jira instance. 

General

To Access our Java API in ScriptRunner, you can follow the ScriptRunner Documentation

We use the following annotation

GROOVY
@WithPlugin("aptis.plugins.epicSumUp")

The available @PluginModule can be found in our API Documentation

Basic CustomField Examples

Calculate time spent for an issue and all of its children

ProgressTimeResult is used to access values that are specifically used as time values.

Custom Script Field

Template

Text Field

Code

GROOVY
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.user.ApplicationUser
import com.onresolve.scriptrunner.runner.customisers.PluginModule
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import aptis.plugins.epicSumUp.api.ProgressProvider
import aptis.plugins.epicSumUp.api.model.ProgressTimeResult
@WithPlugin("aptis.plugins.epicSumUp")
@PluginModule
ProgressProvider progressProvider

ApplicationUser currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
ProgressTimeResult progress = progressProvider.getTimeProgress(
	issue,
	currentUser
)
return (long)progress.getTimeSpent()

Calculate StoryPoints for an issue and all of its children

The ProgressNumberWithStatusResult can be used to process the results of number custom fields (e.g. StoryPoints).

In contrast to ProgressResolutionResultProgressNumberWithStatusResult will return a decimal number and even a budget, if possible.

Custom Script Field

Template

Text Field

Code

GROOVY
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.user.ApplicationUser
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.fields.CustomField
import com.onresolve.scriptrunner.runner.customisers.PluginModule
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import aptis.plugins.epicSumUp.api.ProgressProvider
import aptis.plugins.epicSumUp.api.model.ProgressNumberWithStatusResult
@WithPlugin("aptis.plugins.epicSumUp")
@PluginModule
ProgressProvider progressProvider

CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager()
ApplicationUser currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
CustomField storyPointsField = customFieldManager.getCustomFieldObject("customfield_10106")

ProgressNumberWithStatusResult progress = progressProvider.getNumberProgress(
	issue,
	storyPointsField,
	currentUser
)

return (double)progress.getResolved()

Calculate number of resolved issues for an issue and all of its children

The ProgressResolutionResult is a class that can be used to display the progress of issues. It contains the amount of issues as well as the number of resolved or unresolved issues.

Custom Script Field

Template

Text Field

Code

GROOVY
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.user.ApplicationUser
import com.onresolve.scriptrunner.runner.customisers.PluginModule
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import aptis.plugins.epicSumUp.api.ProgressProvider
import aptis.plugins.epicSumUp.api.model.ProgressResolutionResult
@WithPlugin("aptis.plugins.epicSumUp")
@PluginModule
ProgressProvider progressProvider

ApplicationUser currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
ProgressResolutionResult progress = progressProvider.getProgressByResolution(
	issue,
	currentUser
)

return progress.getResolved()

Render the progressBar as SVG (Server Side)

By using the ProgressBarRenderer you can use our progress bars outside of the Jira system or where our client side rendered progress bars don’t work out of the box.

Custom Script Field

Template

Text Field

Code

GROOVY
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.user.ApplicationUser;
import com.onresolve.scriptrunner.runner.customisers.PluginModule
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import aptis.plugins.epicSumUp.api.ProgressProvider
import aptis.plugins.epicSumUp.api.ProgressBarRenderer
import aptis.plugins.epicSumUp.api.model.ProgressTimeResult
@WithPlugin("aptis.plugins.epicSumUp")
@PluginModule
ProgressProvider progressProvider
@PluginModule
ProgressBarRenderer progressBarRenderer

ApplicationUser currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
ProgressTimeResult progress = progressProvider.getTimeProgress(
    issue,
    currentUser
)
  
return progressBarRenderer.renderAsSvg(progress, 200, 20)

Advanced Examples

Cost calculation base on timeSpent * Cost Factor + Number Fields

Calculate costs for the Issue and all of its children based on time spent and a custom field that contains additional costs.

Be aware that this script is not designed to run in structures with 1000+ child issues. Please contact our support when this is required.

Custom Script Field

Template

Text Field

Code

CODE
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.fields.CustomField;
import com.onresolve.scriptrunner.runner.customisers.PluginModule
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import aptis.plugins.epicSumUp.api.HierarchyProvider
@WithPlugin("aptis.plugins.epicSumUp")
@PluginModule
HierarchyProvider hierarchyProvider
  
CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager();
ApplicationUser currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser();

// define work costs factor ($ / hour)
Double timeCostPerHour = 80.5
// define customfield of type number that contains additional costs in $
CustomField costsField = customFieldManager.getCustomFieldObject("customfield_10525") //Number field that contains e.g. costs in $
if (costsField == null) {
    throw new Exception("costs field does not exist")
}

List<Issue> issues = hierarchyProvider.getChildIssues(issue, currentUser);
issues.add(issue);
  
def value = issues.sum { issueItem ->
    // Costs calculation
    // timeSpent * timeCostPerHour + addionalCosts
    Long timeSpent = issueItem.getTimeSpent() ?: 0
    Double timeSpentInHours = timeSpent / 60 / 60
    Double timeCost = timeSpentInHours * timeCostPerHour
    Double cost = costsField.getValue(issueItem) ?: 0
    
    cost + timeCostPerHour
}
  
return value + "\$"

Time Lozenges optical warning

This example will render different colored lozenge based on time spent and estimates

Lozenge Documentation

  • Grey Background: When there is less than 10m time spent

  • Red Background: When more than 1h is already spent over the original estimate

  • White Background, Red Text: When more than 1h is remaining over the original estimate

  • Green Background: When no above condition is true

Example Output:

Custom Script Field

Template

Custom

Template

CODE
#if (!$value || $value.getTimeSpent() < 600)
    <span class="aui-lozenge aui-lozenge-subtle aui-lozenge-default">
        $jiraDurationUtils.getShortFormattedDuration($value.getTimeSpent())
    </span>
#elseif ($value && $value.getTimeSpentOverEstimate() > 3600)
    <span class="aui-lozenge aui-lozenge-error">
        $jiraDurationUtils.getShortFormattedDuration($value.getTimeSpentOverEstimate())
    </span>
#elseif ($value && $value.getRemainingTimeOverEstimate() > 3600)
    <span class="aui-lozenge aui-lozenge-subtle aui-lozenge-error">
        $jiraDurationUtils.getShortFormattedDuration($value.getRemainingTimeOverEstimate())
    </span>
#else
    <span class="aui-lozenge aui-lozenge-success">
        $jiraDurationUtils.getShortFormattedDuration($value.getTimeSpent())
    </span>
#end

Code

CODE
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.user.ApplicationUser;
import com.onresolve.scriptrunner.runner.customisers.PluginModule
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import aptis.plugins.epicSumUp.api.ProgressProvider
import aptis.plugins.epicSumUp.api.model.ProgressTimeResult
@WithPlugin("aptis.plugins.epicSumUp")
@PluginModule
ProgressProvider progressProvider
   
ApplicationUser currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser();
ProgressTimeResult progress = progressProvider.getTimeProgress(
    issue,
    currentUser
);
   
return progress

Searchable Duration: TimeSpentOverEstimation

Create a JQL searchable custom field that contains the time spent over the original estimate of the issue and all of its children.

It may cause performance issues as it will reindex all parent issues (Epic or Portfolio Hierarchy) when a child is updated. Only use with caution and reduce context (Project / IssueTypes) as much as possible.

Instead of a Custom Event Handler, you can use a custom scheduled job to re-index the relevant issues (epic) only at specific intervals.

Scriptrunner Documentation

Custom Script Field

Template

Duration(time-tracking)

Code

GROOVY
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.user.ApplicationUser;
import com.onresolve.scriptrunner.runner.customisers.PluginModule
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import aptis.plugins.epicSumUp.api.ProgressProvider
import aptis.plugins.epicSumUp.api.model.ProgressTimeResult
@WithPlugin("aptis.plugins.epicSumUp")
@PluginModule
ProgressProvider progressProvider
  
ApplicationUser currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser();
  
ProgressTimeResult progress = progressProvider.getTimeProgress(
    issue,
    currentUser
);
  
return progress.getTimeSpentOverEstimate()
Custom Listener

Events

  • Issue Updated

  • Work Logged On Issue

  • Issue Worklog Updated

  • Issue Worklog Deleted

Script

CODE
import org.apache.log4j.Logger
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.index.IssueIndexManager
import com.atlassian.jira.user.ApplicationUser;
import com.onresolve.scriptrunner.runner.customisers.PluginModule
import com.onresolve.scriptrunner.runner.customisers.WithPlugin
import aptis.plugins.epicSumUp.api.HierarchyProvider
@WithPlugin("aptis.plugins.epicSumUp")
@PluginModule
HierarchyProvider hierarchyProvider
 
Issue issue = event.issue // event is an IssueEvent
ApplicationUser currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser();
def issueIndexManager = ComponentAccessor.getComponent(IssueIndexManager)
 
Issue[] parentIssues = hierarchyProvider.getParents(issue, currentUser)
issueIndexManager.reIndexIssueObjects(parentIssues.collect())

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.