All eazyBI for Jira eazyBI for Confluence Private eazyBI

Jira calculated and scripted custom fields
eazyBI for Jira

In Jira, you can display data based on computed values to support any number of uses cases. On Atlassian Marketplace you can find several Jira apps that let you create calculated custom fields for issues like Scriptrunner for Jira, Jira Misc custom fields, and others.

This page explains the main principle of calculated custom fields and how to import them in eazyBI, and also contains a few examples of ScriptRunner for Jira and Jira Misc Custom Fields.

These custom fields can be used for dynamic calculations and extended use-cases.

Single-issue picker and multi-issue picker field import

For Jira single-issue picker field, the additional definition in advanced settings are required (where the corresponding custom field ID replaces NNNNN).

[jira.customfield_NNNNN]
data_type = "string"
dimension = true

For Jira multi-issue picker field, use this definition to import all values from the custom field as separate members in a new dimension.

[jira.customfield_NNNNN]
data_type = "string"
dimension = true
multiple_values = true
split_by = ","

Create a new calculated field in Jira

Create a new calculated custom field using your preferred app for Jira. Note that it would require Jira system administrator access rights to create new custom fields.

Please read these documentation pages to learn more about how to create new calculated custom fields for Jira using two popular plugins.

  • ScriptRunner for Jira documentation

    All custom field calculation formulas and advanced settings mentioned in the examples below are supported by Adaptavist ScriptRunner for JIRA (Data Center) and will NOT work in ScriptRunner for JIRA Cloud.

  • Jira Misc Custom Fields documentation 

    All custom field calculation formulas mentioned in the examples below are supported by Jira Misc Custom Fields version 1.x. They might not work if using Jira Misc Custom Fields version 2.0.0 or later because of changes in Jira Misc functionality.

    The principles and advanced settings importing Jira Misc fields into eazyBI are the same for all Jira Misc app version.

After creating a new custom field you should re-index Jira as suggested. Visit issue pages and see if your calculated text field is returning the expected value. If you do not see the custom field on Default Screen it may be caused by exception in the formula.

Please see Jira log file (log/atlassian-jira.log in the home directory or console log) if there are any exceptions logged.

Enable calculated custom field in eazyBI advanced settings

By default, eazyBI detects standard custom fields for issues and you can import them in the cube but calculated custom fields are not recognized automatically for import. These fields should be enabled for import in advanced settings.

If you will define Calculated Date/Time Field or Transition Date/Time Field then they will be recognized by eazyBI as additional custom fields and you will be able to select them in Source Data tab in application import custom field selection.

If you defined Calculated Number Field or Calculated Text Field then you will need to add this field to eazyBI advanced settings. In our "Creator" example at first, you need to find out custom field ID. If you go to Edit Custom Field Details screen then in URL you will see

.../secure/admin/EditCustomField!default.jspa?id=NNNNN

where NNNNN is custom field ID. Then in eazyBI advanced settings add

[jira.customfield_NNNNN]
data_type = "string"

where NNNNN is replaced by corresponding custom field ID.

Import custom field into eazyBI

After the calculated custom field is created and eazyBI advanced settings are updated, you should see your new calculated custom field name in the Source Data tab in application import custom field selection. Select the Import as property option for this custom field. After import, you should see additional "Issue Creator" calculated measure in Measures / Calculated members / Issue properties section and you can use it in reports where you have Issue dimension on rows and expanded to detailed issues.

And also you can access this custom field with [Issue].CurrentMember.get("Creator") in your other eazyBI calculation formula.

Examples

We have gathered a few scripted field examples but you can find more in:

Creator

ScriptRunner for Jira 

This Text Field (multi-line) formula returns the creator of an Issue in the calculated text field. 

def creator = issue.getCreator();
if (!creator ) {
    return;
}
else{
    creator.getDisplayName();
}

Jira Misc Custom Fields

This Custom Text field formula returns the creator of an Issue in the calculated text field.

<!-- @@Formula:
creator = issue.getIssueObject().getCreator();
if (creator == null) return null;
creator.getDisplayName();
-->

eazyBI Advanced settings

To import this field as a dimension, add the following advanced settings (by replacing NNNNN with actual field ID) and then importing this field via eazyBI import settings as dimension.

[jira.customfield_NNNNN]
data_type = "string"
dimension = true

Description

ScriptRunner for Jira

This Text Field (multi-line) formula returns the Description of an Issue in the scripted field.

def description = issue.getDescription();
if (!description){
    return;
}
else{
    description;
}

Jira Misc Custom Fields

This Custom Text field formula returns the Description of an Issue in the calculated text field.

<!-- @@Formula:
description = issue.getIssueObject().description;
if (description == null) return null;
description;
-->

eazyBI Advanced settings

To import this calculated custom field as a property of an issue, add the following advanced settings (by replacing NNNNN with actual field ID from ) 

[jira.customfield_NNNNN]
data_type = "text"

Next, you could select this field via eazyBI import settings to import as the property of an issue.

Now you could find a new measure "Issue Description" in the "Measures" dimension that will show the description of an issue for the "Issue" dimension (issue level) members.

Project lead

ScriptRunner for Jira

This Text Field (multi-line) formula return the Project lead of an Issue in the scripted field.

def lead = issue.getProjectObject().getProjectLead();
if (!lead){
    return;
}
else{
    lead.getDisplayName();
}

Jira Misc Custom Fields

This Custom Text field formula return the Project lead of an Issue in the calculated text field.

<!-- @@Formula:
lead = issue.getIssueObject().getProjectObject().getProjectLead();
if (lead == null) return null;
lead.getDisplayName();
-->

eazyBI Advanced settings

To import this new calculated custom field as a separate dimension(and property if necessary) add the following advanced settings (by replacing NNNNN with actual field ID from ) 

[jira.customfield_NNNNN]
data_type = "string"
name = "Project Lead"
dimension = true

Next, you could select a new field "Project Lead" via eazyBI import settings to import as the additional dimension.

Environment

ScriptRunner for Jira

This Text Field (multi-line) formula returns the Environment of an Issue in the scripted field.

def environment = issue.environment;
if (!environment){
    return;
}
else{
    environment;
}

Jira Misc Custom Fields

This Custom Text field formula returns the Environment of an Issue in the calculated text field.

<!-- @@Formula:
environment = issue.getIssueObject().environment;
if (environment == null) return null;
environment;
-->

eazyBI Advanced settings

To import this calculated custom field as a property of an issue, add the following advanced settings (by replacing NNNNN with actual field ID from ) 

[jira.customfield_NNNNN]
data_type = "text"

Next, you could select this field via eazyBI import settings to import as the property of an issue.

Now you could find a new measure "Issue Environment" in the "Measures" dimension that will show the description of an issue for the "Issue" dimension (issue level) members. 

Previous due dates

ScriptRunner for Jira

This Text Field (multi-line) formula will get all previous due dates from issue changelog and will return them as a comma-separated list with dates in YYYY-MM-DD format.

import org.apache.commons.lang.StringUtils;
import com.atlassian.jira.component.ComponentAccessor;

def dateStrings = new ArrayList();
def items = ComponentAccessor.getChangeHistoryManager().getChangeItemsForField(
  issue, "duedate"
);
for (item in items) {
  def from = item.getFrom();
  if (from) {
    dateStrings.add(from);
  }
}
if (dateStrings.isEmpty()) {
    return;
}
else{
StringUtils.join(dateStrings, ",");
}

Jira Misc Custom Fields

This Custom Text Field formula will get all previous due dates from issue changelog and will return them as a comma-separated list with dates in YYYY-MM-DD format.

<!-- @@Formula:
import org.apache.commons.lang.StringUtils;
import com.atlassian.jira.component.ComponentAccessor;
dateStrings = new ArrayList();
items = ComponentAccessor.getChangeHistoryManager().getChangeItemsForField(
  issue.getIssueObject(), "duedate"
);
for (item : items) {
  from = item.getFrom();
  if (from != null) {
    dateStrings.add(from);
  }
}
if (dateStrings.isEmpty()) return null;
StringUtils.join(dateStrings, ",");
-->

eazyBI Advanced settings

To add this field import as property, add the following to advanced settings where NNNNN is replaced by corresponding custom field ID.

[jira.customfield_NNNNN]
data_type = "string"

You can create similar calculated custom text fields for any other previous field values by replacing "duedate" with the different field names.

Due date changes

ScriptRunner for Jira

This Text Field (multi-line) formula will return a string of dates and counters when the due date was changed.

import java.text.SimpleDateFormat;
import org.apache.commons.lang.StringUtils;
import com.atlassian.jira.component.ComponentAccessor;

def dateStrings = new ArrayList();
def dateFormat = new SimpleDateFormat("yyyy-MM-dd");
def settime = issue.getCreated().getTime();
def items = ComponentAccessor.getChangeHistoryManager().getChangeItemsForField(
  issue, "duedate"
);
for (item in items) {
    def fromval = item.getFrom();
    if (fromval != null) {
        def from = dateFormat.parse(fromval).getTime();
        dateStrings.add(dateFormat.format(settime) +",1") ;
    }
    settime = item.getCreated().getTime();
}
if (!issue.dueDate) {
    return
}
else{
    dateStrings.add(dateFormat.format(settime) + ",1") ;
}
 
if (dateStrings.isEmpty()){
    return
}
else{
    StringUtils.join(dateStrings, "\n");
}

Jira Misc Custom Fields

This Custom Text Field formula will return a string of dates and counters when the due date was changed.

<!-- @@Formula:
import java.text.SimpleDateFormat;
import org.apache.commons.lang.StringUtils;
import com.atlassian.jira.component.ComponentAccessor;
dateStrings = new ArrayList();
dateFormat = new SimpleDateFormat("yyyy-MM-dd");
long settime = issue.get("created").getTime();
items = ComponentAccessor.getChangeHistoryManager().getChangeItemsForField(
  issue.getIssueObject(), "duedate"
);
for (item : items) {
	fromval = item.getFrom();
	if (fromval != null) {
		from = dateFormat.parse(fromval).getTime();
		dateStrings.add(dateFormat.format(settime) +",1") ;
	}
	settime = item.getCreated().getTime();
}
if (issue.get("duedate") != null) {
	dateStrings.add(dateFormat.format(settime) + ",1") ;
}

if (dateStrings.isEmpty())  return null;
StringUtils.join(dateStrings, "\n");
-->

eazyBI Advanced settings

To add this field import as a measure "due date change", add the following to advanced settings where NNNNN is replaced by corresponding scripted custom field ID.

[jira.customfield_NNNNN]
data_type = "integer"
measure = true
split_by = ","
multiple_dates = true

After selected via import options and imported as measure you can select your new measure "Due date change" in a report and use together with "Time" dimension

Project category

ScriptRunner for Jira

This Text Field (multi-line) formula will get the issue project category name.

def category = issue.getProjectObject().getProjectCategoryObject();
if (!category){
	return;
}
else{
	category.getName();  
}

To import Category and Project in multi-level eazyBI dimension, add following ScriptRunner Text Field (multi-line) that will return both category and project name.

def categoryName;
def category = issue.getProjectObject().getProjectCategoryObject();
if (!category) {
  categoryName = "(none)";
} else {
  categoryName = category.getName();
}
categoryName + "|" + issue.getProjectObject().getName();

Jira Misc Custom Fields

This Custom Text Field formula will get the issue project category name.

<!-- @@Formula:
category = issue.getIssueObject().getProjectObject().getProjectCategoryObject();
if (category == null) return null;
category.getName();
-->

To import Category and Project in multi-level eazyBI dimension, add the following  Misc Calculated Text field that will return both category and project name 

<!-- @@Formula:
String categoryName;
category = issue.getIssueObject().getProjectObject().getProjectCategoryObject();
if (category == null) {
  categoryName = "(none)";
} else {
  categoryName = category.getName();
}
categoryName + "|" + issue.getIssueObject().getProjectObject().getName();
-->

eazyBI Advanced settings

and then import it as a two-level dimension hierarchy with following eazyBI advanced settings

[jira.customfield_NNNNN]
data_type = "string"
dimension = true
levels = ["Category", "Project"]
split_by = "|"

Parent issue type

ScriptRunner for Jira

This Text Field (multi-line) formula will get parent issue type for sub-tasks.

def parent = issue.getParentObject();
if (!parent){
    return
} 
else{
  parent.getIssueType().getName();  
}

Jira Misc Custom Fields

This Custom Text Field formula will get parent issue type for sub-tasks.

<!-- @@Formula:
parent = issue.getIssueObject().getParentObject();
if (parent == null) return null;
parent.getIssueTypeObject().getName();
-->

eazyBI Advanced settings

To add this field import as property, add the following to advanced settings where NNNNN is replaced by corresponding custom field ID.

[jira.customfield_NNNNN]
data_type = "string"
dimension = true

Parent issue labels

ScriptRunner for Jira

This Text Field (multi-line) formula will get parent issue labels (comma separated string) for sub-tasks.

import org.apache.commons.lang.StringUtils;

def parent = issue.getParentObject();
if (!parent) {
	return
}
else{
	def labelStrings = new ArrayList();
	for (label in parent.getLabels()) {
 		labelStrings.add(label.getLabel());
	}
	if (labelStrings.isEmpty()){
        return;
    }
StringUtils.join(labelStrings, ",");
}

Jira Misc Custom Fields

This Custom Text Field formula will get parent issue labels (comma separated string) for sub-tasks.

<!-- @@Formula:
import org.apache.commons.lang.StringUtils;
parent = issue.getIssueObject().getParentObject();
if (parent == null) return null;
labelStrings = new ArrayList();
for (label : parent.getLabels()) {
  labelStrings.add(label.getLabel());
}
if (labelStrings.isEmpty()) return null;
StringUtils.join(labelStrings, ",");
-->

eazyBI Advanced settings

If you would like to import this as a multi-value custom dimension in eazyBI then in advanced settings specify

[jira.customfield_NNNNN]
data_type = "string"
dimension = true
multiple_values = true
check_calculated_value = true
split_by = ","

Numeric measure with multiple dates

ScriptRunner for Jira

This is an example of how to build a custom field with a formula that returns multiple numeric measure values for one issue but split by several dates.

This Daily estimates example formula checks if the issue has Original estimated time and due date. If yes then it will distribute estimated hours starting from the due date and backward and using no more than 8 hours per day. For example, if the issue had a due date March 14 and had 36.5 estimated hours then calculated field will have the following value (each date on a separate line and the first value is the date in yyyy-mm-dd format, then a comma and then the number of hours):

2014-03-14,8
2014-03-13,8
2014-03-12,8
2014-03-11,8
2014-03-10,4.5

This is Text Field (multi-line) formula for Daily estimates custom field that should be used for this example:

import org.apache.commons.lang.StringUtils;
import java.text.SimpleDateFormat;
import java.util.Calendar;
 
def dateStrings = new ArrayList();
def dateFormat = new SimpleDateFormat("yyyy-MM-dd");
 
def dueDate = issue.dueDate;
def estimate = issue.getOriginalEstimate();

if (dueDate && estimate && estimate > 0) {
  def calendar = Calendar.getInstance();
  calendar.setTime(dueDate);
  def hours = estimate / 3600.0;
  def dateHours	
  while (hours > 0) {
    dateHours = hours > 8 ? 8 : hours;
    dateStrings.add(
      dateFormat.format(calendar.getTime()) + "," +
      String.valueOf(dateHours)
    );
    hours -= dateHours;
    calendar.add(Calendar.DATE, -1);
  }
}
if (!dateStrings) {
	return;  
} 
else{
    StringUtils.join(dateStrings, "\n");
}

