Name

global.BurndownMetric

Description

This parent utility is used by the burn down charts utilites

Script

var BurndownMetric = Class.create();

BurndownMetric.prototype = {
  STORY_METRIC: "RM Story State Capture",
  STORY_STATE_WORK_IN_PROGRESS: 2,
  STORY_STATE_COMPLETE: 3,
  STORY_STATE_PLANNING: 1,
  STORY_STATE_CANCELLED: 4,
  STORY_STATE_DRAFT: -6,
  SCRUM_PP_BURNDOWN_LOG: 'com.snc.sdlc.scrum.pp.burndown.debug',
  
  initialize: function ( burndownRequest ) {
      this.burndownRequest = burndownRequest;
      this.glideRecord = this.getGlideRecord();
      if( this.burndownRequest.view == 'PROJECT') {
          var projectRecordDecorator = new ProjectRecordDecorator(this.glideRecord);
          var dates = projectRecordDecorator.getSprintsStartEndDate();
          if ( dates.startDate )
              this.startDate = new GlideDateTime(dates.startDate);
          if ( dates.startDate )
              this.endDate = new GlideDateTime(dates.endDate);
      }
      else {
          this.startDate = new GlideDateTime( this.glideRecord.getValue('start_date'));
          this.endDate = new GlideDateTime( this.glideRecord.getValue('end_date'));    
      }
      this.gsLog = new GSLog(this.SCRUM_PP_BURNDOWN_LOG, this.type); 
      this.gsLog.setLevel("debug");

      this.gsLog.logDebug('BurndownMetric sysId: ' + this.burndownRequest.sysId + ' ;this.view: ' + this.burndownRequest.view + ' ;this.table: ' + this.burndownRequest.table + ' ;this.startDate: ' + this.startDate +
          ' ;this.endDate: ' + this.endDate + ' ;this.metricDefinition: ' + this.burndownRequest.metricDefinition + ' ;this.metricId: ' + this.burndownRequest.metricId +
          ' ;this.referenceField: ' + this.burndownRequest.referenceField );
  },
  
  getGlideRecord: function() {
      if ( !this.glideRecord ) {
          var gr = new GlideRecord( this.burndownRequest.table );
          gr.get( this.burndownRequest.sysId );
          this.glideRecord = gr;
      }
      return this.glideRecord;
  },

  getTitle: function() {
      var gr = this.getGlideRecord();
      return gr.getValue('short_description');
  },

  getStartDate: function() {
      return this.startDate;
  },

  getEndDate: function() {
      return this.endDate;
  },

  getMetricInstances: function( ) {
      var gr = new GlideRecord("metric_instance");
      gr.addQuery("definition.name", this.burndownRequest.metricDefinition);
      gr.addQuery("id", this.burndownRequest.metricId);
      gr.orderBy("start");
      gr.query();
      return gr;
  },
  
  getDailyTotals: function() {
      var totals = {};
      var grMetrics = this.getMetricInstances();
      if (grMetrics.next()) {
  		totals.startingValue = 0;
          do {
  			if (grMetrics.start.getGlideObject().getDate().compareTo(this.getStartDate()) <= 0)
  				totals.startingValue = this.clearFormatting(grMetrics.value) - 0;
              totals[grMetrics.start.getGlideObject().getLocalDate()] = this.clearFormatting(grMetrics.value) - 0;
              /*this.gsLog.logDebug('getDailyTotals[' + grMetrics.start.getGlideObject().getDate() + ']= ' + this.clearFormatting(grMetrics.value) );*/
          } while (grMetrics.next());
      }
      return totals;
  },
  
  getDailyTotalsMap: function() {
      var data = {};
      var startDate = new GlideDateTime(this.getStartDate()).getLocalDate();
      var endDate = new GlideDateTime(this.getEndDate()).getLocalDate();
      var totals = this.getDailyTotals();
      var dailyPointsTotal = totals.startingValue;
      while (startDate.compareTo(endDate) <= 0) {
          if (JSUtil.nil(totals[startDate])) {
              data[startDate] = {
                  dailyPoints : dailyPointsTotal,
                  changed : false
              };
          }
          else {
              dailyPointsTotal = totals[startDate];
              data[startDate] = {
                  dailyPoints : dailyPointsTotal,
                  changed : true
              };
          }
          /*this.gsLog.logDebug('getDailyTotalsMap[' + startDate + '] { key: ' + data[startDate]['dailyPoints'] + ' , value: ' + data[startDate]['changed'] + '}');*/
          startDate.addDays(1);
      }
      
      data.startingValue = totals.startingValue;
      return data;
  },
  
  getStories: function() {
      var stories = {};
      var gr = new GlideRecord("rm_story");
      gr.addQuery(this.burndownRequest.referenceField, this.burndownRequest.sysId);
      gr.query();
      while (gr.next()) {
          stories[String(gr.sys_id)] = gr.story_points - 0;
      }
      return stories;
  },
  
  getStoryMetricIntances: function( metricIds ) {
      gr = new GlideRecord("metric_instance");
      gr.addQuery("definition.name", this.STORY_METRIC);
      gr.addQuery("id", 'IN', metricIds);
      gr.orderBy("start");
      gr.query();
      return gr;
  },
  
  getStoryMetrics: function() {
      var startDate = new GlideDateTime(this.getStartDate()).getLocalDate();
      var endDate = new GlideDateTime(this.getEndDate()).getLocalDate();
      var metrics = {};
      var storyCarryOvers = {};
      var stories = this.getStories();
      var storySysIds = '';
      for ( var storySysId in stories ) {
          storySysIds += storySysId + ',' ;
      }
      var gr = this.getStoryMetricIntances(storySysIds);
      while (gr.next()) {
          var date = gr.start.getGlideObject().getLocalDate();
          var value = 0;
          if (date.compareTo(startDate) < 0 || date.compareTo(endDate) > 0)
              continue;
          switch (gr.field_value - 0) {
              case this.STORY_STATE_CANCELLED:
              if (!storyCarryOvers[String(gr.id)])
                  storyCarryOvers[String(gr.id)] = this.STORY_STATE_CANCELLED;
  			value = stories[gr.id] * -1;
              break;
              case this.STORY_STATE_COMPLETE:
              if (!storyCarryOvers[String(gr.id)])
                  storyCarryOvers[String(gr.id)] = this.STORY_STATE_CANCELLED;
              value = stories[gr.id] * -1;
              break;
              default:
              if (storyCarryOvers[String(gr.id)])
                  value = stories[gr.id];
          }
          metrics[date] = metrics[date] ? metrics[date] + value : value;
          /*this.gsLog.logDebug('getStoryMetrics[' + date + '] : ' + metrics[date] );*/
      }
      return metrics;
  },
  
  getDailyPoints: function() {
      var data = [];
      var startDate = new GlideDateTime(this.getStartDate()).getLocalDate();
      var endDate = new GlideDateTime(this.getEndDate()).getLocalDate();
      var dailyTotalMap = this.getDailyTotalsMap();
      
      while (startDate.compareTo(endDate) <= 0) {
          data.push({
              // key : String(new GlideDateTime(startDate).getDate()),
              key : String(startDate),
              value : dailyTotalMap[startDate].dailyPoints
          });
          /*this.gsLog.logDebug('getDailyPoints{key: ' + String(new GlideDateTime(startDate).getDate()) + ', value: ' + dailyTotalMap[startDate].dailyPoints + '}');*/
          startDate.addDays(1);
      }
      return data;
  },
  
  getIdeal : function() {
      var isIdealLinear = gs.getProperty('com.snc.sdlc.scrum.pp.burndown.ideal.linear', false);
      if( isIdealLinear && isIdealLinear == 'true') {
          return this.getIdealLinear();
      }
      else {
          return this.getIdealNonLinear();
      }
  },

  getIdealNonLinear: function() {
      var data = [];
      var startDate = new GlideDateTime(this.getStartDate()).getLocalDate();
      var endDate = new GlideDateTime(this.getEndDate()).getLocalDate();
      var daysRemaining = (endDate.getNumericValue() - startDate.getNumericValue()) / 1000 / 3600 / 24 + 1;
      var dailyTotalsMap = this.getDailyTotalsMap();
      var dailyPointsTotal = dailyTotalsMap.startingValue;
      var dailyAfterBurnDown = dailyPointsTotal;
      var burnDownAmount = dailyPointsTotal / daysRemaining;        
      
      while (startDate.compareTo(endDate) <= 0) {
          dailyAfterBurnDown += dailyTotalsMap[startDate].dailyPoints - dailyPointsTotal;
          dailyPointsTotal = dailyTotalsMap[startDate].dailyPoints;
          if (dailyTotalsMap[startDate].changed)
              burnDownAmount = dailyAfterBurnDown / daysRemaining;
          dailyAfterBurnDown -= burnDownAmount;
          daysRemaining--;
          data.push({
              // key : String(new GlideDateTime(startDate).getDate()),
              key : String(startDate),
              value : Math.max(dailyAfterBurnDown, 0)
          });
          /*this.gsLog.logDebug('getIdeal{key: ' + String(new GlideDateTime(startDate).getDate()) + ', value: ' + Math.max(dailyAfterBurnDown, 0) + '}');*/
          startDate.addDays(1);
      }
      return data;
  },

  hasMetricData: function(){
      var gr = this.getMetricInstances();
      if (gr.next())
          return true;
      
      return false;
  },
  
  hasValidDates: function(){
      return !this.glideRecord.start_date || !this.glideRecord.end_date ? false : true;
  },

  getMaxMetricValue: function( ) {
      // Value defn a String, cant get MAX as straight
      var metricInstances = this.getMetricInstances();
      var maxValue = 0;
      while( metricInstances.next() ) {
          if( parseInt(metricInstances.getValue('value')) > maxValue ) {
              maxValue = metricInstances.getValue('value');
          }
      }
      this.gsLog.logDebug('Linear configured, Max Metric Value: ' + maxValue);
      return maxValue;
  },

  getIdealLinear: function() {
      var data = [];
      var startDate = new GlideDateTime(this.getStartDate()).getLocalDate();
      var endDate = new GlideDateTime(this.getEndDate()).getLocalDate();
      var daysRemaining = (endDate.getNumericValue() - startDate.getNumericValue()) / 1000 / 3600 / 24 ;
      var dailyTotalsMap = this.getDailyTotalsMap();
      var dailyPointsTotal = this.getMaxMetricValue();
      var dailyAfterBurnDown = dailyPointsTotal;
      var burnDownAmount = dailyPointsTotal / daysRemaining;

      while (startDate.compareTo(endDate) <= 0) {
          data.push({
              // key : String(new GlideDateTime(startDate).getDate()),
              key : String(startDate),
              value : Math.max(dailyAfterBurnDown, 0)
          });
          dailyAfterBurnDown -= burnDownAmount;
          /*this.gsLog.logDebug('getIdealLinear{key: ' + String(new GlideDateTime(startDate).getDate()) + ', value: ' + Math.max(dailyAfterBurnDown, 0) + '}');*/
          startDate.addDays(1);
      }
      return data;
  },

  clearFormatting: function(value) {
      if (value.includes(',')) {
          return value.replace(',', '');
      }
  	else if (value.includes('.')) {
          return value.replace('.', '');
      }
      return value;
  },

  type:  'BurndownMetric'
};

Sys ID

a7c7e0629f022100598a5bb0657fcfb2

Offical Documentation

Official Docs: