Name
sn_ci_analytics.VADashboardUtils
Description
No description available
Script
var VADashboardUtils = Class.create();
/*
Transform Data broker utils to reuse common functionality for widgets.
*/
VADashboardUtils.prototype = {
initialize: function() {
this.V_TABLE_START_PAGE = 1;
this.V_TABLE_MAX_ROWS = parseInt(gs.getProperty("glide.script.vtable.max_rows", 1000));
this.V_TABLE_MAX_PAGE_CHUNK_SIZE = 1000;
this.DASHBOARD_EXPORT_LIMIT = parseInt(gs.getProperty("sn_ci_analytics.dashboard.export_limit", 1000));
},
getDiffInPercentage: function(curVal, prevVal) {
if (prevVal === 0) {
return '';
}
return (((curVal - prevVal) / prevVal) * 100).toFixed(0) + '%';
},
// input val in date string format "yyyy-mm-dd"
// returns elapsed time in milliseconds since January 1, 1970, 00:00:00 GMT.
getTimeinMillis: function(date) {
return new GlideDateTime(date).getNumericValue();
},
// input num in number format
// output string in Thousands as 'K' format like [1.20K], millions as 'M' format like [1.20M]
numFormatter: function(num) {
var million = Math.pow(10, 6);
var billion = Math.pow(10, 9);
var convertedNum = 0;
var suffix = '';
if (num > 999 && num < million) {
suffix = 'K';
convertedNum = (num / 1000).toFixed(2);
} else if (num >= million && num < billion) {
suffix = 'M';
convertedNum = (num / million).toFixed(2);
} else if (num >= billion) {
suffix = 'B';
convertedNum = (num / billion).toFixed(2);
} else {
convertedNum = num;
}
var intNum = parseInt(convertedNum);
// Remove ".0" and display it as full integer like 1.0K to 1K
if (convertedNum === intNum) {
convertedNum = intNum;
}
return convertedNum + suffix;
},
// input days in number
// output bucketType in string
getBucketType: function(daysDiff) {
if (daysDiff < 35) return 'day';
if (daysDiff >= 35 && daysDiff < 150) return 'week';
return 'month';
},
// input fromdate: string & toDate : string in yyyy-mm-dd format
// output difference in days : number
getDays: function(fromDate, toDate) {
var glideFromDate = new GlideDateTime(fromDate);
var glideToDate = new GlideDateTime(toDate);
var duration = GlideDate.subtract(glideFromDate, glideToDate); // The difference between fromDate and toDate
var daysInclusiveToDate = duration.getDayPart() + 1;
return daysInclusiveToDate;
},
// input timeSeries data in object format { "2020-09-21": 4, "2020-10-18": 1}
// output timeStamp array [ {timestamp: 1501843274096, y0: 282.67}, {timestamp: 1511434701664, y0: 300.00}]ß
transformTimeSeries: function(timeSeries) {
var convertedTimeSeries = [],
self = this;
for (var date in timeSeries) {
if (timeSeries.hasOwnProperty(date)) {
convertedTimeSeries.push([date, timeSeries[date]]);
}
}
convertedTimeSeries.forEach(function(data, index) {
data[0] = self.getTimeinMillis(data[0]);
});
convertedTimeSeries.sort(function(a, b) {
return a[0] - b[0];
});
var finalChartData = convertedTimeSeries.map(function(data) {
return {
"timestamp": data[0],
y0: data[1]
};
});
return finalChartData;
},
// Accepts currentFromDate, currentToDate as date string in yyyy-MM-dd format
// returns previous period's start & end date in string type with format yyyy-MM-dd
getPreviousPeriod: function(currentFromDate, currentToDate) {
var glideFromDate = new GlideDateTime(currentFromDate);
var glideToDate = new GlideDateTime(currentToDate);
// getDays() is not reused here because of creating twice GlideDateTime object tradeoff vs readabilty
var duration = GlideDate.subtract(glideFromDate, glideToDate); // The difference between From and To
var daysInclusiveToDate = duration.getDayPart() + 1;
var periodDurInMS = daysInclusiveToDate * 86400000; // Milliseconds in a day is 86400000
glideFromDate.subtract(periodDurInMS);
glideToDate.subtract(periodDurInMS);
var previousPeriod = {
fromDate: glideFromDate.getDate().toString(),
toDate: glideToDate.getDate().toString()
};
return previousPeriod;
},
// obj = { parent: { child1: { child2:true } } }
// dotPath = array format like ['child1','child2']
// defaultValue - user defined value as fallback value
// example: safelyGet(obj, ['child1','child2'], false) would returns true
safelyGet: function(obj, dotPath, defaultValue) {
if (typeof(obj) !== "object" || !Array.isArray(dotPath)) {
var transformErrorMessage = gs.getMessage('User transform error: invalid input {0} {1}', obj, dotPath);
gs.addInfoMessage(transformErrorMessage);
return defaultValue;
}
var foundProp = obj;
var path = dotPath;
for (var i = 0; i < path.length; i++) {
if (!foundProp.hasOwnProperty(path[i]))
return defaultValue;
foundProp = foundProp[path[i]];
}
return foundProp || defaultValue;
},
// input date in string yyyy-mm-dd format
// returns boolean
isValidDate: function(date) {
if (!date)
return false;
var gdt = new GlideDateTime();
gdt.setDisplayValue(date);
return gdt.isValid() && gdt.hasDate();
},
decodeFilter: function(encodedFilter) {
var filters = encodedFilter.match(/(?:.*)sys_idISNOTEMPTY\^ORsys_id=("?{.*}"?)(.*)/);
var orderByValue = filters && filters[2] && filters[2].match(/(?:.*)\^(ORDERBYDESC|ORDERBY)(.*?)(?:\^.*|$)/) || [];
var filter = filters && filters[1] ? JSON.parse(filters[1]) : {};
var sortDirection = orderByValue && orderByValue[1] === 'ORDERBYDESC' ? 2 : 1;
var sortField = orderByValue && orderByValue[2] || '';
var pageSize = Number(gs.getProperty('glide.script.vtable.max_rows', 1000));
var result = {
filter: filter,
sort_direction: sortDirection,
sort_field: sortField,
page_size: pageSize,
page_num: 1
};
CIAnalyticsLogger.getLogger().debugJson('Decoded filter result - {}', result);
return result;
},
// in place alphabetical sort util
sortNames: function(names, key) {
if (!key) {
names.sort(function(a, b) {
return a.toLowerCase().localeCompare(b.toLowerCase());
});
} else {
names.sort(function(a, b) {
return a[key].toLowerCase().localeCompare(b[key].toLowerCase());
});
}
},
// input date in yyyy-mm-dd format
// returns monday of the week with the given date in yyyy-mm-dd format
getEarliestDayOfTheWeek: function(date) {
var gd = new GlideDateTime(date);
var days = gd.getDayOfWeekUTC();
gd.addDaysUTC(-1 * (days - 1));
return gd;
},
// input date in yyyy-mm-dd format
// returns first date of the month with the given date in yyyy-mm-dd format
getEarliestDayOfTheMonth: function(date) {
var gd = new GlideDateTime(date);
var days = gd.getDayOfMonthUTC();
gd.addDaysUTC(-1 * (days - 1));
return gd;
},
// input dateList as Object like {"2020-12-09":2,"2020-12-08":9,"2020-12-03":3,"2020-12-04":1,"2020-12-06":3,"2020-12-07":2}
// input dateList as nested Object like {"2020-12-07":{"Occurrences":1,"Sessions":1}}
// input date as string in yyyy-mm-dd format
// input resetProps as array format like ['Occurrences', 'Sessions'] to inject zero with the specified keys for empty dates
fillEmptyDataInProps: function(dateList, date, resetProps) {
resetProps = resetProps || [];
if (resetProps.length) {
var props = {};
resetProps.forEach(function(prop) {
props[prop] = 0;
});
dateList[date] = props;
} else {
dateList[date] = 0;
}
},
// input date as string in yyyy-mm-dd format
// buckettype as string, possible types - 'day', 'month', 'week'
getDateByBucketType: function(date, bucketType) {
switch (bucketType) {
case 'week':
return this.getEarliestDayOfTheWeek(date);
case 'month':
return this.getEarliestDayOfTheMonth(date);
default:
return new GlideDateTime(date);
}
},
// input date as yyyy-mm-dd format
// bucketType as string possible types - 'day', 'month', 'week'
// add days or weeks or months to the given date based on the bucket type
addDaysByBucketType: function(date, bucketType) {
switch (bucketType) {
case 'week':
date.addWeeksUTC(1);
break;
case 'month':
date.addMonthsUTC(1);
break;
default:
date.addDaysUTC(1);
}
},
// input startDate as string in yyyy-mm-dd format
// input endDate as string in yyyy-mm-dd format
// input dateList as Object like {"2020-12-09":2,"2020-12-08":9,"2020-12-03":3,"2020-12-04":1,"2020-12-06":3,"2020-12-07":2}
// input dateList as nested Object like {"2020-12-07":{"Occurrences":1,"Sessions":1}}
// input resetProps as array format like ['Occurrences', 'Sessions'] to inject zero with the specified keys of dataList for empty dates
addMissingDates: function(startDate, endDate, dateList, bucketType, resetProps) {
datelist = JSON.parse(JSON.stringify(dateList));
startDate = this.getDateByBucketType(startDate, bucketType);
endDate = this.getDateByBucketType(endDate, bucketType);
var currentDate;
var props;
while (startDate.onOrBefore(endDate)) {
currentDate = startDate.getDate();
if (!datelist.hasOwnProperty(currentDate)) {
this.fillEmptyDataInProps(datelist, currentDate, resetProps);
}
this.addDaysByBucketType(startDate, bucketType);
}
return datelist;
},
encodeFilter: function(filter) {
var encodedFilter = 'sys_idISNOTEMPTY^ORsys_id=' + JSON.stringify(filter);
CIAnalyticsLogger.getLogger().debug('Encoded filter - ' + encodedFilter);
return encodedFilter;
},
getConversationFilterQuery: function(query, pageSize, pageNum) {
var sortFieldMapper = {
"date": "SessionStartTime",
"user_index": "UserAppIndex",
"duration": "SessionDuration",
"user_id": "", //"AppUserId",
"channel": "", //"ChannelName",
"language": "", //"Locale",
"topics": "", //"ScreenCount",
"favorite": "", //"IsFavorite",
"end_state": "" //"EndState",
};
var requestFields = null;
var decodedQuery = this.decodeFilter(query);
var filter = decodedQuery.filter;
if (filter.query) {
var totalFilter = {};
var parsedQuery = JSON.parse(filter.query);
var fromDate = filter.from_date;
var toDate = filter.to_date;
//if pageSize and pageNum exists (for pagination to Export records), make it higher priority
if (pageSize && pageNum) {
if (filter.page_size) filter.page_size = pageSize;
if (filter.page_num) filter.page_num = pageNum;
if (decodedQuery.page_size) decodedQuery.page_size = pageSize;
if (decodedQuery.page_num) decodedQuery.page_num = pageNum;
}
// Temorary check for comparisonModel array because we are keeping appsee
// query builder active too for QE.
if (Array.isArray(parsedQuery)) {
var vaConditionBuilderUtil = new sn_ci_analytics.VAConditionBuilderUtil();
parsedQuery = vaConditionBuilderUtil.convertToAppsee(parsedQuery);
parsedQuery = parsedQuery.query;
}
var queryKey = Object.keys(parsedQuery)[0] || "and";
parsedQuery[queryKey] = parsedQuery[queryKey] || [];
if (fromDate && toDate) {
var timeQuery = {
"SessionStartTime": {
"gte": fromDate + "T00:00:00",
"lte": toDate + "T23:59:59"
}
};
if (queryKey == "and")
parsedQuery[queryKey].push(timeQuery);
else {
timeQuery[queryKey] = parsedQuery[queryKey];
parsedQuery = {
"and": [
timeQuery
]
};
}
totalFilter = {
"and": [
timeQuery
]
};
}
//Default sort for conversations is by latest date
var sortField = sortFieldMapper[decodedQuery.sort_field] || 'SessionStartTime';
var sortDirection = sortFieldMapper[decodedQuery.sort_field] ? decodedQuery.sort_direction : 2;
requestFields = {
filterQuery: {
"page_size": filter.page_size || decodedQuery.page_size,
"page_num": filter.page_num || decodedQuery.page_num,
"sort_direction": sortDirection,
"sort_field": sortField,
"query": JSON.stringify(parsedQuery)
},
totalQuery: {
"page_size": filter.page_size || decodedQuery.page_size,
"page_num": filter.page_num || decodedQuery.page_num,
"sort_direction": sortDirection,
"sort_field": sortField,
"query": JSON.stringify(totalFilter)
}
};
}
return requestFields;
},
// computes absolute percentage if absolute is true (numerator/denominator)*1, otherwise convert to 100 scale percentage
// returns percentage with precision 1
getPercentage: function(numerator, denominator, absolute) {
var mutiply = absolute ? 1 : 100;
var percentage = (denominator !== 0) ? ((numerator / denominator) * mutiply) : 0;
return absolute ? percentage : percentage.toFixed(1);
},
// converts value to 100 scale/ percentage
// return val to string appended with '%' symbol
toPercentageDisplayValue: function(val, fixed) {
return val ? ((val * 100).toFixed(fixed || 1).toString()) + '%' : '0%';
},
// checks if object is empty or not
// returns true if object is empty or false if not empty
isEmpty: function(obj) {
for (var key in obj) {
return false;
}
return true;
},
// Returns the glideDateTime Object with display value set to an UTC Timezone.
// Using these method to display the date in the UTC format instead of showing in user time zone.
getUtcDateTime: function(date) {
var utcDateTime = null;
if (date) {
var dateTime = new GlideDateTime('1970-01-01');
dateTime.add(date);
dateTime = dateTime.getValue().toString();
utcDateTime = new GlideDateTime();
utcDateTime.setValue(dateTime);
}
return utcDateTime;
},
// Turns object's keys into camel case keys
// For example: { ManAge: 14, Name: 'Pablo', FavoriteActivity: 'guitar' } will turn into: { age: 14, name: 'Pablo', favoriteActivity: 'guitar' }
// Also supports Arary of objects
toCamel: function(o) {
var self = this;
var newO, origKey, newKey, value;
if (o instanceof Array) {
return o.map(function(value) {
if (typeof value === "object") {
value = self.toCamel(value);
}
return value;
});
} else {
newO = {};
for (origKey in o) {
if (o.hasOwnProperty(origKey)) {
newKey = (origKey.charAt(0).toLowerCase() + origKey.slice(1) || origKey).toString();
value = o[origKey];
if (value instanceof Array || (value !== null && value.constructor === Object)) {
value = self.toCamel(value);
}
newO[newKey] = value;
}
}
}
return newO;
},
getDaysFromMilliseconds: function(milliseconds) {
var dur = new GlideDuration(milliseconds);
return dur.getDayPart();
},
getFormattedTime: function(milliseconds) {
var glideTime = new GlideTime(milliseconds);
var timeParts = {
DAY: {
value: this.getDaysFromMilliseconds(milliseconds),
singularPrefix: gs.getMessage('Day'),
pluralPrefix: gs.getMessage('Days'),
},
HOUR: {
value: glideTime.getHourUTC(),
singularPrefix: gs.getMessage('Hour'),
pluralPrefix: gs.getMessage('Hours'),
},
MINUTE: {
value: glideTime.getMinutesUTC(),
singularPrefix: gs.getMessage('Minute'),
pluralPrefix: gs.getMessage('Minutes'),
},
SECOND: {
value: glideTime.getSeconds(),
singularPrefix: gs.getMessage('Second'),
pluralPrefix: gs.getMessage('Seconds'),
}
};
var formattedTime = Object.keys(timeParts).reduce(function(timeStr, key) {
var timePart = timeParts[key];
return (timePart.value) ? timeStr + timePart.value + ' ' + (timePart.value > 1 ? timePart.pluralPrefix : timePart.singularPrefix) + ' ' : timeStr;
}, '');
return formattedTime;
},
getChangeInfoTextForScoreCard: function(curentCount, previousCount) {
var changeText = '';
var difference = Math.abs(curentCount - previousCount);
if (difference > 0) {
changeText = this.numFormatter(difference) + '';
if (previousCount > 0) {
var percentageChange = this.getDiffInPercentage(curentCount, previousCount);
if (!percentageChange.includes('-'))
percentageChange = '+' + percentageChange;
changeText += ' (' + percentageChange + ')';
}
}
return changeText;
},
type: 'VADashboardUtils'
};
Sys ID
04205570535020105946ddeeff7b1286