Document toolboxDocument toolbox

Migrating from Feature Score


On this page




Configuration



  1. Create an Issue Score field with the same name as your Feature Score field
  2. Create as many Issue Score Key-Value pair custom field, as many parameter you have in the regarding Feature Score configuration
  3. Configure your Custom Field Formula as it is in the Feature Score
    1. Feature Score:  ((parameter * multiplier) + (parameter * multiplier) + ... + (parameter * multiplier))
    2. Issue Score:  (("Key_Value custom field name" * multiplier number) + ("Key_Value custom field name" multiplier number) + ... + ("Key_Value custom field name" multiplier number))
  4. Configure your Issue Score ranges to represent the same 4 colors and ranking as in the regarding Feature Score configuration, eg. in case of "Higher is the Better":

  5. Configure your Issue Score Key-Value pair custom field or Number custom field, as the parameters you have in the regarding Feature Score configuration, eg.:
    1. Feature Score
      1. Name: Parameter One
        1. Multiplier: 1
        2. Options:
          1. Name: Option One, Value: 1
          2. Name: Option Two, Value: 2
    2. Issue Score:
      1. Name: Parameter One
        1. Options:
          1. Key: Option One, Value: 1
          2. Key: Option Two, Value: 2
  6. In the end, you should have the same results on your Issue Screen.



Parameter Mapping


Feature ScoreIssue Score
Name-Value parameterKey-Value custom field
Linear parameterNumber custom field


Data migration


Manual

If only a few Score fields and/or parameters are created and few Issues are involved in your system, you can also perform the migration manually. Simply click on the values on the relevant tickets page.


Automatic with a scripting tool

If a lot of Score fields and/or parameters are created and there are many Issues involved in your system, you might want to perform the migration using a script tool. Here's how you can do this with Adaptavist ScriptRunner.

Feature Score store the selected values in the database. The values can be retrieved with their ID.

Here is an example of the data structure:

{
    "calculatedValue" : 40,
    "calculatedColor" : "aui-lozenge-success",
    "tooltip" : "tooltip-sample",
    "scoreParameterPair": [{
        "optionType" : "LINEAR",
        "scoreParameterId": 1,
        "scoreParameterOptionId": 1
    }, {
        "optionType" : "NAME-VALUES",
        "scoreParameterId": 3,
        "scoreParameterOptionId": 5
    }, {
        "optionType" : "LINEAR",
        "scoreParameterId": 2,
        "scoreParameterOptionId": 7
    }]
}

You should explore your Feature Score parameters' and their values' ID's. You can do that on an Issue page with the help of your browser's developer tool.

  1. Open an Issue Screen with a configured Feature Score field
  2. Click on the Feature Score's Edit button
  3. Click on the Parameter's name with right-click
  4. Inspect the element

Name-Value Pair IDs

  • "score_param_5" belongs to the "Parameter One"
  • The ID of this parameter is "5"
  • "<option value="9">" and other options belongs to the "Parameter One"'s options
  • The ID of the parameters' options are the values of the option

Linear IDs

  • "score_param_7" belongs to the "Parameter Linear"
  • The ID of this parameter is "7"
  • "<option value="1">" and other options belongs to the "Parameter Linear"'s options
  • The ID of the parameters' options are the values of the option



You can also retrieve your new Issue Score Key-Value pair custom fields' option ID, or you get get it from the URL when you are editing the option, eg.: /plugins/servlet/issue-score/customfield/key-value/admin/edit?customFieldId=10410&keyValueId=25&fieldConfigId=10514


Based on the known IDs now you can create a script that will map the Feature Score parameters to the new custom fields.

Below you can see an example for the two parameter types and their equivalents with Issue Score. We recommend to create a mapping table with the IDs, eg.:

Name-Value to Key-Value

Feature Score|Issue Score
Feature Score ParameterTypeOptionID|Custom FieldTypeOptionID
Parameter OneName-Value
5|Parameter OneIssue Score Key-Value



Option One9|

Option One - 125

Linear to Number

