<commit_before>/* Dashboard object */

function TasseoDashboard(metrics, datasource, period, options) {
  this.options = $.extend({
    interpolation: 'step-after',
    renderer: 'area',
    stroke: true,
    criticalColor: '#d59295',
    warningColor: '#f5cb56',
    normalColor: '#afdab1',
    container: '.main'
  }, options)

  this.period = period;
  this.datasource = datasource;
  this.buildContainers(metrics);
  this.createGraphs(metrics);
}

TasseoDashboard.prototype = {
  createGraphs: function(metrics) {
    var filtered = []
    _.each(metrics, function(metric, idx) {
      if (_.isString(metric.target)) {
        var element = document.querySelector('.graph' + idx);
        filtered.push(new TasseoMetric(element, metric, this.options));
      }
    }, this);

    this.metrics = filtered;
  },

  clearGraphs: function() {
    _.each(this.metrics, function(metric) {
      metric.clear();
    })
  },

  // add our containers
  buildContainers: function(metrics) {
    var falseTargets = 0;
    for (var i=0; i<metrics.length; i++) {
      if (metrics[i].target === false) {
        $(this.options.container).append('<div class="false"></div>');
        falseTargets++;
      } else {
        var j = i - falseTargets;
        var link_open = 'link' in metrics[i] ? '<a href="' + metrics[i].link + '" target="_new">' : '';
        var link_close = 'link' in metrics[i] ? '</a>' : '';
        var graph_div =
        '<div id="' + j + '" class="graph graph' + j + '">' +
        '<span class="description description' + j + '"></span>' +
        link_open + '<div class="overlay-name overlay-name' + j + '"></div>' + link_close +
        '<div class="overlay-number overlay-number' + j + '"></div>' +
        '</div>';
        $(this.options.container).append(graph_div);
      }
    }
  },

  setPeriod: function(period) {
    this.period = period;
  },

  refreshData: function(immediately) {
    this.datasource.refresh(this.metrics, this.period, function(metric) {
      if (immediately) {
        metric.redraw()
      }
    });

    if (!immediately) {
      _.each(this.metrics, function(metric) {
        metric.redraw()
      });
    }
  }
}


function TasseoMetric(element, metricSpec, dashboardOptions) {
  this.element = element
  this.alias = metricSpec.alias || metricSpec.target;
  this.description = metricSpec.description;
  this.metricSpec = metricSpec
  this.dashboardOptions = dashboardOptions;

  this.clear();
}

TasseoMetric.prototype = {
  getTarget: function() {
    return this.metricSpec.target;
  },
  clear: function() {
    this.datum = [{ x:0, y:0 }];
    this.graph = new Rickshaw.Graph({
      element: this.element,
      width: this.dashboardOptions.graph_width || 348,
      height: this.dashboardOptions.graph_height || 100,
      interpolation: this.dashboardOptions.interpolation,
      renderer: this.dashboardOptions.renderer,
      stroke: this.dashboardOptions.stroke,
      series: [{
        name: this.alias,
        color: this.dashboardOptions.normalColor,
        data: this.datum
      }]
    });

    this.graph.render();
  },

  setColor: function(color) {
    this.graph.series[0].color = color;
  },

  update: function(newData) {
    _.each(newData, function(record, idx) {
      if (!_.isUndefined(record)) {
        this.datum[idx] = record
      }
    }, this)

    // check our thresholds and update color
    var lastValue = _.last(this.datum).y
    var warning = this.metricSpec.warning;
    var critical = this.metricSpec.critical;
    if (critical > warning) {
      if (lastValue >= critical) {
        this.setColor(this.dashboardOptions.criticalColor)
      } else if (lastValue >= warning) {
        this.setColor(this.dashboardOptions.warningColor)
      } else {
        this.setColor(this.dashboardOptions.normalColor)
      }
    } else {
      if (lastValue <= critical) {
        this.setColor(this.dashboardOptions.criticalColor)
      } else if (lastValue <= warning) {
        this.setColor(this.dashboardOptions.warningColor)
      } else {
        this.setColor(this.dashboardOptions.normalColor)
      }
    }
  },

  redraw: function() {
    var element = $(this.element);

    // update our graph
    this.graph.update();

    element.find('.overlay-name').text(this.alias);

    if (!_.isUndefined(_.last(this.datum))) {
      var lastValue = _.last(this.datum).y
      var lastValueDisplay;
      if (_.isNumber(lastValue) && lastValue < 2.0) {
        lastValueDisplay = Math.round(lastValue*1000)/1000;
      } else {
        lastValueDisplay = parseInt(lastValue);
      }
      if (this.description) {
        element.find('.description').html('Note:<br /><br />' + this.description);
      }
      element.find('.overlay-name').text(this.alias);
      element.find('.overlay-number').text(lastValueDisplay);
      if (!_.isUndefined(this.metricSpec.unit)) {
        element.find('.overlay-number').append('<span class="unit">' + this.metricSpec.unit + '</span>');
      }
    } else {
      element.find('.overlay-number').html('<span class="error">NF</span>');
    }
  }
}


