Name
sn_ssa_core.SSADeflectionAnalyticsServiceSNC
Description
For invoking Pattern Matcher on the loaded Activity and Pattern Strings.
Script
var SSADeflectionAnalyticsServiceSNC = Class.create();
SSADeflectionAnalyticsServiceSNC.prototype = {
initialize: function() {
this.activityTypeRepId = {};
this.patternRegExp = {};
this.sequence = 1;
this.windowObj = {};
this.activityService = {};
this.deflectionConfig = {};
},
/**
* captures deflection metrics for a given deflection config id
*
* @param SysId:Deflection Configuration sys_id
*/
captureDeflectionAnalytics: function(deflectionConfigId, userRefField) {
this.deflectionConfig = new GlideRecordSecure(SSAConstants.TABLE_DEFLECTION_CONFIGURATION);
if (this.deflectionConfig.get(deflectionConfigId + '')) {
var user = new GlideRecordSecure(this.deflectionConfig.activity_context.context_table + '');
var durationInMinutes = (new GlideDateTime(this.deflectionConfig.window).getNumericValue()/60000)*2;
var loginTimeFilter = 'last_login_time>=javascript:gs.minutesAgo('+durationInMinutes+')';
if(!gs.nil(userRefField))
loginTimeFilter = userRefField + '.' + loginTimeFilter;
user.addEncodedQuery(loginTimeFilter);
user.query();
if (user.hasNext()) {
this._getActivityWindow();
var actSubCtx = global.ActivitySubscriptionContext.getContext();
this.activityService = actSubCtx.getService('DEFLECTION_CONTEXT_SERVICE');
while (user.next()) {
this._fetchActivitiesAndMatchPatterns(user);
}
}
}
},
/**
* fetches Activity String and RegEx representation of Activity Pattern
*
* @param GlideRecord: Customer Contact/ Consumer record
*/
_fetchActivitiesAndMatchPatterns: function(user) {
var activities = this._getActivitiesData(user);
if (activities.length > 0) {
var activityString = this._getActivityString(activities);
var deflectionPattern = this._getDeflectionPattern();
while (deflectionPattern.next()) {
this._matchDeflectionPattern(activityString, deflectionPattern, activities, user);
}
}
},
/**
* returns Activity Array based on the Window duration in the Deflection Config and last Activity time captured/considered for measuring deflection.
*
* @param GlideRecord: Customer Contact/ Consumer record
*/
_getActivitiesData: function(user) {
var activities = [];
var result = this.activityService.getActivities({
"contextId": this.deflectionConfig.activity_context.sys_id,
"contextInstanceId": user.sys_id + '',
"startDate": this.windowObj.startDate + '',
"endDate": this.windowObj.endDate + '',
"fetchGroupsFromMapping": true,
"sortOrder": 'Asc'
});
activities = result.data.activities;
return activities;
},
/**
* matches the Deflection Pattern RegEx on the Activity String
*
* @param String: String representation of Activities
* @param GlideRecord: Deflection Pattern
* @param Array: Array of Activity Objects
* @param GlideRecord: Customer Contact/ Consumer record
*/
_matchDeflectionPattern: function(activityString, deflectionPattern, activities, user) {
var deflectionPatternRegExp = this._getExpForActivityPattern(deflectionPattern.activity_pattern+'');
if (!gs.nil(deflectionPatternRegExp)) {
var reg = new RegExp(deflectionPatternRegExp, 'g');
while ((matchString = reg.exec(activityString)) !== null && matchString[0].length > 0 && (matchString[0].length%SSAConstants.REP_ID_LENGTH == 0)) {
var matchIf = deflectionPattern.activity_pattern.match_if;
var end = (matchString.index + matchString[0].length) / SSAConstants.REP_ID_LENGTH;
var start = matchString.index / SSAConstants.REP_ID_LENGTH;
var matchEndTime = new GlideDateTime(activities[end - 1].created + '');
var matchStartTime = new GlideDateTime(activities[start].created + '');
var matchInWindowLimits = this._isMatchFoundInWindowLimits(activities, matchStartTime, matchEndTime);
if (matchInWindowLimits) {
if (gs.nil(matchIf)) {
this._createDeflectionMetricData(deflectionPattern, start, end, activities);
} else {
var subStringToLookForPrimaryActivity = activityString.substring(matchString.index, activityString.length);
var primaryActivityIndex = subStringToLookForPrimaryActivity.indexOf(this._getRepresentativeId(deflectionPattern.activity_pattern.primary_activity + ''));
var primaryActivity = (primaryActivityIndex + matchString.index) / SSAConstants.REP_ID_LENGTH;
if (matchIf == SSAConstants.MATCH_IF_FOUND && primaryActivityIndex > 0 && primaryActivity == end) {
var primaryActivityCreatedTime = new GlideDateTime(activities[primaryActivity].created + '');
var activityDuration = GlideDateTime.subtract(matchStartTime, primaryActivityCreatedTime);
if (this._compareDurations(activityDuration, this.deflectionConfig.window) == -1)
this._createDeflectionMetricData(deflectionPattern, start, end, activities);
} else if (matchIf == SSAConstants.MATCH_IF_NOT_FOUND && primaryActivityIndex == -1 && activities.length == end) {
if (this._compareDurations(GlideDateTime.subtract(matchStartTime, new GlideDateTime()), this.deflectionConfig.window) == 1)
this._createDeflectionMetricData(deflectionPattern, start, end, activities);
} else if (matchIf == SSAConstants.MATCH_IF_NOT_FOUND && primaryActivityIndex > 0 && primaryActivity == end) {
primaryActivityCreatedTime = new GlideDateTime(activities[primaryActivity].created + '');
activityDuration = GlideDateTime.subtract(matchStartTime, primaryActivityCreatedTime);
if (this._compareDurations(activityDuration, this.deflectionConfig.window) == 1)
this._createDeflectionMetricData(deflectionPattern, start, end, activities);
}
}
}
}
}
},
/**
* returns true if there is a Match found for Primary Activity in the given Window limit. Default Window limit is 24 hours.
*
* @param GlideRecord: Deeflection Config
* @param Array: Activities
* @param GlideDateTime: Start time of matching subsequence
*/
_isMatchFoundInWindowLimits: function(activities, matchStartTime, matchEndTime) {
var matchDuration = GlideDateTime.subtract(matchStartTime, matchEndTime);
var windowDuration = this.deflectionConfig.window;
return this._compareDurations(matchDuration, windowDuration) == -1;
},
/**
* created Deflection Metric and Deflection Meetric Activity records for the matching patterns.
*
* @param GlideRecord: Deflection Pattern
* @param Array: Activity Array
*/
_createDeflectionMetricData: function(deflectionPattern, start, end, activities) {
var finalActivity = activities[end - 1];
var deflectionMetricId = this._createDeflectionMetric(deflectionPattern, finalActivity.sys_id, finalActivity.source_table_name);
var order = end - start - 1;
for (i = end - 2; i >= start; i--) {
var activity = activities[i];
this._createDeflectionActivity(activity, deflectionMetricId, order);
order--;
}
},
/**
* returns 1 if duration1 > duration2
* returns 0 if duration1 = duration2
* returns -1 if duration1 < duration2
*/
_compareDurations: function(duration1, duration2) {
var dt1 = new GlideDateTime(duration1);
var dt2 = new GlideDateTime(duration2);
return dt1.compareTo(dt2);
},
/**
* returns the Deflection Pattern for 'No Deflection' if Activities of type 'Case created' exist.
* returns the Deflection Pattern for 'Potential/Confirmed Deflection' if Activities of type 'Case created' does not exist.
*
* @param SysId: Deflection Config sys_id
* @param Boolean: holds true if there are any Activities of type 'Case Created' in the Activity string.
*/
_getDeflectionPattern: function() {
var deflectionPattern = new GlideRecordSecure(SSAConstants.TABLE_DEFLECTION_PATTERN);
deflectionPattern.addQuery('deflection_configuration', this.deflectionConfig.sys_id);
deflectionPattern.query();
return deflectionPattern;
},
/**
* returns window obj based on the deflection configuration and the last Activities captured/considered in the previous run.
*
* @param GlideRecord: Deflection configuration record
*/
_getActivityWindow: function() {
var gdt = new GlideDateTime();
this.windowObj.endDate = gdt + '';
var windowDuration = this.deflectionConfig.window.dateNumericValue();
gdt.add(-(windowDuration * 2));
this.windowObj.startDate = gdt + '';
},
/**
* returns String representation of Activities for the given Obj Array
*
* @param Array: Activity Obj Array
*/
_getActivityString: function(activities) {
var activityString = '';
if (activities.length > 0) {
for (var i = 0; i < activities.length; i++) {
activityString += this._getRepresentativeId(activities[i].activity_type_id + '');
}
}
return activityString;
},
/**
* Create deflection metric for matched Patterns
*
* @param GlideRecord: Deflection Pattern
* @param GlideRecord: Customer Contact/ Consumer record
*/
_createDeflectionMetric: function(deflectionPattern, activityId, activityTable) {
var deflectionMetric = new GlideRecordSecure(SSAConstants.TABLE_DEFLECTION_METRIC);
deflectionMetric.initialize();
deflectionMetric.type = deflectionPattern.activity_pattern.outcome + '';
deflectionMetric.deflection_pattern = deflectionPattern.sys_id;
deflectionMetric.content_id = activityId;
deflectionMetric.content_table = activityTable;
return deflectionMetric.insert() + '';
},
/**
* creates deflection metric activity for given Deflection Metric
**/
_createDeflectionActivity: function(activity, deflectionMetricId, order) {
var deflectionMetricActivity = new GlideRecordSecure(SSAConstants.TABLE_DEFLECTION_METRIC_ACTIVITY);
deflectionMetricActivity.initialize();
deflectionMetricActivity.activity_table = activity.source_table_name;
deflectionMetricActivity.activity = activity.sys_id;
deflectionMetricActivity.metric = deflectionMetricId;
deflectionMetricActivity.order = order;
deflectionMetricActivity.insert();
},
/**
* returns RegEx representation of Pattern Element
*/
_getExpForPatternElement: function(elementSysId) {
if (this.patternRegExp[elementSysId] == -1) {
var patternElement = new GlideRecordSecure(SSAConstants.TABLE_PATTERN_ELEMENT);
if (patternElement.get(elementSysId)) {
var activityTypeRecord = patternElement.activity_type.getRefRecord();
if (activityTypeRecord.isValidRecord()) {
var repId = this._getRepresentativeId(activityTypeRecord.sys_id + '');
this.patternRegExp[elementSysId] = this._quantifyElement(repId, patternElement);
} else {
this.patternRegExp[elementSysId] = '';
}
} else {
this.patternRegExp[elementSysId] = '';
}
}
return this.patternRegExp[elementSysId];
},
/**
* returns RegEx representation of Pattern Element Groups
*/
_getExpForPatternElementGroup: function(elementGroupSysId) {
if (this.patternRegExp[elementGroupSysId] == -1) {
var patternElementGroup = new GlideRecordSecure(SSAConstants.TABLE_PATTERN_ELEMENT_GROUP);
if (patternElementGroup.get(elementGroupSysId)) {
var firstElementRecord = patternElementGroup.first_element.getRefRecord();
var secondElementRecord = patternElementGroup.second_element.getRefRecord();
if (firstElementRecord.isValidRecord())
var patternExpForFirstElement = this._getExpForElementBase(patternElementGroup.first_element);
if (secondElementRecord.isValidRecord())
var patternExpForSecondElement = this._getExpForElementBase(patternElementGroup.second_element);
if (!gs.nil(patternExpForFirstElement) && !gs.nil(patternExpForSecondElement)) {
switch (patternElementGroup.operator + "") {
case SSAConstants.AND_OPERATOR:
this.patternRegExp[elementGroupSysId] = this._quantifyElement(patternExpForFirstElement + '' + patternExpForSecondElement, patternElementGroup);
break;
case SSAConstants.OR_OPERATOR:
this.patternRegExp[elementGroupSysId] = this._quantifyElement(patternExpForFirstElement + '|' + patternExpForSecondElement, patternElementGroup);
}
} else if (!gs.nil(patternExpForFirstElement))
this.patternRegExp[elementGroupSysId] = this._quantifyElement(patternExpForFirstElement + '', patternElementGroup);
else if (!gs.nil(patternExpForSecondElement))
this.patternRegExp[elementGroupSysId] = this._quantifyElement(patternExpForSecondElement + '', patternElementGroup);
else
this.patternRegExp[elementGroupSysId] = '';
} else
this.patternRegExp[elementGroupSysId] = '';
}
return this.patternRegExp[elementGroupSysId];
},
_quantifyElement: function(repId, elementBase) {
var elementExp;
switch (elementBase.occurrence + "") {
case SSAConstants.OCCURRENCE_ONCE:
elementExp = '(' + repId + ')';
break;
case SSAConstants.OCCURRENCE_OPTIONALLY_ONCE:
elementExp = '(' + repId + ')?';
break;
case SSAConstants.OCCURRENCE_OPTIONALLY_MANY:
elementExp = '(' + repId + ')*';
break;
case SSAConstants.OCCURRENCE_ATLEAST_ONCE:
elementExp = '(' + repId + ')+';
break;
case SSAConstants.OCCURRENCE_RANGE:
var rangeQuantifier = '{' + elementBase.minimum + '';
if (!gs.nil(elementBase.maximum)) {
rangeQuantifier += ',';
if (elementBase.maximum > 0)
rangeQuantifier += elementBase.maximum + '';
}
rangeQuantifier += '}';
elementExp = '(' + repId + ')' + rangeQuantifier;
break;
}
return elementExp;
},
/**
* returns RegEx representation of Element Base
*/
_getExpForElementBase: function(elementBaseSysId) {
if (gs.nil(this.patternRegExp[elementBaseSysId])) {
this.patternRegExp[elementBaseSysId] = -1;
var elementBase = new GlideRecordSecure(SSAConstants.TABLE_ELEMENT_BASE);
if (elementBase.get(elementBaseSysId)) {
if (elementBase.sys_class_name == SSAConstants.TABLE_PATTERN_ELEMENT_GROUP) {
this._getExpForPatternElementGroup(elementBaseSysId);
} else {
this._getExpForPatternElement(elementBaseSysId);
}
}
} else if (this.patternRegExp[elementBaseSysId] == -1) {
this.patternRegExp[elementBaseSysId] = '';
}
return this.patternRegExp[elementBaseSysId];
},
/**
* returns RegEx representation of Actiivity Pattern
*/
_getExpForActivityPattern: function(activityPatternSysId) {
if (gs.nil(this.patternRegExp[activityPatternSysId])) {
var activityPattern = new GlideRecordSecure(SSAConstants.TABLE_ACTIVITY_PATTERN);
activityPattern.get(activityPatternSysId);
if (activityPattern.isValidRecord()) {
var patternElementMapping = new GlideRecordSecure(SSAConstants.TABLE_PATTERN_ELEMENT_MAPPING);
patternElementMapping.addQuery('pattern', activityPatternSysId);
patternElementMapping.orderBy('order');
patternElementMapping.query();
if (patternElementMapping.hasNext()) {
var patternExp = '';
while (patternElementMapping.next()) {
var elementBaseRecord = patternElementMapping.element.getRefRecord();
if (elementBaseRecord.isValidRecord()) {
var expForElementBase = this._getExpForElementBase(patternElementMapping.element);
if (!gs.nil(expForElementBase)) {
patternExp += '(' + this._getExpForElementBase(patternElementMapping.element) + ')';
this.patternRegExp[activityPatternSysId] = patternExp;
}
}
}
gs.info('RegEx for Activity Pattern : ' + activityPattern.sys_id + ' : ' + patternExp + ' : Primary Activity:' + this._getRepresentativeId(activityPattern.primary_activity + ''));
}
}
}
return this.patternRegExp[activityPatternSysId];
},
/**
* Auto generates 5 digit unique literal for a given Activity Type
*/
_getRepresentativeId: function(activityTypeSysId) {
if (!gs.nil(this.activityTypeRepId[activityTypeSysId])) {
return this.activityTypeRepId[activityTypeSysId];
} else {
var repId = this._lpad(this.sequence.toString(16).toUpperCase(), 4, "0");
this.activityTypeRepId[activityTypeSysId] = SSAConstants.REP_ID_DELIMITER + repId;
this.sequence++;
return SSAConstants.REP_ID_DELIMITER + repId; // Prefix a delimiter to RepId
}
},
_lpad: function(s, width, charCount) {
return (s.length >= width) ? s : (new Array(width).join(charCount) + s).slice(-width);
},
type: 'SSADeflectionAnalyticsServiceSNC'
};
Sys ID
731bc32228f74010f877f90c1752e0d6