<commit_before>'use strict';

var VisLinesExecution = Class.extend({
  init: function(divId, type, category) {
    // Set default locale based on the app setting
    this.locale = I18n.locale;
    this.localeFallback = this.locale === 'en' ? 'es' : this.locale;

    d3.formatDefaultLocale(eval(this.locale));
    d3.timeFormatDefaultLocale(eval(this.locale));

    this.container = divId;
    this.data = null;
    this.executionKind = type;
    this.budgetCategory = category;
    this.placeId = d3.select('body').attr('data-place-id');
    this.parseTime = d3.timeParse('%Y-%m-%d');
    this.pctFormat = d3.format(',');
    this.monthFormat = d3.timeFormat('%B %Y');
    this.dayFormat = d3.timeFormat('%e %B %Y');
    this.isMobile = window.innerWidth <= 768;
    this.selectionNode = d3.select(this.container).node();
    this.currentYear = new Date().getFullYear();
    this.budgetYear = d3.select('body').attr('data-year');

    // Income and expenses colors
    this.color = d3.scaleOrdinal()
      .domain(['I', 'G'])
      .range(['#f88f59', '#00909e']);

    // Create axes
    this.xAxis = d3.axisBottom();

    // Chart objects
    this.svg = null;
    this.chart = null;

    this.tooltip = d3.select(this.container)
      .append('div')
      .attr('class', 'tooltip');
  },
  getData: function() {
    this.dataUrl = '/api/data/widget/budget_execution_comparison/' + this.placeId + '/' + this.budgetYear + '/' + this.executionKind + '/' + this.budgetCategory + '.json';

    d3.json(this.dataUrl, function(error, jsonData) {
      if (error) throw error;

      this.data = jsonData;
      this.updated = this.parseTime(this.data.last_update);

      // Setting scales in a separate step, as we need the lines to set the height
      this.setScales();
      this.updateRender();
    }.bind(this));
  },
  render: function() {
    if (this.data === null) {
      this.getData();
    } else {
      this.updateRender();
    }
  },
  setScales: function(callback) {
    // Chart dimensions
    this.margin = {top: 55, right: 0, bottom: 50, left: this.isMobile ? 0 : 385};
    this.width = this._width() - this.margin.left - this.margin.right;
    this.height = this._height() - this.margin.top - this.margin.bottom;

    this.maxPct = d3.max(this.data.lines, function(d) { return d.pct_executed;});

    // Triggered if a budget line's execution is over 500%
    this.bigDeviation = this.maxPct > 500;

    // Scales & Ranges
    this.x = this.bigDeviation ? d3.scaleLog().range([0.1, this.width]) : d3.scaleLinear().range([0, this.width]).clamp(true);
    this.z = d3.scaleTime().range([0, this.width]);
    this.y0 = d3.scaleBand().rangeRound([this.height, 0]).paddingInner(0.5);
    this.y1 = d3.scaleBand().rangeRound([this.height, 0]).paddingInner(0.5).paddingOuter(0);

    // Create main elements
    this.svg = d3.select(this.container)
      .append('svg')
      .attr('width', this.width + this.margin.left + this.margin.right)
      .attr('height', this.height + this.margin.top + this.margin.bottom)
      .append('g')
      .attr('class', 'chart-container')
      .attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');

    this.xAxis = d3.axisTop(this.x)
      .tickFormat(function(d) { return d === 0 ? '' : this.pctFormat(d) + '%'}.bind(this))
      .tickSize(-this.height - this.margin.bottom)
      .tickPadding(10)
      .ticks(this.isMobile ? 2 : this.bigDeviation ? 3 : 5);

    // Message if custom lines are empty
    if (this.data.lines.length === 0) {
      d3.select(this.container).append('div')
        .attr('class', 'flash-message alert')
        .text(I18n.t('gobierto_budgets.budgets_execution.index.vis.empty_lines'));
    }
  },
  updateRender: function(callback) {
    this.nested = d3.nest()
      .key(function(d) { return d.parent_id;})
      .sortValues(function(a, b) {
        // Parent lines are the first for each group, and then child lines are sorted by execution rate
        return a.level === 1 || b.level === 1 ? b.level - a.level : a.pct_executed - b.pct_executed;
      })
      .entries(this.data.lines);

    // Get parent line values for each group to sort them later
    this.nested.forEach(function(d) {
      d.group_pct = d.values.filter(function(d) { return d.level == 1;}).map(function(d) { return d.pct_executed;})[0];
      d.group_executed = d.values.filter(function(d) { return d.level == 1;}).map(function(d) { return d.executed;})[0];

      return d;
    });

    if (this.data.lines.length === 0)
      return false;

    // Sort by execution
    this.nested.sort(function(a ,b) { return a.group_pct - b.group_pct;});

    /* Extent of the execution */
    this.x.domain(this.maxPct <= 100 ? [0.1, 100] : [0.1, this.maxPct]);<commit_msg>Sort second level by ID<commit_after>'use strict';

var VisLinesExecution = Class.extend({
  init: function(divId, type, category) {
    // Set default locale based on the app setting
    this.locale = I18n.locale;
    this.localeFallback = this.locale === 'en' ? 'es' : this.locale;

    d3.formatDefaultLocale(eval(this.locale));
    d3.timeFormatDefaultLocale(eval(this.locale));

    this.container = divId;
    this.data = null;
    this.executionKind = type;
    this.budgetCategory = category;
    this.placeId = d3.select('body').attr('data-place-id');
    this.parseTime = d3.timeParse('%Y-%m-%d');
    this.pctFormat = d3.format(',');
    this.monthFormat = d3.timeFormat('%B %Y');
    this.dayFormat = d3.timeFormat('%e %B %Y');
    this.isMobile = window.innerWidth <= 768;
    this.selectionNode = d3.select(this.container).node();
    this.currentYear = new Date().getFullYear();
    this.budgetYear = d3.select('body').attr('data-year');

    // Income and expenses colors
    this.color = d3.scaleOrdinal()
      .domain(['I', 'G'])
      .range(['#f88f59', '#00909e']);

    // Create axes
    this.xAxis = d3.axisBottom();

    // Chart objects
    this.svg = null;
    this.chart = null;

    this.tooltip = d3.select(this.container)
      .append('div')
      .attr('class', 'tooltip');
  },
  getData: function() {
    this.dataUrl = '/api/data/widget/budget_execution_comparison/' + this.placeId + '/' + this.budgetYear + '/' + this.executionKind + '/' + this.budgetCategory + '.json';

    d3.json(this.dataUrl, function(error, jsonData) {
      if (error) throw error;

      this.data = jsonData;
      this.updated = this.parseTime(this.data.last_update);

      // Setting scales in a separate step, as we need the lines to set the height
      this.setScales();
      this.updateRender();
    }.bind(this));
  },
  render: function() {
    if (this.data === null) {
      this.getData();
    } else {
      this.updateRender();
    }
  },
  setScales: function(callback) {
    // Chart dimensions
    this.margin = {top: 55, right: 0, bottom: 50, left: this.isMobile ? 0 : 385};
    this.width = this._width() - this.margin.left - this.margin.right;
    this.height = this._height() - this.margin.top - this.margin.bottom;

    this.maxPct = d3.max(this.data.lines, function(d) { return d.pct_executed;});

    // Triggered if a budget line's execution is over 500%
    this.bigDeviation = this.maxPct > 500;

    // Scales & Ranges
    this.x = this.bigDeviation ? d3.scaleLog().range([0.1, this.width]) : d3.scaleLinear().range([0, this.width]).clamp(true);
    this.z = d3.scaleTime().range([0, this.width]);
    this.y0 = d3.scaleBand().rangeRound([this.height, 0]).paddingInner(0.5);
    this.y1 = d3.scaleBand().rangeRound([this.height, 0]).paddingInner(0.5).paddingOuter(0);

    // Create main elements
    this.svg = d3.select(this.container)
      .append('svg')
      .attr('width', this.width + this.margin.left + this.margin.right)
      .attr('height', this.height + this.margin.top + this.margin.bottom)
      .append('g')
      .attr('class', 'chart-container')
      .attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');

    this.xAxis = d3.axisTop(this.x)
      .tickFormat(function(d) { return d === 0 ? '' : this.pctFormat(d) + '%'}.bind(this))
      .tickSize(-this.height - this.margin.bottom)
      .tickPadding(10)
      .ticks(this.isMobile ? 2 : this.bigDeviation ? 3 : 5);

    // Message if custom lines are empty
    if (this.data.lines.length === 0) {
      d3.select(this.container).append('div')
        .attr('class', 'flash-message alert')
        .text(I18n.t('gobierto_budgets.budgets_execution.index.vis.empty_lines'));
    }
  },
  updateRender: function(callback) {
    this.nested = d3.nest()
      .key(function(d) { return d.parent_id;})
      .sortValues(function(a, b) {
        // Parent lines are the first for each group, and then child lines are sorted by execution rate
        return a.level === 1 || b.level === 1 ? b.level - a.level : b.id - a.id;
      })
      .entries(this.data.lines);

    // Get parent line values for each group to sort them later
    this.nested.forEach(function(d) {
      d.group_pct = d.values.filter(function(d) { return d.level == 1;}).map(function(d) { return d.pct_executed;})[0];
      d.group_executed = d.values.filter(function(d) { return d.level == 1;}).map(function(d) { return d.executed;})[0];

      return d;
    });

    if (this.data.lines.length === 0)
      return false;

    // Sort by execution
    this.nested.sort(function(a ,b) { return a.group_pct - b.group_pct;});

    /* Extent of the execution */
    this.x.domain(this.maxPct <= 100 ? [0.1, 100] : [0.1, this.maxPct]);
{'fraction_non_alphanumeric': 0.17295929128875764, 'fraction_numerical': 0.019194262813752375, 'mean_word_length': 2.891259745588839, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, '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': '5109754', 'n_tokens_mistral': 3343, 'n_tokens_neox': 3196, 'n_words': 649}