Name

global.AvailabilitySegmentProcessor

Description

No description available

Script

var AvailabilitySegmentProcessor = Class.create();
AvailabilitySegmentProcessor.prototype = {
  initialize: function() {
  	this.CONSTANTS = new global.AvailabilityConstants();

  	// key is availability type, value is map of segments to calculate for the type
  	this.BUCKET_MAP = {};
  	this.BUCKET_MAP[this.CONSTANTS.AVAIL_TYPES.ANNUALLY] = {};
  	this.BUCKET_MAP[this.CONSTANTS.AVAIL_TYPES.MONTHLY] = {};
  	this.BUCKET_MAP[this.CONSTANTS.AVAIL_TYPES.WEEKLY] = {};
  	this.BUCKET_MAP[this.CONSTANTS.AVAIL_TYPES.DAILY] = {};
  	this.BUCKET_MAP[this.CONSTANTS.AVAIL_TYPES.LAST_12_MONTHS] = {};
  	this.BUCKET_MAP[this.CONSTANTS.AVAIL_TYPES.LAST_30_DAYS] = {};
  	this.BUCKET_MAP[this.CONSTANTS.AVAIL_TYPES.LAST_7_DAYS] = {};

  	/**
  	 * Define the methods needed to support segment boundary checking,
  	 * segment iteration, segment type checking for each availability type
  	 * - Two types of availability intervals
  	 *   - fixed (yearly, monthly, weekly, daily) based off begin and end
  	 *   - rolling (last x months/days) based off the current date
  	 * - fixed availability types define decrementSegment() method to add outage across multiple segments
  	 */
  	this.TYPES = {};
  	this.TYPES[this.CONSTANTS.AVAIL_TYPES.ANNUALLY] = Object.freeze({
  		type: this.CONSTANTS.SEGMENT_TYPES.FIXED,
  		beginningOfSegment: function(date) { return gs.beginningOfYear(date); },
  		endOfSegment: function(date) { 
  			var endOfYear = new GlideDateTime(gs.endOfYear(date)); 
  			// additional second to roll end date to midnight (display & calc)
  			endOfYear.addSeconds(1);
  			return endOfYear;
  		},
  		decrementSegment: function (date, number) { return date.addYearsLocalTime(number); },
  	});
  	this.TYPES[this.CONSTANTS.AVAIL_TYPES.MONTHLY] = Object.freeze({
  		type: this.CONSTANTS.SEGMENT_TYPES.FIXED,
  		beginningOfSegment: function(date) { return gs.beginningOfMonth(date); },
  		endOfSegment: function(date) { 
  			var endOfMonth = new GlideDateTime(gs.endOfMonth(date));
  			// additional second to roll end date to midnight (display & calc)
  			endOfMonth.addSeconds(1);
  			return endOfMonth;
  		},
  		decrementSegment: function(date, number) { return date.addMonthsLocalTime(number); },
  	});
  	this.TYPES[this.CONSTANTS.AVAIL_TYPES.WEEKLY] = Object.freeze({
  		type: this.CONSTANTS.SEGMENT_TYPES.FIXED,
  		beginningOfSegment: function(date) { return gs.beginningOfWeek(date); },
  		endOfSegment: function(date) { 
  			var endOfWeek = new GlideDateTime(gs.endOfWeek(date));
  			// additional second to roll end date to midnight (display & calc)
  			endOfWeek.addSeconds(1);
  			return endOfWeek;
  		},
  		decrementSegment: function(date, number) { return date.addWeeksLocalTime(number); },
  	});
  	this.TYPES[this.CONSTANTS.AVAIL_TYPES.DAILY] = Object.freeze({
  		type: this.CONSTANTS.SEGMENT_TYPES.FIXED,
  		beginningOfSegment: function(date) { return gs.beginningOfDay(date); },
  		endOfSegment: function(date) { 
  			var endOfDay = new GlideDateTime(gs.endOfDay(date));
  			// additional second to roll end date to midnight (display & calc)
  			endOfDay.addSeconds(1);
  			return endOfDay;
  		},
  		decrementSegment: function(date, number) { return date.addDaysLocalTime(number); },
  	});
  	this.TYPES[this.CONSTANTS.AVAIL_TYPES.LAST_12_MONTHS] = Object.freeze({
  		type: this.CONSTANTS.SEGMENT_TYPES.ROLLING,
  		beginningOfSegment: function() {
  			// segment should include today, so we subtract 12 months from the beginning of the next day
  			var beginningOfDate = new GlideDateTime(gs.beginningOfTomorrow());
  			beginningOfDate.addMonthsLocalTime(-12);
  			return beginningOfDate.getValue();
  		},
  		endOfSegment: function() {
  			// end at midnight the next day
  			return gs.beginningOfTomorrow();
  		},
  	});
  	this.TYPES[this.CONSTANTS.AVAIL_TYPES.LAST_30_DAYS] = Object.freeze({
  		type: this.CONSTANTS.SEGMENT_TYPES.ROLLING,
  		beginningOfSegment: function() {
  			// segment should include today, so we subtract 12 months from the beginning of the next day
  			var beginningOfDate = new GlideDateTime(gs.beginningOfTomorrow());
  			beginningOfDate.addDaysLocalTime(-30);
  			return beginningOfDate.getValue();
  		},
  		endOfSegment: function() {
  			// end at midnight the next day
  			return gs.beginningOfTomorrow();
  		},
  	});
  	this.TYPES[this.CONSTANTS.AVAIL_TYPES.LAST_7_DAYS] = Object.freeze({
  		type: this.CONSTANTS.SEGMENT_TYPES.ROLLING,
  		beginningOfSegment: function() {
  			// segment should include today, so we subtract 12 months from the beginning of the next day
  			var beginningOfDate = new GlideDateTime(gs.beginningOfTomorrow());
  			beginningOfDate.addDaysLocalTime(-7);
  			return beginningOfDate.getValue();
  		},
  		endOfSegment: function() {
  			// end at midnight the next day
  			return gs.beginningOfTomorrow();
  		},
  	});
  	Object.freeze(this.TYPES);
  },

  /**
   * Return segments to calculate for an availability type
   * @param {String} type - availability type
   * @return {Object|null}
   */
  getSegments: function(type) {
  	if (this.BUCKET_MAP[type])
  		return this.BUCKET_MAP[type];
  	return null;
  },

  /**
   * Return all segments grouped by availability type
   * @return {Object}
   */
  getBucketMap: function () {
  	return this.BUCKET_MAP;
  },

  /**
   * Create segment and associate to an availability type
   * @param {String} type - availability type
   * @param {GlideDateTime} segmentBegin
   * @param {GlideDateTime} segmentEnd
   */
  _generateSegment: function(type, segmentBegin, segmentEnd) {
  	if (!type)
  		return;
  	else if (!segmentBegin || !segmentBegin.isValid() || !segmentEnd || !segmentEnd.isValid())
  		return;

  	var typeBucket = this.BUCKET_MAP[type];

  	// form hashkey from string of begin and end unix timestamp
  	var key = segmentBegin.getNumericValue().toString() + segmentEnd.getNumericValue().toString();

  	// create new segment in bucket if it does not exist
  	if (!typeBucket[key])
  		// instead of actual outages, start and end index of outages are tracked
  		// since outages are kept in increasing order by start date
  		// initial values for indexes are -1 to skip outage iteration in calculations
  		typeBucket[key] = {
  			begin: segmentBegin.getNumericValue(),
  			end: segmentEnd.getNumericValue(),
  			// beginGDT: segmentBegin.getValue(),
  			// endGDT: segmentEnd.getValue(),
  			startIndex: -1,
  			endIndex: -1 // endIndex should be exclusive
  		};
  },

  /**
   * Generate all segments for an availability type that may be calculated between begin and end
   * @param {String} type - availability type to generate segments to calculate
   * @param {GlideDateTime} begin
   * @param {GlideDateTime} end
   */
  _generateSegmentsForType: function(type, begin, end) {
  	if (!type)
  		return;
  	else if (!begin || !begin.isValid() || !end || !end.isValid())
  		return;

  	var typeEnum = this.TYPES[type];

  	// get the latest segment to calculate from outage end (outward-most moving inward)
  	var segmentBegin = new GlideDateTime(typeEnum.beginningOfSegment(end));
  	var segmentEnd = new GlideDateTime(typeEnum.endOfSegment(segmentBegin));

  	if (typeEnum.type === this.CONSTANTS.SEGMENT_TYPES.FIXED)
  		// loop until all segments between begin and end are created
  		while (end.getNumericValue() > segmentBegin.getNumericValue() && begin.getNumericValue() < segmentEnd.getNumericValue()) {
  			this._generateSegment(type, segmentBegin, segmentEnd);

  			// decrement one segment by adjusting begin and end dates
  			typeEnum.decrementSegment(segmentBegin, -1);
  			segmentEnd.setValue(typeEnum.endOfSegment(segmentBegin));
  		}
  	else if (typeEnum.type === this.CONSTANTS.SEGMENT_TYPES.ROLLING)
  		this._generateSegment(type, segmentBegin, segmentEnd);
  },

  /**
   * Generate all segments between begin and end across all availability types
   * @param {GlideDateTime} begin
   * @param {GlideDateTime} end
   */
  _generateAllSegments: function(begin, end) {
  	if (!begin || !begin.isValid() || !end || !end.isValid())
  		return;

  	for (var type in this.BUCKET_MAP) {
  		if (this.BUCKET_MAP.hasOwnProperty(type))
  			this._generateSegmentsForType(type, begin, end);
  	}
  },

  /**
   * Add all outages across all applicable segments between begin and end
   * @param {Array<Object>} outages - index of outage to process in outages array
   * @param {GlideDateTime} begin
   * @param {GlideDateTime} end
   */
  _addOutagesToAllSegments: function(outages, begin, end) {
  	if (!begin || !begin.isValid() || !end || !end.isValid())
  		return;

  	var outageLength = (outages && outages.length ? outages.length : 0);
  	for (var outageIndex = 0; outageIndex < outages.length; outageIndex++) {
  		for (var type in this.BUCKET_MAP) {
  			if (this.BUCKET_MAP.hasOwnProperty(type))
  				this._addOutageToTypeSegments(outages[outageIndex], outageIndex, type, begin, end);
  		}
  	}
  },

  /**
   * Add outage to applicable segments of an availability type between begin and end
   * - associate the outage with the applicable segments for one availability type
   * @param {Object} outage
   * @param {Number} outageIndex - index of outage in outages array
   * @param {GlideDateTime} begin
   * @param {GlideDateTime} end
   */
  _addOutageToTypeSegments: function(outage, outageIndex, type, begin, end) {
  	if (!outage || !outage.begin || !outage.end)
  		return;
  	else if (outageIndex === null || !typeof outageIndex === 'number')
  		return;
  	else if (!begin || !begin.isValid() || !end || !end.isValid())
  		return;

  	var typeEnum = this.TYPES[type];
  	var typeBucket = this.BUCKET_MAP[type];

  	/**
  	 * Increment indexes in segment to account for additional outage
  	 * @param {Number} outageArrIndex - index of outage from larger outage array
  	 * @param {GlideDateTime} segmentBegin
  	 * @param {GlideDateTime} segmentEnd
  	 */
  	function _addOutageToSegment(outageArrIndex, segmentBegin, segmentEnd) {
  		// form hashkey from string of begin and end unix timestamp
  		var key = segmentBegin.getNumericValue().toString() + segmentEnd.getNumericValue().toString();

  		// create new segment in bucket if it does not exist
  		var existingSegment = typeBucket[key];
  		if (!existingSegment)
  			return;

  		if (existingSegment.startIndex === -1)
  			existingSegment.startIndex = outageArrIndex;

  		existingSegment.endIndex = outageArrIndex + 1; // endIndex should be exclusive
  	};


  	var applicableStart = new GlideDateTime(typeEnum.beginningOfSegment(begin));
  	var applicableEnd = new GlideDateTime(typeEnum.endOfSegment(end));

  	if (outage.end >= applicableStart.getNumericValue() && outage.begin < applicableEnd.getNumericValue()) {
  		// convert to GDT to get ISO date format
  		var outageEndGDT = new GlideDateTime();
  		outageEndGDT.setNumericValue(outage.end);

  		// get the latest segment to calculate from outage end (outward-most moving inward)
  		var segmentBegin = new GlideDateTime(typeEnum.beginningOfSegment(outageEndGDT));
  		var segmentEnd = new GlideDateTime(typeEnum.endOfSegment(segmentBegin));

  		if (typeEnum.type === this.CONSTANTS.SEGMENT_TYPES.FIXED)
  			// outage may fall across multiple segments, so need to loop appropriately
  			while (outage.end > segmentBegin.getNumericValue() && outage.begin < segmentEnd.getNumericValue()) {
  				_addOutageToSegment(outageIndex, segmentBegin, segmentEnd);

  				// decrement one segment by adjusting begin and end dates
  				typeEnum.decrementSegment(segmentBegin, -1);
  				segmentEnd.setValue(typeEnum.endOfSegment(segmentBegin));
  			}
  		else if (typeEnum.type === this.CONSTANTS.SEGMENT_TYPES.ROLLING)
  			if (outage.end > segmentBegin.getNumericValue() && outage.begin < segmentEnd.getNumericValue())
  				_addOutageToSegment(outageIndex, segmentBegin, segmentEnd);
  	}
  },

  /**
   * Determine the segments for each availability type that need to be calculated
   * @param {Array<Object>} outages
   * @param {GlideDateTime} begin
   * @param {GlideDateTime} end
   */
  determineWorkingSegments: function(outages, begin, end) {
  	if (!begin || !begin.isValid() || !end || !end.isValid())
  		return null;

  	this._generateAllSegments(begin, end);
  	this._addOutagesToAllSegments(outages, begin, end);
  },

  type: 'AvailabilitySegmentProcessor'
};

Sys ID

7f0c822feb412110ae20d7ac7852288a

Offical Documentation

Official Docs: