Name

global.ApplyPcLookup

Description

No description available

Script

var ApplyPcLookup = Class.create();
ApplyPcLookup.prototype = Object.extendsObject(AbstractAjaxProcessor, {
  // DO NOT MODIFY THIS SCRIPT INCLUDE NAME
  // THIS SCRIPT INCLUDE IS BEING INVOKED FROM JAVA CLASS - CLASSIFICATIONSOLUTIONVERSION
  CLASSIFICATION:'classification_trainer',
  SIMILARITY:'similarity_trainer',
  WORKFLOW_CLASSIFICATION: 'workflow_classification_trainer',
  
  applyPcLookup: function() {		
  	try{
  		
  		this.log = new GSLog('com.snc.ml.apply.pc.lookup', 'ApplyPcLookup');
  		this.log.disableDatabaseLogs();
  		
  		var pcLookupRec = this.__getPcLookupRec(this.getParameter('sysparm_sysId'));
  		if(pcLookupRec == null)
  			return;
  		
  		if (!JSUtil.nil(pcLookupRec.solution.capability)) {
  			this.solution_type = pcLookupRec.solution.capability;
  		} else if (!JSUtil.nil(pcLookupRec.solution.ml_capability_definition)) {
  			this.solution_type = pcLookupRec.solution.ml_capability_definition.capability.value;
  		}
  		
  		if(this.solution_type==this.CLASSIFICATION || this.solution_type==this.WORKFLOW_CLASSIFICATION){
  			this.__updatePcLookupRefInClass(pcLookupRec);
  			this.updatePrecCovForSolution(pcLookupRec.solution);
  		} else if(this.solution_type==this.SIMILARITY){
  			this.__updateThresholdCoverageForSolution(pcLookupRec.threshold, pcLookupRec.coverage, pcLookupRec.solution);
  		}
  		
  	} catch(e){
  		this.log.logErr('Exception caught in ApplyPcLookup: '+e, 'ApplyPcLookup');
  	}
  	
      return;
  },
  		
  // DO NOT MODIFY THIS METHOD SIGNATURE
  // THIS METHOD IS BEING INVOKED FROM JAVA CLASS - CLASSIFICATIONSOLUTIONVERSION
  updatePrecCovForSolution: function(solutionSysId) {
  	var sol = this.__calcPrecCovForSolution(solutionSysId);		
  	this.__updatePrecCovForSolution(sol.precision, sol.coverage, sol.recall, solutionSysId);
  },
  
  __getPcLookupRec: function(sysId) {
  	var record = new GlideRecord("ml_pc_lookup");
  	record.addQuery("sys_id", sysId);
  	record.query();
  	if (record.next())
  		return record;
  	else
  		return null;
  },
  
  __updatePcLookupRefInClass: function(pcLookupRec) {
  	var record = new GlideRecord("ml_class");		
  	record.addQuery("sys_id", pcLookupRec.class_name);
  	record.query();	
  	if (record.next()) {
  		record.pc_lookup = pcLookupRec.sys_id;
  		record.update();
  	}
  },
  
  __calcPrecCovForSolution: function(solutionSysId) {
  	var sol_prec = 0.0; 
  	var sol_cov = 0.0; 
  	var sol_rec = 0.0;
  	var tot_dist = 0.0;
  	var record = new GlideRecord("ml_class");
  	record.addQuery("solution", solutionSysId);
  	record.query();
  	while (record.next()) {
  		sol_prec += record.distribution * record.pc_lookup.precision;
  		sol_cov += record.distribution * record.pc_lookup.coverage;
  		sol_rec += record.distribution * record.pc_lookup.recall;
  		tot_dist += record.distribution;
  	}
  	sol_prec = tot_dist != 0 ? sol_prec / tot_dist : 0;
  	sol_rec = tot_dist != 0 ? sol_rec / tot_dist : 0;
  	sol_cov = sol_cov / 100;
  	var sol = {};
  	sol.precision = sol_prec;
  	sol.coverage = sol_cov;
  	sol.recall = sol_rec;
  	return sol;
  },
  
  __calcThresholdCoverageForSolution: function(solutionSysId){
  	return Math.floor((Math.random() * 100) + 1);
  },
  
  __updatePrecCovForSolution: function(sol_prec, sol_cov, sol_rec, solutionSysId) {
  	var record = new GlideRecord("ml_solution");
  	record.addQuery("sys_id", solutionSysId);
  	record.query();	
  	if (record.next()) {
  		record.solution_precision = sol_prec;
  		record.solution_coverage = sol_cov;
  		record.solution_recall = sol_rec;
  		record.update();
  	}
  },
  
  __updateThresholdCoverageForSolution: function(solution_threshold, solution_coverage, solutionSysId){
  	var record = new GlideRecord("ml_solution");
  	record.addQuery("sys_id", solutionSysId);
  	record.query();	
  	if (record.next()) {
  		record.threshold = solution_threshold;
  		record.solution_coverage = solution_coverage;
  		record.update();
  	}
  },
  
  getCombinationsCloserToUserTarget: function(pcLookuptable, input, usePercentage) {
  	
  	var inputs = Object.keys(input);
  	
  	var allclasses = Object.keys(pcLookuptable);
  	var classes = Object.keys(pcLookuptable);

  	var nearestRecord = {};
  	
  	//If solution level targets are specified, iterate k times and choose a random class level value(if class target is not specified), else choose the nearest to the class target.
  	if("solution" in input) {
  		var metric = Object.keys(input.solution)[0];
  		var inputTarget = parseFloat(input.solution[metric]);
  		var randomList = [];
  		
  		var min = 9999;
  		
  		var minorityClass = {};
  		// For classess with distribution < 1%, compute the nearest.
  		for(var i in allclasses) {
  			var className = allclasses[i];
  			if(inputs.indexOf(className) < 0 && "solution" in input) {
  				var pclookups = pcLookuptable[className];
  				if(parseFloat(pclookups[0]["distribution"]) < 0.5) {
  					minorityClass[className] = this.findNearest(className, pcLookuptable, metric, inputTarget, usePercentage);
  				}
  			}
  			if(inputs.indexOf(className)>=0) {
  				var classMetric = Object.keys(input[className])[0];
  				var classTarget = parseFloat(input[className][classMetric]);
  				minorityClass[className] = this.findNearest(className, pcLookuptable, classMetric, classTarget, usePercentage);
  			}	
  			
  		}
  		
  		classes = this.difference(allclasses, Object.keys(minorityClass));
  		
  		var solEstimate1 = 0.0;
  		var totalDistr1 = 0.0;
  		for(var cn in minorityClass) {
  			if(minorityClass[cn] !== null) {
  				solEstimate1 = solEstimate1 + parseFloat(minorityClass[cn][metric]) * parseFloat(minorityClass[cn]["distribution"]);
  				totalDistr1 = totalDistr1 + parseFloat(minorityClass[cn]["distribution"]);
  			}
  			
  		}

  		for(var key in minorityClass)
  		{
  			nearestRecord[key] = minorityClass[key];
  		}
  		
  		
  		var iterations = 1000;
  		for(var k=1; k <= iterations; k++) {
  			
  			var classRandom = {};
  			for(var j in classes) {
  				var clasname = classes[j];
  				
  				//Choose the nearest to the solution target at the class level in the last iteration so that we don't rule out these estimates as 
  				//the estimates from the random approach may be better or worse than the default approach.
  				if(k==iterations) {
  					gs.info("Last iteration -- finding the nearest ones");
  					var near = this.findNearest(clasname, pcLookuptable, metric, inputTarget, usePercentage);
  					classRandom[clasname] = near;
  				}else {
  					classRandom[clasname] = this.randomPerClass(pcLookuptable[clasname]);
  				}
  			}
  			
  			//For each random selection, check if it is closer to solution target.
  			var solEstimate = 0.0;
  			var totalDistr = 0.0;
  			
  			for(var l in classes) {
  				var classname = classes[l];
  				if(classRandom[classname] !== null) {
  					solEstimate = solEstimate + parseFloat(classRandom[classname][metric]) * parseFloat(classRandom[classname]["distribution"]);
  					totalDistr = totalDistr + parseFloat(classRandom[classname]["distribution"]);
  				}
  			}
  			
  			solEstimate = (solEstimate + solEstimate1)/(totalDistr+ totalDistr1);
  			
  			if(k==iterations) {
  				gs.info("iteration = " + k + " Final Sol Estimate = " + solEstimate + " Total Distribution = " + (totalDistr+totalDistr1));
  			}
  			
  			if(usePercentage) {
  				solEstimate = solEstimate * 100;
  			}
  			
  			var diff = Math.abs(solEstimate - inputTarget);
  			if(diff < min) {
  				min = diff;
  				for(var ke in classRandom)
  				{
  					nearestRecord[ke] = classRandom[ke];
  				}
  			}
  			randomList.add(classRandom);
  		}
  		
  		var solEstimate2 = 0.0;
  		var totalDistr2 = 0.0;
  		for(var cname in nearestRecord) {
  			if(nearestRecord[cname] != null) {
  			solEstimate2 = solEstimate2 + parseFloat(nearestRecord[cname][metric]) * parseFloat(nearestRecord[cname]["distribution"]);
  			totalDistr2 = totalDistr2 + parseFloat(nearestRecord[cname]["distribution"]);
  			}
  		}
  		gs.info("Final Estimate: " + (solEstimate2/totalDistr2));
  	}
  	//If only class level targets are specified, choose nearest value to the target for each class.
  	else {
  		for(var cln in input) {
  			var clMetric = Object.keys(input[cln])[0];
  			var near = this.findNearest(cln, pcLookuptable, clMetric, parseFloat(input[cln][clMetric]), usePercentage);
  			nearestRecord[cln] =  near;
  		}
  	}
  	
  	return nearestRecord;
  },
  
  difference: function(a1, a2) {
  	var result = [];
  	for (var i = 0; i < a1.length; i++) {
  		if (a2.indexOf(a1[i]) < 0) {
  			result.push(a1[i]);
  		}
  	}
  	
  	return result;
  },
  
  randomPerClass: function(list) {
  	var num = Math.random() * (list.length);
  	var rnd = Math.floor(num);
      return list[rnd];
  },
  
  findNearest: function(className, pcLookuptable, metric, value, usePercentage) {
  	var pclookups = pcLookuptable[className];
  	var min = 9999;
  	var nearestRecord = null;
  	for(var i in pclookups) {
  		var pcLookup = pclookups[i];
  		var currentValue = parseFloat(pcLookup[metric]);
  		if(usePercentage) {
  			currentValue = currentValue*100;
  		}
  		var diff = Math.abs(currentValue - value);
  		if(diff < min) {
  			if(metric == "precision" && currentValue == 100.0 && value != 100.0) {
  				continue;
  			}
  			min = diff;
  			nearestRecord = pcLookup;
  		}
  	}
  	
  	return nearestRecord;
  },
  
  updateSolution: function(pcLookRef){
  	var pcLookupRec = this.__getPcLookupRec(pcLookRef);
  	if(pcLookupRec == null)
  		return;
  	this.__updatePcLookupRefInClass(pcLookupRec);
  	this.updatePrecCovForSolution(pcLookupRec.solution);
  },
  
  type: 'ApplyPcLookup'
});

Sys ID

bbdeb246eb0003004afb5722aaa34565

Offical Documentation

Official Docs: