Change custom field based on multiple select fields in ScriptRunner, e.g. calculating the average

Cory Strope May 13, 2021

I am trying to recalculate the average value whenever one of the numbers change. I believe this is possible using behaviors for each value, but there will be hundreds of such fields. As an example, I have attempted to put together this behavior in a Scriptrunner 6.20.0 custom scripted field:

-----

import com.atlassian.jira.component.ComponentAccessor

def customFieldManager = ComponentAccessor.getCustomFieldManager()

def M1_nacc = customFieldManager.getCustomFieldObjectsByName("mon.M1.nacc")
def M1_conf = customFieldManager.getCustomFieldObjectsByName("mon.M1.conf")
def M2_nacc = customFieldManager.getCustomFieldObjectsByName("mon.M2.nacc")
def M2_conf = customFieldManager.getCustomFieldObjectsByName("mon.M2.conf")
def M3_nacc = customFieldManager.getCustomFieldObjectsByName("mon.M3.nacc")
def M3_conf = customFieldManager.getCustomFieldObjectsByName("mon.M3.conf")

static float metric_score (nacc, conf) {
if (nacc == "Unacceptable") { return 4.0 as float }
if (conf == "High") { return 1.0 as float }
if (conf == "Medium") { return 2.0 as float }
return 3.0 as float
}

def conf = 0
def acc = 0
if (M1_nacc != "N/A") { acc += 1; conf += metric_score (M1_nacc, M1_conf) }
if (M2_nacc != "N/A") { acc += 1; conf += metric_score (M2_nacc, M2_conf) }
if (M3_nacc != "N/A") { acc += 1; conf += metric_score (M3_nacc, M3_conf) }

if (acc == 0) return 0
return (conf / acc)

----

Here, the *_nacc field controls whether the *_conf field is calculated. if:
*_nacc = "N/A" --> ignore in calculation of average
*_nacc = "Unacceptable" --> value returned is 4
*_nacc = "Acceptable" --> use conf field value for average value calculation

The above script is simplistic, but it does calculate the appropriate value *the first time*. Any changes that occur later do not affect the returned value. What I would like to do is to capture each change to the nacc and conf values to modify the average value.

In the worst case scenario, this could be done on transition, but I would like to avoid that path.

Thanks!

1 answer

1 accepted

1 vote
Answer accepted
Peter-Dave Sheehan
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
May 13, 2021

To implement this as a behaviour, you would want to assign the same behaviour script to each field.  That might be tedious, but by using a script file and pointing each field to the same file, at least you would have a central place to maintain the file.

Here is my attempt at reverse engineer your logic a best as I understood in a way that should be relatively easy to expand and adjust

def fieldPrefix = 'mon'
def fieldIdList = ['M1','M2','M3'] //add as many fields as you need
def confScoreMap = [High:1F, Medium: 2F]
def naccScoreMap = ['Unacceptable':4F, 'Acceptable':3F] //used for default values
def scores =[]
fieldIdList.each{
def naccField = getFieldByName("${fieldPrefix}.${it}.nacc")
def confField = getFieldByName("${fieldPrefix}.${it}.conf")
def nacc = naccField.value
def conf = confField.value
switch(nacc){
case 'Unacceptable':
scores << naccScoreMap[nacc]
break
case 'Acceptable':
scores << confScoreMap[conf] ?: naccScoreMap[nacc] //default to 3 if value not found in map
break
}
}
def total = scores.sum() as Float
def average = total/scores.size()
getFieldByName('average result field').setReadOnly(true).setFormValue(average)
Cory Strope May 14, 2021

This is a great reverse engineer! Though it may be slightly tedious to add the behavior to all .nacc and .conf fields, it us much less tedious than cluttering the behaviors in scriptrunner with > 150 specific behaviors.

Thanks!

Suggest an answer

Log in or Sign up to answer