Feature Score|Issue Score
Feature Score ParameterTypeOptionID|Custom FieldTypeValue
Parameter LinearLinear
7|Parameter NumberNumber custom field


11|

1


Script example for Jira 7.x


import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.fields.CustomField;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.MutableIssue;
import com.atlassian.jira.security.JiraAuthenticationContext;
import com.atlassian.jira.issue.search.SearchProvider;
import com.atlassian.jira.jql.parser.JqlQueryParser;
import com.atlassian.jira.util.JiraUtils;
import com.atlassian.jira.workflow.WorkflowTransitionUtil;
import com.atlassian.jira.workflow.WorkflowTransitionUtilImpl;
import com.atlassian.jira.user.util.UserManager;
import com.atlassian.crowd.embedded.api.User;
import com.atlassian.jira.user.ApplicationUsers;
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder;
import com.atlassian.jira.issue.ModifiedValue;

UserManager userManager = ComponentAccessor.getUserManager();
ApplicationUser adminUserApp = userManager.getUserByName("jira.admin"); 				// <- Add here the user who has the necessary permissions
def AuthContext=ComponentAccessor.getJiraAuthenticationContext();
AuthContext.setLoggedInUser(adminUserApp);


def findIssues(String jqlQuery) {
    def issueManager = ComponentAccessor.issueManager
    def user = ComponentAccessor.jiraAuthenticationContext.getLoggedInUser()
    def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser.class)
    def searchProvider = ComponentAccessor.getComponent(SearchProvider.class)
	org.apache.lucene.search.Collector collector 
    def query = jqlQueryParser.parseQuery(jqlQuery)
    def results = searchProvider.search(query, user, com.atlassian.jira.web.bean.PagerFilter.getUnlimitedFilter())
    results.issues.collect { issue -> issueManager.getIssueObject(issue.id) }
}

def currentUser = ComponentAccessor.jiraAuthenticationContext.getLoggedInUser().getName();
def jqlQuery = "project = IMAS" 															// <- Define your project or Issues with JQL
def issues = findIssues(jqlQuery)

issues.each {    
    def issueManager = ComponentAccessor.issueManager;
    def issue = (Issue) it;  
    def customFieldManager = ComponentAccessor.getCustomFieldManager();   
    def featureScore = customFieldManager.getCustomFieldObjectByName('Feature Score');		// <- Define your FS custom field
    def featureScoreCFvalue = issue.getCustomFieldValue(featureScore);
    def keyValueOne = customFieldManager.getCustomFieldObjectByName('Parameter One');		// <- Define your Issue Score Key-Value custom field
    def keyValueOneCFvalue = issue.getCustomFieldValue(keyValueOne);
    def numberField = customFieldManager.getCustomFieldObjectByName('Issue Score Number');	// <- Define your Number custom field
    def numberFieldvalue = issue.getCustomFieldValue(numberField);
    
    // Example of the value mapping
	// IGNORE THE RED STATIC TYPE CHECKING ERROR   

    if(featureScoreCFvalue != null) {
        def list = featureScoreCFvalue.getScoreParameterPair();								// <- Iterate in your FS data structure
        for (item in list) {
                    
             if(item.scoreParameterId == 5){												// <- If "Parameter One" with ID=5
                 if(item.scoreParameterOptionId == 9){										// <- If "Parameter One"'s selected otpion is "Option One" with ID=9
                     def changeHolder = new DefaultIssueChangeHolder()
                     keyValueOne.updateValue(null, issue, new ModifiedValue(keyValueOneCFvalue, (Double) 25),changeHolder) // <- Set Issue Score KV pair to "Option One - 1" with ID=25
                 }
             }
             if(item.scoreParameterId == 7){    											// <- If "Parameter Linear" with ID=7
                 if(item.scoreParameterOptionId == 1){										// <- If "Parameter Linear"'s selected otpion is "1" with ID=1
                     def changeHolder = new DefaultIssueChangeHolder()
                     numberField.updateValue(null, issue, new ModifiedValue(numberFieldvalue, (Double) 1),changeHolder) // <- Set number field to "1"
                 }
             }          
    	}
	}	
}



Script example for Jira 8.x


import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.fields.CustomField;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.MutableIssue;
import com.atlassian.jira.security.JiraAuthenticationContext;
import com.atlassian.jira.bc.issue.search.SearchService;
import com.atlassian.jira.jql.parser.JqlQueryParser;
import com.atlassian.jira.util.JiraUtils;
import com.atlassian.jira.workflow.WorkflowTransitionUtil;
import com.atlassian.jira.workflow.WorkflowTransitionUtilImpl;
import com.atlassian.jira.user.util.UserManager;
import com.atlassian.crowd.embedded.api.User;
import com.atlassian.jira.user.ApplicationUsers;
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder;
import com.atlassian.jira.issue.ModifiedValue;
 
UserManager userManager = ComponentAccessor.getUserManager();
ApplicationUser adminUserApp = userManager.getUserByName("jira.admin");                 // <- Add here the user who has the necessary permissions
def AuthContext=ComponentAccessor.getJiraAuthenticationContext();
AuthContext.setLoggedInUser(adminUserApp);
 
 
def findIssues(String jqlQuery) {
    def issueManager = ComponentAccessor.issueManager
    def user = ComponentAccessor.jiraAuthenticationContext.getLoggedInUser()
    def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser.class)
    def searchService = ComponentAccessor.getComponent(SearchService.class)
    org.apache.lucene.search.Collector collector
    def query = jqlQueryParser.parseQuery(jqlQuery)
    def results = searchService.search(user, query, com.atlassian.jira.web.bean.PagerFilter.getUnlimitedFilter())
    results.results.collect { issue -> issueManager.getIssueObject(issue.id) }
}

def currentUser = ComponentAccessor.jiraAuthenticationContext.getLoggedInUser().getName();
def jqlQuery = "project = IMAS" 															// <- Define your project or Issues with JQL
def issues = findIssues(jqlQuery)

issues.each {    
    def issueManager = ComponentAccessor.issueManager;
    def issue = (Issue) it;  
    def customFieldManager = ComponentAccessor.getCustomFieldManager();   
    def featureScore = customFieldManager.getCustomFieldObjectByName('Feature Score');		// <- Define your FS custom field
    def featureScoreCFvalue = issue.getCustomFieldValue(featureScore);
    def keyValueOne = customFieldManager.getCustomFieldObjectByName('Parameter One');		// <- Define your Issue Score Key-Value custom field
    def keyValueOneCFvalue = issue.getCustomFieldValue(keyValueOne);
    def numberField = customFieldManager.getCustomFieldObjectByName('Issue Score Number');	// <- Define your Number custom field
    def numberFieldvalue = issue.getCustomFieldValue(numberField);
    
    // Example of the value mapping
	// IGNORE THE RED STATIC TYPE CHECKING ERROR    

    if(featureScoreCFvalue != null) {
        def list = featureScoreCFvalue.getScoreParameterPair();								// <- Iterate in your FS data structure
        for (item in list) {
                    
             if(item.scoreParameterId == 5){												// <- If "Parameter One" with ID=5
                 if(item.scoreParameterOptionId == 9){										// <- If "Parameter One"'s selected otpion is "Option One" with ID=9
                     def changeHolder = new DefaultIssueChangeHolder()
                     keyValueOne.updateValue(null, issue, new ModifiedValue(keyValueOneCFvalue, (Double) 25),changeHolder) // <- Set Issue Score KV pair to "Option One - 1" with ID=25
                 }
             }
             if(item.scoreParameterId == 7){    											// <- If "Parameter Linear" with ID=7
                 if(item.scoreParameterOptionId == 1){										// <- If "Parameter Linear"'s selected otpion is "1" with ID=1
                     def changeHolder = new DefaultIssueChangeHolder()
                     numberField.updateValue(null, issue, new ModifiedValue(numberFieldvalue, (Double) 1),changeHolder) // <- Set number field to "1"
                 }
             }          
    	}
	}	
}


After running the script, run the Bulk Update on these issues to apply the changes and calculate the Issue core as described here.