/* Graphite datasource */

function TasseoGraphiteDatasource(url, auth, period, options) {
  this.baseUrl = url;
  this.auth = auth;
  this.period = period
  this.options = _.extend({
    padnulls: true
  })
}

TasseoGraphiteDatasource.prototype = {
  refresh: function(metrics, period, callback) {
    $.ajax({
      beforeSend: function(xhr) {
        if (this.auth && this.auth.length > 0) {
          var bytes = Crypto.charenc.Binary.stringToBytes(this.auth);
          var base64 = Crypto.util.bytesToBase64(bytes);
          xhr.setRequestHeader('Authorization', 'Basic ' + base64);
        }
      },
      dataType: 'json',
      error: function(xhr, textStatus, errorThrown) { console.log(errorThrown); },
      url: this.urlForMetrics(metrics, period)
    }).done(function(d) {
      if (d.length > 0) {
        _.each(d, function(datapoints, idx) {
          var metric = metrics[idx];
          metric.update(_.map(datapoints, function(datapoint) {
            return { x: datapoint[1], y: datapoint[0] }
          }))

          callback.call(metric)
        })
      }
    })
  },

  urlForMetrics: function(metrics, period) {
    var targets = _.map(metrics, function(metric) {
      if (this.options.padnulls) {
        'target=keepLastValue(' + encodeURI(metric.metricSpec.target) + ')'
      } else {
        'target=' + encodeURI(metric.metricSpec.target)
      }
    }).join("&");

    var myUrl = this.url + '/render/?' + targets + '&from=-' + period + 'minutes&format=json';
    return myUrl;
  }
}

/* UI functionality */

function TasseoUi(dashboard, options) {
  this.dashboard = dashboard;
  this.options = _.extend({
    darkMode: false,
    title: true,
    toolbar: true,
    refreshInterval: 2000,
    realtimePeriod: 5
  })

  this.setDarkMode(this.options.darkMode)

  if (!this.options.title) {
    $('div.title span').css('visibility', 'hidden');
  }
<commit_msg>Fix a couple small issues with graphite datasource<commit_after>/* Dashboard object */

function TasseoDashboard(metrics, datasource, period, options) {
  this.options = $.extend({
    interpolation: 'step-after',
    renderer: 'area',
    stroke: true,
    criticalColor: '#d59295',
    warningColor: '#f5cb56',
    normalColor: '#afdab1',
    container: '.main'
  }, options)

  this.period = period;
  this.datasource = datasource;
  this.buildContainers(metrics);
  this.createGraphs(metrics);
}

TasseoDashboard.prototype = {
  createGraphs: function(metrics) {
    var filtered = []
    _.each(metrics, function(metric, idx) {
      if (_.isString(metric.target)) {
        var element = document.querySelector('.graph' + idx);
        filtered.push(new TasseoMetric(element, metric, this.options));
      }
    }, this);

    this.metrics = filtered;
  },

  clearGraphs: function() {
    _.each(this.metrics, function(metric) {
      metric.clear();
    })
  },

  // add our containers
  buildContainers: function(metrics) {
    var falseTargets = 0;
    for (var i=0; i<metrics.length; i++) {
      if (metrics[i].target === false) {
        $(this.options.container).append('<div class="false"></div>');
        falseTargets++;
      } else {
        var j = i - falseTargets;
        var link_open = 'link' in metrics[i] ? '<a href="' + metrics[i].link + '" target="_new">' : '';
        var link_close = 'link' in metrics[i] ? '</a>' : '';
        var graph_div =
        '<div id="' + j + '" class="graph graph' + j + '">' +
        '<span class="description description' + j + '"></span>' +
        link_open + '<div class="overlay-name overlay-name' + j + '"></div>' + link_close +
        '<div class="overlay-number overlay-number' + j + '"></div>' +
        '</div>';
        $(this.options.container).append(graph_div);
      }
    }
  },

  setPeriod: function(period) {
    this.period = period;
  },

  refreshData: function(immediately) {
    this.datasource.refresh(this.metrics, this.period, function(metric) {
      if (immediately) {
        metric.redraw()
      }
    });

    if (!immediately) {
      _.each(this.metrics, function(metric) {
        metric.redraw()
      });
    }
  }
}


function TasseoMetric(element, metricSpec, dashboardOptions) {
  this.element = element
  this.alias = metricSpec.alias || metricSpec.target;
  this.description = metricSpec.description;
  this.metricSpec = metricSpec
  this.dashboardOptions = dashboardOptions;

  this.clear();
}

TasseoMetric.prototype = {
  getTarget: function() {
    return this.metricSpec.target;
  },
  clear: function() {
    this.datum = [{ x:0, y:0 }];
    this.graph = new Rickshaw.Graph({
      element: this.element,
      width: this.dashboardOptions.graph_width || 348,
      height: this.dashboardOptions.graph_height || 100,
      interpolation: this.dashboardOptions.interpolation,
      renderer: this.dashboardOptions.renderer,
      stroke: this.dashboardOptions.stroke,
      series: [{
        name: this.alias,
        color: this.dashboardOptions.normalColor,
        data: this.datum
      }]
    });

    this.graph.render();
  },

  setColor: function(color) {
    this.graph.series[0].color = color;
  },

  update: function(newData) {
    _.each(newData, function(record, idx) {
      if (!_.isUndefined(record)) {
        this.datum[idx] = record
      }
    }, this)

    // check our thresholds and update color
    var lastValue = _.last(this.datum).y
    var warning = this.metricSpec.warning;
    var critical = this.metricSpec.critical;
    if (critical > warning) {
      if (lastValue >= critical) {
        this.setColor(this.dashboardOptions.criticalColor)
      } else if (lastValue >= warning) {
        this.setColor(this.dashboardOptions.warningColor)
      } else {
        this.setColor(this.dashboardOptions.normalColor)
      }
    } else {
      if (lastValue <= critical) {
        this.setColor(this.dashboardOptions.criticalColor)
      } else if (lastValue <= warning) {
        this.setColor(this.dashboardOptions.warningColor)
      } else {
        this.setColor(this.dashboardOptions.normalColor)
      }
    }
  },

  redraw: function() {
    var element = $(this.element);

    // update our graph
    this.graph.update();

    element.find('.overlay-name').text(this.alias);

    if (!_.isUndefined(_.last(this.datum))) {
      var lastValue = _.last(this.datum).y
      var lastValueDisplay;
      if (_.isNumber(lastValue) && lastValue < 2.0) {
        lastValueDisplay = Math.round(lastValue*1000)/1000;
      } else {
        lastValueDisplay = parseInt(lastValue);
      }
      if (this.description) {
        element.find('.description').html('Note:<br /><br />' + this.description);
      }
      element.find('.overlay-name').text(this.alias);
      element.find('.overlay-number').text(lastValueDisplay);
      if (!_.isUndefined(this.metricSpec.unit)) {
        element.find('.overlay-number').append('<span class="unit">' + this.metricSpec.unit + '</span>');
      }
    } else {
      element.find('.overlay-number').html('<span class="error">NF</span>');
    }
  }
}


/* Graphite datasource */

function TasseoGraphiteDatasource(url, auth, period, options) {
  this.baseUrl = url;
  this.auth = auth;
  this.period = period
  this.options = _.extend({
    padnulls: true
  })
}

TasseoGraphiteDatasource.prototype = {
  refresh: function(metrics, period, callback) {
    $.ajax({
      beforeSend: function(xhr) {
        if (this.auth && this.auth.length > 0) {
          var bytes = Crypto.charenc.Binary.stringToBytes(this.auth);
          var base64 = Crypto.util.bytesToBase64(bytes);
          xhr.setRequestHeader('Authorization', 'Basic ' + base64);
        }
      },
      dataType: 'json',
      error: function(xhr, textStatus, errorThrown) { console.log(errorThrown); },
      url: this.urlForMetrics(metrics, period)
    }).done(function(d) {
      if (d.length > 0) {
        _.each(d, function(datapoints, idx) {
          var metric = metrics[idx];
          metric.update(_.map(datapoints, function(datapoint) {
            return { x: datapoint[1], y: datapoint[0] }
          }))

          callback.call(metric)
        })
      }
    })
  },

  urlForMetrics: function(metrics, period) {
    var targets = _.map(metrics, function(metric) {
      if (this.options.padnulls) {
        return 'target=keepLastValue(' + encodeURI(metric.metricSpec.target) + ')'
      } else {
        return 'target=' + encodeURI(metric.metricSpec.target)
      }
    }, this).join("&");

    var myUrl = this.baseUrl + '/render/?' + targets + '&from=-' + period + 'minutes&format=json';
    return myUrl;
  }
}

/* UI functionality */

function TasseoUi(dashboard, options) {
  this.dashboard = dashboard;
  this.options = _.extend({
    darkMode: false,
    title: true,
    toolbar: true,
    refreshInterval: 2000,
    realtimePeriod: 5
  })

  this.setDarkMode(this.options.darkMode)

  if (!this.options.title) {
    $('div.title span').css('visibility', 'hidden');
  }

{'fraction_non_alphanumeric': 0.15339085787634602, 'fraction_numerical': 0.006418027526206946, 'mean_word_length': 2.720880870257363, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3601577', 'n_tokens_mistral': 4506, 'n_tokens_neox': 4403, 'n_words': 817}