Jira Misc Custom Fields

This is an example of how to build a custom field with a formula that returns multiple numeric measure values for one issue but split by several dates.

This Daily estimates example formula checks if the issue has Original estimated time and due date. If yes then it will distribute estimated hours starting from the due date and backward and using no more than 8 hours per day. For example, if the issue had a due date March 14 and had 36.5 estimated hours then calculated field will have the following value (each date on a separate line and the first value is the date in yyyy-mm-dd format, then a comma and then a number of hours):

2014-03-14,8
2014-03-13,8
2014-03-12,8
2014-03-11,8
2014-03-10,4.5

This is Custom Text Field formula for Daily estimates custom field that should be used for this example:

<!-- @@Formula:
import org.apache.commons.lang.StringUtils;
import java.text.SimpleDateFormat;
import java.util.Calendar;

dateStrings = new ArrayList();
dateFormat = new SimpleDateFormat("yyyy-MM-dd");

dueDate = issue.get("duedate");
estimate = issue.get("timeoriginalestimate");

if (dueDate != null && estimate != null && estimate > 0) {
  calendar = Calendar.getInstance();
  calendar.setTime(dueDate);
  hours = estimate / 3600.0;

  while (hours > 0) {
    dateHours = hours > 8 ? 8 : hours;
    dateStrings.add(
      dateFormat.format(calendar.getTime()) + "," +
      String.valueOf(dateHours)
    );
    hours -= dateHours;
    calendar.add(Calendar.DATE, -1);
  }
}

if (dateStrings.isEmpty()) return null;
StringUtils.join(dateStrings, "\n");
-->

eazyBI Advanced settings

In advanced settings add the following settings for this custom field:

[jira.customfield_NNNNN]
data_type = "decimal"
measure = true
multiple_dates = true

In the eazyBI Source Data tab import this custom field both as measure and as property. 

Original estimated hours history

ScriptRunner for Jira

This example is how to import changes of field "Original estimated hours history" in eazyBI as a separate measure. In order to achieve that, there should be a new Text Field (multi-line) of the scripted custom field created. This is the formula for the Original estimated hours history custom field.

import org.apache.commons.lang.StringUtils;
import com.atlassian.jira.component.ComponentAccessor;
import java.text.SimpleDateFormat;

def dateFormat = new SimpleDateFormat("yyyy-MM-dd");
def values = new ArrayList();
def items = ComponentAccessor.getChangeHistoryManager().getChangeItemsForField(
  issue, "timeoriginalestimate"
);
for (item in items) {
  def to = item.getTo();
  if (to != null) {
    def toNumber = Float.parseFloat(to)/3600;
    values.add(dateFormat.format(item.getCreated()) + "," +toNumber. toString());
  }
}
if (!values) {
	return;  
} 
else{
    StringUtils.join(values, "\n");
}

Jira Misc Custom Fields

This example is how to import changes of field "Original estimated hours history" in eazyBI as a separate measure

In order to achieve that, there should be a new text type of MISC calculated custom field created.

This is the formula for Original estimated hours history custom field that you could put in the field description

<!-- @@Formula:
import org.apache.commons.lang.StringUtils;
import com.atlassian.jira.component.ComponentAccessor;
import java.text.SimpleDateFormat;

dateFormat = new SimpleDateFormat("yyyy-MM-dd");
values = new ArrayList();
items = ComponentAccessor.getChangeHistoryManager().getChangeItemsForField(
  issue.getIssueObject(), "timeoriginalestimate"
);
for (item : items) {
  to = item.getTo();
  if (to != null) {
    toNumber = Float.parseFloat(to)/3600;
    values.add(dateFormat.format(item.getCreated()) + "," +toNumber. toString());
  }
}
if (values.isEmpty()) return null;
StringUtils.join(values, "\n");
-->

That should retrieve all the changes for field "Original estimated hours" and return them in string

eazyBI Advanced settings

After that, in advanced settings add the following settings for this custom field where you place your actual field ID instead of NNNNN.

[jira.customfield_NNNNN]
data_type = "decimal"
measure = true
multiple_dimensions = ["Time"]
split_by = ","

Then In the eazyBI Source Data tab import this custom field as measure and use it together with Time Dimension in your eazyBI reports.

Original estimated hours change

ScriptRunner for Jira

This example is how to import changes of field "Original estimated hours history" in eazyBI as a separate measure. In order to achieve that, there should be a new Text Field (multi-line) of the scripted custom field created. This is the formula for the Original estimated hours history custom field.

import org.apache.commons.lang.StringUtils;
import com.atlassian.jira.component.ComponentAccessor;
import java.text.SimpleDateFormat;
import com.atlassian.jira.issue.IssueImpl;

def dateFormat = new SimpleDateFormat("yyyy-MM-dd");
def values = new ArrayList();
def items = ComponentAccessor.getChangeHistoryManager().getChangeItemsForField(
  issue, "timeoriginalestimate"
);
def created_d = issue.created;
def curest = issue.getOriginalEstimate();
def firstchange = true;
def removal = true;
def toNumber = 0.00;

if(curest && !items)
{
    	 toNumber = curest/3600;
    values.add(dateFormat.format(created_d) + "," +toNumber.toString());   
}
//System.out.println(curest);
for (item in items) {
  def from = item.getFrom();
  def to = item.getTo();
  if (to != null) {  
      if(from != null)
      {
       if(firstchange)
          {
	 toNumber = Float.parseFloat(from)/3600;
    values.add(dateFormat.format(created_d) + "," +toNumber.toString());
          }
          firstchange = false;
     toNumber = Float.parseFloat(to)/3600-Float.parseFloat(from)/3600;
    values.add(dateFormat.format(item.getCreated()) + "," +toNumber.toString());
      }
  }
    if(to == null) {
      if(from != null) {
      
          toNumber = Float.parseFloat(from)/3600;
    values.add(dateFormat.format(item.getCreated()) + "," +toNumber.toString());
      
          if(removal){
     toNumber = 0-Float.parseFloat(from)/3600;
    values.add(dateFormat.format(item.getCreated()) + "," +toNumber.toString());
      }
      }
  };
}
if (!values) {
	return;  
}    
else{
    StringUtils.join(values, "\n");
}

That should retrieve all the changes for field "Original estimated hours" and return them in string

eazyBI Advanced settings

After that, in advanced settings add the following settings for this custom field where you place your actual field ID instead of NNNNN.

[jira.customfield_NNNNN]
data_type = "decimal"
measure = true
multiple_dimensions = ["Time"]
split_by = ","

Then In the eazyBI Source Data tab import this custom field as a measure and use it together with Time Dimension in your eazyBI reports.

Amount paid (ScriptRunner for Jira)

ScriptRunner for Jira

This Custom Text field formula returns the amount paid of an Issue in the calculated text field.

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue

// sum up the values of this custom field
final String customFieldName = "Amount Paid"

def subtasks = issue.subTaskObjects

// if the issue doesn't have any sub-tasks or is a subtask itself then no need for action
if (!subtasks) {
    return
}

def firstSubtask = subtasks.first()
def customField = ComponentAccessor.customFieldManager.getCustomFieldObjects(firstSubtask).findByName(customFieldName)
if (!customField) {
    log.info "Custom field with name $customFieldName is not configured for issue type $firstSubtask.issueType.name and project $firstSubtask.projectObject.key"
    return null
}

subtasks.sum { Issue it -> it.getCustomFieldValue(customField) ?: 0 }

eazyBI Advanced settings

To import this field as a measure, add the following advanced settings (by replacing NNNNN with actual field ID) and then importing this field via eazyBI import settings as dimension.

[jira.customfield_NNNNN]
data_type = "decimal"
measure = true