<commit_before>/**
 * Visual Blocks Language
 *
 * Copyright 2012 Google Inc.
 * http://code.google.com/p/blockly/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Factory for building blocks.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

/**
 * The uneditable container block that everything else attaches to.
 * @type {Blockly.Block}
 */
var rootBlock = null;

/**
 * The uneditable preview block.
 * @type {Blockly.Block}
 */
var previewBlock = null;

/**
 * The type of the generated block.
 */
var blockType = '';

/**
 * Initialize Blockly.  Called on page load.
 * @param {!Blockly} blockly Instance of Blockly from iframe.
 */
function initPreview(blockly) {
  window.Blockly = blockly;
  if (window.EditorBlockly) {
    // If the main editor has already loaded, update the preview.
    updatePreview();
  }
}

/**
 * Initialize Blockly.  Called on page load.
 * @param {!Blockly} blockly Instance of Blockly from iframe.
 */
function initEditor(blockly) {
  //window.onbeforeunload = function() {
  //  return 'Leaving this page will result in the loss of your work.';
  //};

  window.EditorBlockly = blockly;

  // Create the root block.
  rootBlock = new EditorBlockly.Block(EditorBlockly.mainWorkspace,
                                      'factory_base');
  rootBlock.initSvg();
  rootBlock.render();
  rootBlock.editable = false;

  bindEvent(EditorBlockly.mainWorkspace.getCanvas(),
      'blocklyWorkspaceChange', onchange);
}

/**
 * Bind an event to a function call.
 * @param {!Element} element Element upon which to listen.
 * @param {string} name Event name to listen to (e.g. 'mousedown').
 * @param {!Function} func Function to call when event is triggered.
 *     W3 browsers will call the function with the event object as a parameter,
 *     MSIE will not.
 */
function bindEvent(element, name, func) {
  if (element.addEventListener) {  // W3C
    element.addEventListener(name, func, false);
  } else if (element.attachEvent) {  // IE
    element.attachEvent('on' + name, func);
  }
}

/**
 * When the workspace changes, update the three other displays.
 */
function onchange() {
  var cat = rootBlock.getTitleValue('CAT');
  var name = rootBlock.getTitleValue('NAME');
  var code = [];
  var type;
  if (cat && name) {
    type = cat + '_' + name;
  } else {
    type = name || cat || '_';
  }
  blockType = type.replace(/\W/g, '_').replace(/^(d)/, '_\\1').toLowerCase();
  updateLanguage();
  updateGenerator();
  updatePreview();
}

/**
 * Update the language code.
 */
function updateLanguage() {
  // Generate name and category.
  var cat = rootBlock.getTitleValue('CAT');
  var code = [];
  if (cat) {
    cat = cat.replace(/\\/g, '\\\\').replace(/'/g, '\\\'');
    cat = '\'' + cat + '\'';
  } else {
    cat = 'null';
  }
  code.push('Blockly.Language.' + blockType + ' = {');
  code.push('  category: ' + cat + ',');
  code.push('  helpUrl: \'http://www.example.com/\',');
  code.push('  init: function() {');
  // Generate colour.
  var colourBlock = rootBlock.getInputTargetBlock('COLOUR');
  if (colourBlock) {
    var hue = parseInt(colourBlock.getTitleValue('HUE'), 10);
    code.push('    this.setColour(' + hue + ');');
  }
  // Generate inputs.
  var TYPES = {'input_value': 'appendValueInput',
               'input_statement': 'appendStatementInput',
               'input_dummy': 'appendDummyInput'};
  var inputVarDefined = false;
  var contentsBlock = rootBlock.getInputTargetBlock('INPUTS');
  while (contentsBlock) {
    var align = contentsBlock.getTitleValue('ALIGN');
    var titles = getTitles(contentsBlock.getInputTargetBlock('TITLES'));
    var name = '';
    // Dummy inputs don't have names.  Other inputs do.
    if (contentsBlock.type != 'input_dummy') {
      name = escapeString(contentsBlock.getTitleValue('INPUTNAME'));
    }
    var check = getOptTypesFrom(contentsBlock, 'TYPE');
    code.push('    this.' + TYPES[contentsBlock.type] +
        '(' + name + ')');
    if (check && check != 'null') {
      code.push('        .setCheck(' + check + ')');
    }
    if (align != 'LEFT') {
      code.push('        .setAlign(Blockly.ALIGN_' + align + ')');
    }
    for (var x = 0; x < titles.length; x++) {
      code.push('        .appendTitle(' + titles[x] + ')');
    }
    // Add semicolon to last line to finish the statement.
    code[code.length - 1] += ';';
    contentsBlock = contentsBlock.nextConnection &&
        contentsBlock.nextConnection.targetBlock();
  }
  // Generate inline/external switch.
  if (rootBlock.getTitleValue('INLINE') == 'INT') {
    code.push('    this.setInputsInline(true);');
  }
  // Generate output, or next/previous connections.
  switch (rootBlock.getTitleValue('CONNECTIONS')) {
    case 'LEFT':
      var outputType = getOptTypesFrom(rootBlock, 'OUTPUTTYPE');
      if (outputType) {
        outputType = ', ' + outputType;
      }
      code.push('    this.setOutput(true' + outputType + ');');
      break;
    case 'BOTH':
      var topType = getOptTypesFrom(rootBlock, 'TOPTYPE');
      if (outputType) {
        outputType = ', ' + outputType;
      }
      code.push('    this.setPreviousStatement(true' + topType + ');');
      var bottomType = getOptTypesFrom(rootBlock, 'BOTTOMTYPE');
      if (outputType) {
        outputType = ', ' + outputType;
      }
      code.push('    this.setNextStatement(true' + bottomType + ');');
      break;
    case 'TOP':
      var topType = getOptTypesFrom(rootBlock, 'TOPTYPE');
      if (outputType) {
        outputType = ', ' + outputType;
      }
      code.push('    this.setPreviousStatement(true' + topType + ');');
      break;
    case 'BOTTOM':
      var bottomType = getOptTypesFrom(rootBlock, 'BOTTOMTYPE');
      if (outputType) {
        outputType = ', ' + outputType;
      }
      code.push('    this.setNextStatement(true' + bottomType + ');');
      break;
  }
  code.push('    this.setTooltip(\'\');');
  code.push('  }');
  code.push('};');

  var ta = document.getElementById('languageTextarea');
  ta.value = code.join('\n');
}

/**
 * Returns a title string and any config.
 * @param {!Blockly.Block} block Title block.
 * @return {string} Title string.
 */
function getTitles(block) {
  var titles = [];
  while (block) {
    switch (block.type) {
      case 'title_static':
        // Result: 'hello'
        titles.push(escapeString(block.getTitleValue('TEXT')));
        break;
      case 'title_input':
        // Result: new Blockly.FieldTextInput('Hello'), 'GREET'
        titles.push('new Blockly.FieldTextInput(' +
            escapeString(block.getTitleValue('TEXT')) + '), ' +
            escapeString(block.getTitleValue('TITLENAME')));
        break;
      case 'title_checkbox':
        // Result: new Blockly.FieldCheckbox('TRUE'), 'CHECK'
        titles.push('new Blockly.FieldCheckbox(' +
            escapeString(block.getTitleValue('CHECKED')) + '), ' +
            escapeString(block.getTitleValue('TITLENAME')));
        break;
      case 'title_variable':
        // Result:
        // new Blockly.FieldVariable('item'), 'VAR'
        var varname = block.getTitleValue('TEXT');
        varname = varname ? escapeString(varname) : 'null';
        titles.push('new Blockly.FieldVariable(' + varname + '), ' +
            escapeString(block.getTitleValue('TITLENAME')));
        break;
      case 'title_dropdown':
        // Result:
        // new Blockly.FieldDropdown([['yes', '1'], ['no', '0']]), 'TOGGLE'
        var options = [];
        for (var x = 0; x < block.optionCount_; x++) {
          options[x] = '[' + escapeString(block.getTitleValue('USER' + x)) +
              ', ' + escapeString(block.getTitleValue('CPU' + x)) + ']';
        }
        if (options.length) {
          titles.push('new Blockly.FieldDropdown([' +
              options.join(', ') + ']), ' +
              escapeString(block.getTitleValue('TITLENAME')));
        }
        break;
      case 'title_image':
        // Result: new Blockly.FieldImage('http://...', 80, 60)
        var src = escapeString(block.getTitleValue('SRC'));
        var width = Number(block.getTitleValue('WIDTH'));
        var height = Number(block.getTitleValue('HEIGHT'));
        titles.push('new Blockly.FieldImage(' +
            src + ', ' + width + ', ' + height + ')');
        break;
    }
    block = block.nextConnection && block.nextConnection.targetBlock();
  }
  return titles;
}

/**
 * Escape a string.
 * @param {string} string String to escape.
 * @return {string} Escaped string surrouned by quotes.
 */
function escapeString(string) {
  if (JSON && JSON.stringify) {
    return JSON.stringify(string);
  }
  // Hello MSIE 8.
  return '"' + string.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
}

/**
 * Fetch the type(s) defined in the given input.
 * Format as a string for appending to the generated code.
 * @param {!Blockly.Block} block Block with input.
 * @param {string} name Name of the input.
 * @return {string} String defining the types.
 */
function getOptTypesFrom(block, name) {
  var types = getTypesFrom_(block, name);
  if (types.length == 0) {
    return '';
  } else if (types.length == 1) {
    return types[0];
  } else if (types.indexOf('null') != -1) {
    return 'null';
  } else {
    return '[' + types.join(', ') + ']';
  }
}

/**
 * Fetch the type(s) defined in the given input.
 * @param {!Blockly.Block} block Block with input.
 * @param {string} name Name of the input.
 * @return {!Array.<string>} List of types.
 * @private
 */
function getTypesFrom_(block, name) {
  var typeBlock = block.getInputTargetBlock(name);
  var types;
  if (!typeBlock) {
    types = [];
  } else if (typeBlock.type == 'type_other') {
    types = [escapeString(typeBlock.getTitleValue('TYPE'))];
  } else if (typeBlock.type == 'type_group') {
    types = [];
    for (var n = 0; n < typeBlock.typeCount_; n++) {
      types = types.concat(getTypesFrom_(typeBlock, 'TYPE' + n));
    }
    // Remove duplicates.
    var hash = {};
    for (var n = types.length - 1; n >= 0; n--) {
      if (hash['X_' + types[n]]) {
        types.splice(n, 1);
        continue;
      }
      hash['X_' + types[n]] = true;
    }
  } else {
    types = [typeBlock.valueType];
  }
  return types;
}

/**
 * Update the generator code.
 */
function updateGenerator() {
  function makeVar(root, name) {
    name = name.toLowerCase().replace(/\W/g, '_');
    return '  var ' + root + '_' + name;
  }
  var language = document.getElementById('language').value;
  var code = [];
  code.push('Blockly.' + language + '.' + blockType + ' = function() {');
  // Loop through every block, and generate getters for any fields or inputs.
  var blocks = rootBlock.getDescendants();
  for (var x = 0, block; block = blocks[x]; x++) {
    switch (block.type) {
      case 'title_input':
        var name = block.getTitleValue('TITLENAME');
        code.push(makeVar('text', name) +
                  ' = this.getTitleValue(\'' + name + '\');');
        break;
      case 'title_dropdown':
        var name = block.getTitleValue('TITLENAME');
        code.push(makeVar('dropdown', name) +
                  ' = this.getTitleValue(\'' + name + '\');');
        break;
      case 'title_variable':
        var name = block.getTitleValue('TITLENAME');
        code.push(makeVar('variable', name) +
                  ' = Blockly.' + language +
                  '.variableDB_.getName(this.getTitleValue(\'' + name +
                  '\'), Blockly.Variables.NAME_TYPE);');
        break;
      case 'input_value':
        var name = block.getTitleValue('INPUTNAME');
        code.push(makeVar('value', name) +
                  ' = Blockly.' + language + '.valueToCode(this, \'' + name +
                  '\', Blockly.' + language + '.ORDER_ATOMIC);');
        break;
      case 'input_statement':
        var name = block.getTitleValue('INPUTNAME');
        code.push(makeVar('statements', name) +
                  ' = Blockly.' + language + '.statementToCode(this, \'' +
                  name + '\');');
        break;
    }
  }
  code.push('  // TODO: Assemble ' + language + ' into code variable.');
  code.push('  var code = \'...\'');
  if (rootBlock.getTitleValue('CONNECTIONS') == 'LEFT') {
    code.push('  // TODO: Change ORDER_NONE to the correct strength.');
    code.push('  return [code, Blockly.' + language + '.ORDER_NONE];');
  } else {
    code.push('  return code;');
  }
  code.push('};');

  var ta = document.getElementById('generatorTextarea');
  ta.value = code.join('\n');
}

/**
 * Update the preview display.
 */
function updatePreview() {
  if (!Blockly) {
    // If the preview frame hasn't loaded yet, don't try to update.
    return;
  }
  if (previewBlock) {
    previewBlock.dispose();
  }
  var type = blockType;
  var code = document.getElementById('languageTextarea').value;
  eval(code);
  // Create the preview block.
  previewBlock = new Blockly.Block(Blockly.mainWorkspace, type);
  previewBlock.initSvg();
  previewBlock.render();
  previewBlock.editable = false;
}

<commit_msg>Add code generator for checkbox.<commit_after>/**
 * Visual Blocks Language
 *
 * Copyright 2012 Google Inc.
 * http://code.google.com/p/blockly/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Factory for building blocks.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

/**
 * The uneditable container block that everything else attaches to.
 * @type {Blockly.Block}
 */
var rootBlock = null;

/**
 * The uneditable preview block.
 * @type {Blockly.Block}
 */
var previewBlock = null;

/**
 * The type of the generated block.
 */
var blockType = '';

/**
 * Initialize Blockly.  Called on page load.
 * @param {!Blockly} blockly Instance of Blockly from iframe.
 */
function initPreview(blockly) {
  window.Blockly = blockly;
  if (window.EditorBlockly) {
    // If the main editor has already loaded, update the preview.
    updatePreview();
  }
}

/**
 * Initialize Blockly.  Called on page load.
 * @param {!Blockly} blockly Instance of Blockly from iframe.
 */
function initEditor(blockly) {
  //window.onbeforeunload = function() {
  //  return 'Leaving this page will result in the loss of your work.';
  //};

  window.EditorBlockly = blockly;

  // Create the root block.
  rootBlock = new EditorBlockly.Block(EditorBlockly.mainWorkspace,
                                      'factory_base');
  rootBlock.initSvg();
  rootBlock.render();
  rootBlock.editable = false;

  bindEvent(EditorBlockly.mainWorkspace.getCanvas(),
      'blocklyWorkspaceChange', onchange);
}

/**
 * Bind an event to a function call.
 * @param {!Element} element Element upon which to listen.
 * @param {string} name Event name to listen to (e.g. 'mousedown').
 * @param {!Function} func Function to call when event is triggered.
 *     W3 browsers will call the function with the event object as a parameter,
 *     MSIE will not.
 */
function bindEvent(element, name, func) {
  if (element.addEventListener) {  // W3C
    element.addEventListener(name, func, false);
  } else if (element.attachEvent) {  // IE
    element.attachEvent('on' + name, func);
  }
}

/**
 * When the workspace changes, update the three other displays.
 */
function onchange() {
  var cat = rootBlock.getTitleValue('CAT');
  var name = rootBlock.getTitleValue('NAME');
  var code = [];
  var type;
  if (cat && name) {
    type = cat + '_' + name;
  } else {
    type = name || cat || '_';
  }
  blockType = type.replace(/\W/g, '_').replace(/^(d)/, '_\\1').toLowerCase();
  updateLanguage();
  updateGenerator();
  updatePreview();
}

/**
 * Update the language code.
 */
function updateLanguage() {
  // Generate name and category.
  var cat = rootBlock.getTitleValue('CAT');
  var code = [];
  if (cat) {
    cat = cat.replace(/\\/g, '\\\\').replace(/'/g, '\\\'');
    cat = '\'' + cat + '\'';
  } else {
    cat = 'null';
  }
  code.push('Blockly.Language.' + blockType + ' = {');
  code.push('  category: ' + cat + ',');
  code.push('  helpUrl: \'http://www.example.com/\',');
  code.push('  init: function() {');
  // Generate colour.
  var colourBlock = rootBlock.getInputTargetBlock('COLOUR');
  if (colourBlock) {
    var hue = parseInt(colourBlock.getTitleValue('HUE'), 10);
    code.push('    this.setColour(' + hue + ');');
  }
  // Generate inputs.
  var TYPES = {'input_value': 'appendValueInput',
               'input_statement': 'appendStatementInput',
               'input_dummy': 'appendDummyInput'};
  var inputVarDefined = false;
  var contentsBlock = rootBlock.getInputTargetBlock('INPUTS');
  while (contentsBlock) {
    var align = contentsBlock.getTitleValue('ALIGN');
    var titles = getTitles(contentsBlock.getInputTargetBlock('TITLES'));
    var name = '';
    // Dummy inputs don't have names.  Other inputs do.
    if (contentsBlock.type != 'input_dummy') {
      name = escapeString(contentsBlock.getTitleValue('INPUTNAME'));
    }
    var check = getOptTypesFrom(contentsBlock, 'TYPE');
    code.push('    this.' + TYPES[contentsBlock.type] +
        '(' + name + ')');
    if (check && check != 'null') {
      code.push('        .setCheck(' + check + ')');
    }
    if (align != 'LEFT') {
      code.push('        .setAlign(Blockly.ALIGN_' + align + ')');
    }
    for (var x = 0; x < titles.length; x++) {
      code.push('        .appendTitle(' + titles[x] + ')');
    }
    // Add semicolon to last line to finish the statement.
    code[code.length - 1] += ';';
    contentsBlock = contentsBlock.nextConnection &&
        contentsBlock.nextConnection.targetBlock();
  }
  // Generate inline/external switch.
  if (rootBlock.getTitleValue('INLINE') == 'INT') {
    code.push('    this.setInputsInline(true);');
  }
  // Generate output, or next/previous connections.
  switch (rootBlock.getTitleValue('CONNECTIONS')) {
    case 'LEFT':
      var outputType = getOptTypesFrom(rootBlock, 'OUTPUTTYPE');
      if (outputType) {
        outputType = ', ' + outputType;
      }
      code.push('    this.setOutput(true' + outputType + ');');
      break;
    case 'BOTH':
      var topType = getOptTypesFrom(rootBlock, 'TOPTYPE');
      if (outputType) {
        outputType = ', ' + outputType;
      }
      code.push('    this.setPreviousStatement(true' + topType + ');');
      var bottomType = getOptTypesFrom(rootBlock, 'BOTTOMTYPE');
      if (outputType) {
        outputType = ', ' + outputType;
      }
      code.push('    this.setNextStatement(true' + bottomType + ');');
      break;
    case 'TOP':
      var topType = getOptTypesFrom(rootBlock, 'TOPTYPE');
      if (outputType) {
        outputType = ', ' + outputType;
      }
      code.push('    this.setPreviousStatement(true' + topType + ');');
      break;
    case 'BOTTOM':
      var bottomType = getOptTypesFrom(rootBlock, 'BOTTOMTYPE');
      if (outputType) {
        outputType = ', ' + outputType;
      }
      code.push('    this.setNextStatement(true' + bottomType + ');');
      break;
  }
  code.push('    this.setTooltip(\'\');');
  code.push('  }');
  code.push('};');

  var ta = document.getElementById('languageTextarea');
  ta.value = code.join('\n');
}

/**
 * Returns a title string and any config.
 * @param {!Blockly.Block} block Title block.
 * @return {string} Title string.
 */
function getTitles(block) {
  var titles = [];
  while (block) {
    switch (block.type) {
      case 'title_static':
        // Result: 'hello'
        titles.push(escapeString(block.getTitleValue('TEXT')));
        break;
      case 'title_input':
        // Result: new Blockly.FieldTextInput('Hello'), 'GREET'
        titles.push('new Blockly.FieldTextInput(' +
            escapeString(block.getTitleValue('TEXT')) + '), ' +
            escapeString(block.getTitleValue('TITLENAME')));
        break;
      case 'title_checkbox':
        // Result: new Blockly.FieldCheckbox('TRUE'), 'CHECK'
        titles.push('new Blockly.FieldCheckbox(' +
            escapeString(block.getTitleValue('CHECKED')) + '), ' +
            escapeString(block.getTitleValue('TITLENAME')));
        break;
      case 'title_variable':
        // Result:
        // new Blockly.FieldVariable('item'), 'VAR'
        var varname = block.getTitleValue('TEXT');
        varname = varname ? escapeString(varname) : 'null';
        titles.push('new Blockly.FieldVariable(' + varname + '), ' +
            escapeString(block.getTitleValue('TITLENAME')));
        break;
      case 'title_dropdown':
        // Result:
        // new Blockly.FieldDropdown([['yes', '1'], ['no', '0']]), 'TOGGLE'
        var options = [];
        for (var x = 0; x < block.optionCount_; x++) {
          options[x] = '[' + escapeString(block.getTitleValue('USER' + x)) +
              ', ' + escapeString(block.getTitleValue('CPU' + x)) + ']';
        }
        if (options.length) {
          titles.push('new Blockly.FieldDropdown([' +
              options.join(', ') + ']), ' +
              escapeString(block.getTitleValue('TITLENAME')));
        }
        break;
      case 'title_image':
        // Result: new Blockly.FieldImage('http://...', 80, 60)
        var src = escapeString(block.getTitleValue('SRC'));
        var width = Number(block.getTitleValue('WIDTH'));
        var height = Number(block.getTitleValue('HEIGHT'));
        titles.push('new Blockly.FieldImage(' +
            src + ', ' + width + ', ' + height + ')');
        break;
    }
    block = block.nextConnection && block.nextConnection.targetBlock();
  }
  return titles;
}

/**
 * Escape a string.
 * @param {string} string String to escape.
 * @return {string} Escaped string surrouned by quotes.
 */
function escapeString(string) {
  if (JSON && JSON.stringify) {
    return JSON.stringify(string);
  }
  // Hello MSIE 8.
  return '"' + string.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
}

/**
 * Fetch the type(s) defined in the given input.
 * Format as a string for appending to the generated code.
 * @param {!Blockly.Block} block Block with input.
 * @param {string} name Name of the input.
 * @return {string} String defining the types.
 */
function getOptTypesFrom(block, name) {
  var types = getTypesFrom_(block, name);
  if (types.length == 0) {
    return '';
  } else if (types.length == 1) {
    return types[0];
  } else if (types.indexOf('null') != -1) {
    return 'null';
  } else {
    return '[' + types.join(', ') + ']';
  }
}

/**
 * Fetch the type(s) defined in the given input.
 * @param {!Blockly.Block} block Block with input.
 * @param {string} name Name of the input.
 * @return {!Array.<string>} List of types.
 * @private
 */
function getTypesFrom_(block, name) {
  var typeBlock = block.getInputTargetBlock(name);
  var types;
  if (!typeBlock) {
    types = [];
  } else if (typeBlock.type == 'type_other') {
    types = [escapeString(typeBlock.getTitleValue('TYPE'))];
  } else if (typeBlock.type == 'type_group') {
    types = [];
    for (var n = 0; n < typeBlock.typeCount_; n++) {
      types = types.concat(getTypesFrom_(typeBlock, 'TYPE' + n));
    }
    // Remove duplicates.
    var hash = {};
    for (var n = types.length - 1; n >= 0; n--) {
      if (hash['X_' + types[n]]) {
        types.splice(n, 1);
        continue;
      }
      hash['X_' + types[n]] = true;
    }
  } else {
    types = [typeBlock.valueType];
  }
  return types;
}

/**
 * Update the generator code.
 */
function updateGenerator() {
  function makeVar(root, name) {
    name = name.toLowerCase().replace(/\W/g, '_');
    return '  var ' + root + '_' + name;
  }
  var language = document.getElementById('language').value;
  var code = [];
  code.push('Blockly.' + language + '.' + blockType + ' = function() {');
  // Loop through every block, and generate getters for any fields or inputs.
  var blocks = rootBlock.getDescendants();
  for (var x = 0, block; block = blocks[x]; x++) {
    switch (block.type) {
      case 'title_input':
        var name = block.getTitleValue('TITLENAME');
        code.push(makeVar('text', name) +
                  ' = this.getTitleValue(\'' + name + '\');');
        break;
      case 'title_dropdown':
        var name = block.getTitleValue('TITLENAME');
        code.push(makeVar('dropdown', name) +
                  ' = this.getTitleValue(\'' + name + '\');');
        break;
      case 'title_checkbox':
        var name = block.getTitleValue('TITLENAME');
        code.push(makeVar('checkbox', name) +
                  ' = this.getTitleValue(\'' + name + '\') == \'TRUE\';');
        break;
      case 'title_variable':
        var name = block.getTitleValue('TITLENAME');
        code.push(makeVar('variable', name) +
                  ' = Blockly.' + language +
                  '.variableDB_.getName(this.getTitleValue(\'' + name +
                  '\'), Blockly.Variables.NAME_TYPE);');
        break;
      case 'input_value':
        var name = block.getTitleValue('INPUTNAME');
        code.push(makeVar('value', name) +
                  ' = Blockly.' + language + '.valueToCode(this, \'' + name +
                  '\', Blockly.' + language + '.ORDER_ATOMIC);');
        break;
      case 'input_statement':
        var name = block.getTitleValue('INPUTNAME');
        code.push(makeVar('statements', name) +
                  ' = Blockly.' + language + '.statementToCode(this, \'' +
                  name + '\');');
        break;
    }
  }
  code.push('  // TODO: Assemble ' + language + ' into code variable.');
  code.push('  var code = \'...\'');
  if (rootBlock.getTitleValue('CONNECTIONS') == 'LEFT') {
    code.push('  // TODO: Change ORDER_NONE to the correct strength.');
    code.push('  return [code, Blockly.' + language + '.ORDER_NONE];');
  } else {
    code.push('  return code;');
  }
  code.push('};');

  var ta = document.getElementById('generatorTextarea');
  ta.value = code.join('\n');
}

/**
 * Update the preview display.
 */
function updatePreview() {
  if (!Blockly) {
    // If the preview frame hasn't loaded yet, don't try to update.
    return;
  }
  if (previewBlock) {
    previewBlock.dispose();
  }
  var type = blockType;
  var code = document.getElementById('languageTextarea').value;
  eval(code);
  // Create the preview block.
  previewBlock = new Blockly.Block(Blockly.mainWorkspace, type);
  previewBlock.initSvg();
  previewBlock.render();
  previewBlock.editable = false;
}

<|endoftext|>


<commit_before>import EmberRouter from '@ember/routing/router';
import config from './config/environment';
import GoogleAnalyticsRoute from 'ember-tracker/mixins/google-analytics-route';

const Router = EmberRouter.extend(GoogleAnalyticsRoute, {
  location: config.locationType,
  rootURL: config.rootURL
});

Router.map(function() {
  this.route('providers', function() {
    this.route('show', { path: '/:provider_id' }, function() {
      this.route('settings');
      this.route('dois');
      this.route('prefixes');
      this.route('users');
      this.route('clients');
    });
  });
  this.route('clients', function() {
    this.route('show', { path: '/:client_id' }, function() {
      this.route('settings');
      this.route('dois');
      this.route('transfer');
      this.route('prefixes');
      this.route('users');
    });
  });
  this.route('dois', function() {
    this.route('show', { path: '/:doi_id' });
  });
  this.route('settings', function() {
    this.route('show', { path: '/:provider_id' });
  });
  this.route('users', function() {
    this.route('show', { path: '/:user_id' }, function() {
      this.route('settings');
      this.route('dois');
    });
    this.route('new');
  });
  this.route('prefixes', function() {
    this.route('new');
  });
  this.route('provider-prefixes', function() {
    this.route('show', { path: '/:provider-prefix_id' });
    this.route('new');
  });
  this.route('client-prefixes', function() {
    this.route('show', { path: '/:client-prefix_id' });
    this.route('new');
  });

  //set up all of your known routes, and then...
  this.route("404", { path: "*path"});
});

export default Router;
<commit_msg>fix settings route<commit_after>import EmberRouter from '@ember/routing/router';
import config from './config/environment';
import GoogleAnalyticsRoute from 'ember-tracker/mixins/google-analytics-route';

const Router = EmberRouter.extend(GoogleAnalyticsRoute, {
  location: config.locationType,
  rootURL: config.rootURL
});

Router.map(function() {
  this.route('providers', function() {
    this.route('show', { path: '/:provider_id' }, function() {
      this.route('settings');
      this.route('dois');
      this.route('prefixes');
      this.route('users');
      this.route('clients');
    });
  });
  this.route('clients', function() {
    this.route('show', { path: '/:client_id' }, function() {
      this.route('settings');
      this.route('dois');
      this.route('transfer');
      this.route('prefixes');
      this.route('users');
    });
  });
  this.route('dois', function() {
    this.route('show', { path: '/:doi_id' });
  });
  this.route('settings', function() {
    this.route('index', { path: '/' });
  });
  this.route('users', function() {
    this.route('show', { path: '/:user_id' }, function() {
      this.route('settings');
      this.route('dois');
    });
    this.route('new');
  });
  this.route('prefixes', function() {
    this.route('new');
  });
  this.route('provider-prefixes', function() {
    this.route('show', { path: '/:provider-prefix_id' });
    this.route('new');
  });
  this.route('client-prefixes', function() {
    this.route('show', { path: '/:client-prefix_id' });
    this.route('new');
  });

  //set up all of your known routes, and then...
  this.route("404", { path: "*path"});
});

export default Router;
<|endoftext|>


<commit_before>import React, {PropTypes, Component} from "react";
import {render} from "react-dom";
import {connect} from "react-redux";
import {Tabs, Tab} from "react-bootstrap";
import { Scrollbars } from 'react-custom-scrollbars';
import {browserHistory} from "react-router";
import { Panel } from 'react-bootstrap';
import {bindActionCreators} from "redux";
import Dropzone from "react-dropzone";
import cn from "classnames";

import ActionsUtils from "../../actions/ActionsUtils";
import HttpService from "../../http/HttpService";
import DialogMessages from "../../common/DialogMessages";
import ProcessUtils from "../../common/ProcessUtils";
import SideNodeDetails from "./SideNodeDetails";
import NodeUtils from '../graph/NodeUtils'
import InlinedSvgs from "../../assets/icons/InlinedSvgs"
import Dialogs from "../modals/Dialogs"
import TogglePanel from "../TogglePanel";

import '../../stylesheets/userPanel.styl';

class UserRightPanel extends Component {

  static propTypes = {
    isOpened: React.PropTypes.bool.isRequired,
    graphLayoutFunction: React.PropTypes.func.isRequired,
    layout: React.PropTypes.array.isRequired,
    exportGraph: React.PropTypes.func.isRequired,
    zoomIn: React.PropTypes.func.isRequired,
    zoomOut: React.PropTypes.func.isRequired,
    featuresSettings: React.PropTypes.object.isRequired,
  }

  render() {
    const { isOpened, actions } = this.props;
    const buttonsInBusinessView = [
      "metrics",
      "compare",
      "export",
      "exportPDF",
      "zoomIn",
      "zoomOut",
      "properties",
      "counts",
    ];

    const config = this.getConfig();

    return (

        <TogglePanel type="right" isOpened={isOpened} onToggle={actions.toggleRightPanel}/>
        <Scrollbars renderThumbVertical={props =>
} hideTracksWhenNotNeeded={true}>

              <input type="checkbox" onChange={(e) => {
                this.props.actions.businessViewChanged(e.target.checked)
                this.props.actions.fetchProcessToDisplay(this.processId(), this.versionId(), e.target.checked)
              }}/>
              Business view

          {config.filter(obj => obj).map ((panel, panelIdx) => {
            return (
              <Panel key={panelIdx} collapsible defaultExpanded header={panel.panelName}>
                {panel.buttons.map((panelButton, idx) => this.renderPanelButton(panelButton, idx, buttonsInBusinessView))}
              </Panel>
            )}
          )}
          <Panel collapsible defaultExpanded header="Details">
            <SideNodeDetails/>
          </Panel>
        </Scrollbars>

    )
  }

  getConfig = () => {
    const saveDisabled = this.props.nothingToSave && this.props.processIsLatestVersion;
    const deployPossible = this.props.processIsLatestVersion && ProcessUtils.hasNoErrors(this.props.processToDisplay) && this.props.nothingToSave;
    const migratePossible = this.props.processIsLatestVersion && ProcessUtils.hasNoErrors(this.props.processToDisplay) && this.props.nothingToSave;

    return [
      (this.props.isSubprocess ? null : {
        panelName: "Deployment",
        buttons:[
          {name: "deploy", visible: this.props.loggedUser.canDeploy, disabled: !deployPossible, onClick: this.deploy, icon: InlinedSvgs.buttonDeploy},
          {name: "stop", visible: this.props.loggedUser.canDeploy, disabled: !this.isRunning(), onClick: this.stop, icon: InlinedSvgs.buttonStop},
          {name: "metrics", onClick: this.showMetrics, icon: InlinedSvgs.buttonMetrics}
        ]
      }),
      {
      panelName: "Process",
      buttons: [
        {name: "save" + (!saveDisabled ? "*" : ""), visible: this.props.loggedUser.canWrite, disabled: saveDisabled, onClick: this.save, icon: InlinedSvgs.buttonSave},
        {name: "migrate", visible: this.props.loggedUser.canDeploy && !_.isEmpty(this.props.featuresSettings.migration), disabled: !migratePossible, onClick: this.migrate, icon: InlinedSvgs.buttonMigrate},
        {name: "compare", onClick: this.compareVersions, icon: InlinedSvgs.compareButton, disabled: this.hasOneVersion()},
        {name: "import", visible: this.props.loggedUser.canWrite, disabled: false, onClick: this.importProcess, icon: InlinedSvgs.buttonImport, dropzone: true},
        {name: "export", onClick: this.exportProcess, icon: InlinedSvgs.buttonExport},
        {name: "exportPDF", disabled: !this.props.nothingToSave, onClick: this.exportProcessToPdf, icon: InlinedSvgs.buttonExport},
        {name: "zoomIn", onClick: this.props.zoomIn, icon: InlinedSvgs.buttonZoomIn},
        {name: "zoomOut", onClick: this.props.zoomOut, icon: InlinedSvgs.buttonZoomOut}

      ]
    },
      {
        panelName: "Edit",
        buttons: [
          {name: "undo", onClick: this.undo, icon: InlinedSvgs.buttonUndo},
          {name: "redo", onClick: this.redo, icon: InlinedSvgs.buttonRedo},
          {name: "align", onClick: this.props.graphLayoutFunction, icon: InlinedSvgs.buttonAlign, visible: this.props.loggedUser.canWrite},
          {name: "properties", onClick: this.showProperties, icon: InlinedSvgs.buttonSettings, visible: !this.props.isSubprocess},
          {name: "duplicate", onClick: this.duplicateNode, icon: InlinedSvgs.duplicateButton,
            //cloning groups can be tricky...
            disabled: this.noChosenNode(this.props.nodeToDisplay) || NodeUtils.nodeIsGroup(this.props.nodeToDisplay),
            visible: this.props.loggedUser.canWrite},
          {name: "delete", onClick: this.deleteNode, icon: InlinedSvgs.buttonDelete, visible: this.props.loggedUser.canWrite, disabled: this.noChosenNode(this.props.nodeToDisplay) }

        ]
      },
      //TODO: testing subprocesses should work, but currently we don't know how to pass parameters in sane way...
      (this.props.isSubprocess ? null : {
        panelName: "Test",
        buttons: [
          {name: "from file", onClick: this.testProcess, icon: InlinedSvgs.buttonFromFile, dropzone: true,
            disabled: !this.props.testCapabilities.canBeTested},
          {name: "hide", onClick: this.hideRunProcessDetails, icon: InlinedSvgs.buttonHide, disabled: !this.props.showRunProcessDetails},
          {name: "generate", onClick: this.generateData, icon: InlinedSvgs.buttonGenerate,
            disabled: !this.props.processIsLatestVersion || !this.props.testCapabilities.canGenerateTestData},
          {name: "counts", onClick: this.fetchProcessCounts, icon: InlinedSvgs.buttonCounts,
            visible: this.props.featuresSettings.counts && !this.props.isSubprocess},
        ]
      }),
      {
        panelName: "Group",
        buttons: [
          {name: "start", onClick: this.props.actions.startGrouping, icon: InlinedSvgs.buttonGroup, disabled: this.props.groupingState != null},
          {name: "finish", onClick: this.props.actions.finishGrouping, icon: InlinedSvgs.buttonGroup, disabled: (this.props.groupingState || []).length <= 1},
          {name: "cancel", onClick: this.props.actions.cancelGrouping, icon: InlinedSvgs.buttonUngroup, disabled: !this.props.groupingState },
          {name: "ungroup", onClick: this.ungroup, icon: InlinedSvgs.buttonUngroup, disabled: !NodeUtils.nodeIsGroup(this.props.nodeToDisplay) }
        ]
      }
    ];
  }

  renderPanelButton = (panelButton, idx, buttonsInBusinessView) => {
    const buttonClass = "espButton right-panel"
    return panelButton.visible === false || (this.props.businessView === true && !_.includes(buttonsInBusinessView, panelButton.name)) ? null :
      panelButton.dropzone ?
        <Dropzone key={idx} disableClick={panelButton.disabled === true} onDrop={panelButton.onClick}
                  className={"dropZone " + buttonClass + (panelButton.disabled === true ? " disabled" : "")}>

{panelButton.name}

          </Dropzone>
        :
        <button key={idx} type="button" className={buttonClass} disabled={panelButton.disabled === true}
                onClick={panelButton.onClick}>

 {panelButton.name}
        </button>
  }

  noChosenNode = (node) => {
    return NodeUtils.nodeIsProperties(node) || _.isEmpty(node)
  }

  isRunning = () => this.props.fetchedProcessDetails && (this.props.fetchedProcessDetails.currentlyDeployedAt || []).length > 0

  showProperties = () => {
    this.props.actions.displayModalNodeDetails(this.props.processToDisplay.properties)
  }

  save = () => {
    this.props.actions.toggleModalDialog(Dialogs.types.saveProcess)
  }

  migrate = () => {
    this.props.actions.toggleConfirmDialog(true, DialogMessages.migrate(this.processId(), this.props.featuresSettings.migration.targetEnvironmentId), () => {
      HttpService.migrateProcess(this.processId())
    })
  }

  deploy = () => {
    this.props.actions.toggleConfirmDialog(true, DialogMessages.deploy(this.processId()), () => {
      return HttpService.deploy(this.processId()).then((resp) => {
        this.fetchProcessDetails()
      })
    })
  }

  stop = () => {
    this.props.actions.toggleConfirmDialog(true, DialogMessages.stop(this.processId()), () => {
      return HttpService.stop(this.processId()).then((resp) => {
        this.fetchProcessDetails()
      })
    })
  }

  clearHistory = () => {
    return this.props.undoRedoActions.clear()
  }

  fetchProcessDetails = () => {
    this.props.actions.displayCurrentProcessVersion(this.processId())
  }

  processId = () => {
    return (this.props.processToDisplay || {}).id
  }

  versionId = () => this.props.fetchedProcessDetails.processVersionId

  showMetrics = () => {
    browserHistory.push('/metrics/' + this.processId())
  }

  exportProcess = () => {
    HttpService.exportProcess(this.processId(), this.versionId())
  }

  exportProcessToPdf = () => {
    const data = this.props.exportGraph()
    HttpService.exportProcessToPdf(this.processId(), this.versionId(), data, this.props.businessView)
  }

  generateData = () => {
    this.props.actions.toggleModalDialog(Dialogs.types.generateTestData)
  }

  compareVersions = () => {
    this.props.actions.toggleModalDialog(Dialogs.types.compareVersions)
  }

  fetchProcessCounts = () => {
    this.props.actions.toggleModalDialog(Dialogs.types.calculateCounts)
  }

  importProcess = (files) => {
    files.forEach((file)=>
      this.props.actions.importProcess(this.processId(), file)
    );
  }

  testProcess = (files) => {
    files.forEach((file)=>
      this.props.actions.testProcessFromFile(this.processId(), file, this.props.processToDisplay)
    );
  }

  hideRunProcessDetails = () => {
    this.props.actions.hideRunProcessDetails()
  }

  undo = () => {
    //this `if` should be closer to reducer?
    if (this.props.keyActionsAvailable) {
      this.props.undoRedoActions.undo()
    }
  }

  redo = () => {
    if (this.props.keyActionsAvailable) {
      this.props.undoRedoActions.redo()
    }
  }

  duplicateNode = () => {
    const duplicatedNodePosition = this.props.layout.find(node => node.id === this.props.nodeToDisplay.id) || {position: {x: 0, y: 0}}
    const visiblePosition = {x: duplicatedNodePosition.position.x -200, y: duplicatedNodePosition.position.y}
    this.props.actions.nodeAdded(this.props.nodeToDisplay, visiblePosition)
  }

  deleteNode = () => {
    this.props.actions.deleteNode(this.props.nodeToDisplay.id)
  }

  ungroup = () => {
    this.props.actions.ungroup(this.props.nodeToDisplay)
  }

  hasOneVersion = () => _.get(this.props.fetchedProcessDetails, 'history', []).length <= 1

}

function mapState(state) {
  const fetchedProcessDetails = state.graphReducer.fetchedProcessDetails
  return {
    isOpened: state.ui.rightPanelIsOpened,
    fetchedProcessDetails: fetchedProcessDetails,
    processToDisplay: state.graphReducer.processToDisplay,
    //TODO: now only needed for duplicate, maybe we can do it somehow differently?
    layout: state.graphReducer.layout || [],

    testCapabilities: state.graphReducer.testCapabilities || {},

    loggedUser: state.settings.loggedUser,
    nothingToSave: ProcessUtils.nothingToSave(state),
    showRunProcessDetails: !_.isEmpty(state.graphReducer.testResults) || !_.isEmpty(state.graphReducer.processCounts),
    keyActionsAvailable: state.ui.allModalsClosed,
    processIsLatestVersion: _.get(fetchedProcessDetails, 'isLatestVersion', false),
    nodeToDisplay: state.graphReducer.nodeToDisplay,
    groupingState: state.graphReducer.groupingState,
    featuresSettings: state.settings.featuresSettings,
    isSubprocess: _.get(fetchedProcessDetails, "properties.isSubprocess", false),
    businessView: state.graphReducer.businessView
  };
}

export default connect(mapState, ActionsUtils.mapDispatchWithEspActions)(UserRightPanel);
<commit_msg>[ITDEVESP-215] reader view - hide some buttons<commit_after>import React, {PropTypes, Component} from "react";
import {render} from "react-dom";
import {connect} from "react-redux";
import {Tabs, Tab} from "react-bootstrap";
import { Scrollbars } from 'react-custom-scrollbars';
import {browserHistory} from "react-router";
import { Panel } from 'react-bootstrap';
import {bindActionCreators} from "redux";
import Dropzone from "react-dropzone";
import cn from "classnames";

import ActionsUtils from "../../actions/ActionsUtils";
import HttpService from "../../http/HttpService";
import DialogMessages from "../../common/DialogMessages";
import ProcessUtils from "../../common/ProcessUtils";
import SideNodeDetails from "./SideNodeDetails";
import NodeUtils from '../graph/NodeUtils'
import InlinedSvgs from "../../assets/icons/InlinedSvgs"
import Dialogs from "../modals/Dialogs"
import TogglePanel from "../TogglePanel";

import '../../stylesheets/userPanel.styl';

class UserRightPanel extends Component {

  static propTypes = {
    isOpened: React.PropTypes.bool.isRequired,
    graphLayoutFunction: React.PropTypes.func.isRequired,
    layout: React.PropTypes.array.isRequired,
    exportGraph: React.PropTypes.func.isRequired,
    zoomIn: React.PropTypes.func.isRequired,
    zoomOut: React.PropTypes.func.isRequired,
    featuresSettings: React.PropTypes.object.isRequired,
  }

  render() {
    const { isOpened, actions } = this.props;

    const config = this.getConfig();

    return (

        <TogglePanel type="right" isOpened={isOpened} onToggle={actions.toggleRightPanel}/>
        <Scrollbars renderThumbVertical={props =>
} hideTracksWhenNotNeeded={true}>

              <input type="checkbox" onChange={(e) => {
                this.props.actions.businessViewChanged(e.target.checked)
                this.props.actions.fetchProcessToDisplay(this.processId(), this.versionId(), e.target.checked)
              }}/>
              Business view

          {config.filter(panel => panel).map ((panel, panelIdx) => {
              const visibleButtons = panel.buttons.filter(button => button.visible !== false)
              return _.isEmpty(visibleButtons) ? null : (
                <Panel key={panelIdx} collapsible defaultExpanded header={panel.panelName}>
                  {visibleButtons.map((panelButton, idx) => this.renderPanelButton(panelButton, idx))}
                </Panel>
              )
            }
          )}
          {this.props.loggedUser.canWrite ? //TODO remove SideNodeDetails? turn out to be not useful
            (<Panel collapsible defaultExpanded header="Details">
              <SideNodeDetails/>
            </Panel>) : null
          }
        </Scrollbars>

    )
  }

  getConfig = () => {
    const saveDisabled = this.props.nothingToSave && this.props.processIsLatestVersion;
    const deployPossible = this.props.processIsLatestVersion && ProcessUtils.hasNoErrors(this.props.processToDisplay) && this.props.nothingToSave;
    const migratePossible = this.props.processIsLatestVersion && ProcessUtils.hasNoErrors(this.props.processToDisplay) && this.props.nothingToSave;

    return [
      (this.props.isSubprocess ? null : {
        panelName: "Deployment",
        buttons:[
          {name: "deploy", visible: this.props.loggedUser.canDeploy, disabled: !deployPossible, onClick: this.deploy, icon: InlinedSvgs.buttonDeploy},
          {name: "stop", visible: this.props.loggedUser.canDeploy, disabled: !this.isRunning(), onClick: this.stop, icon: InlinedSvgs.buttonStop},
          {name: "metrics", onClick: this.showMetrics, icon: InlinedSvgs.buttonMetrics}
        ]
      }),
      {
      panelName: "Process",
      buttons: [
        {name: "save" + (!saveDisabled ? "*" : ""), visible: this.props.loggedUser.canWrite, disabled: saveDisabled, onClick: this.save, icon: InlinedSvgs.buttonSave},
        {name: "migrate", visible: this.props.loggedUser.canDeploy && !_.isEmpty(this.props.featuresSettings.migration), disabled: !migratePossible, onClick: this.migrate, icon: InlinedSvgs.buttonMigrate},
        {name: "compare", onClick: this.compareVersions, icon: InlinedSvgs.compareButton, disabled: this.hasOneVersion()},
        {name: "import", visible: this.props.loggedUser.canWrite, disabled: false, onClick: this.importProcess, icon: InlinedSvgs.buttonImport, dropzone: true},
        {name: "export", visible: this.props.loggedUser.canWrite, onClick: this.exportProcess, icon: InlinedSvgs.buttonExport},
        {name: "exportPDF", disabled: !this.props.nothingToSave, onClick: this.exportProcessToPdf, icon: InlinedSvgs.buttonExport},
        {name: "zoomIn", onClick: this.props.zoomIn, icon: InlinedSvgs.buttonZoomIn},
        {name: "zoomOut", onClick: this.props.zoomOut, icon: InlinedSvgs.buttonZoomOut}

      ]
    },
      {
        panelName: "Edit",
        buttons: [
          {name: "undo", visible: this.props.loggedUser.canWrite, onClick: this.undo, icon: InlinedSvgs.buttonUndo},
          {name: "redo", visible: this.props.loggedUser.canWrite, onClick: this.redo, icon: InlinedSvgs.buttonRedo},
          {name: "align", onClick: this.props.graphLayoutFunction, icon: InlinedSvgs.buttonAlign, visible: this.props.loggedUser.canWrite},
          {name: "properties", onClick: this.showProperties, icon: InlinedSvgs.buttonSettings, visible: !this.props.isSubprocess && this.props.loggedUser.canWrite},
          {name: "duplicate", onClick: this.duplicateNode, icon: InlinedSvgs.duplicateButton,
            //cloning groups can be tricky...
            disabled: this.noChosenNode(this.props.nodeToDisplay) || NodeUtils.nodeIsGroup(this.props.nodeToDisplay),
            visible: this.props.loggedUser.canWrite},
          {name: "delete", onClick: this.deleteNode, icon: InlinedSvgs.buttonDelete, visible: this.props.loggedUser.canWrite, disabled: this.noChosenNode(this.props.nodeToDisplay) }

        ]
      },
      //TODO: testing subprocesses should work, but currently we don't know how to pass parameters in sane way...
      (this.props.isSubprocess ? null : {
        panelName: "Test",
        buttons: [
          {name: "from file", onClick: this.testProcess, icon: InlinedSvgs.buttonFromFile, dropzone: true,
            disabled: !this.props.testCapabilities.canBeTested, visible: this.props.loggedUser.canWrite},
          {name: "hide", onClick: this.hideRunProcessDetails, icon: InlinedSvgs.buttonHide, disabled: !this.props.showRunProcessDetails, visible: this.props.loggedUser.canWrite},
          {name: "generate", onClick: this.generateData, icon: InlinedSvgs.buttonGenerate,
            disabled: !this.props.processIsLatestVersion || !this.props.testCapabilities.canGenerateTestData, visible: this.props.loggedUser.canWrite},
          {name: "counts", onClick: this.fetchProcessCounts, icon: InlinedSvgs.buttonCounts,
            visible: this.props.featuresSettings.counts && !this.props.isSubprocess},
        ]
      }),
      {
        panelName: "Group",
        buttons: [
          {name: "start", onClick: this.props.actions.startGrouping, icon: InlinedSvgs.buttonGroup, disabled: this.props.groupingState != null, visible: this.props.loggedUser.canWrite},
          {name: "finish", onClick: this.props.actions.finishGrouping, icon: InlinedSvgs.buttonGroup, disabled: (this.props.groupingState || []).length <= 1, visible: this.props.loggedUser.canWrite},
          {name: "cancel", onClick: this.props.actions.cancelGrouping, icon: InlinedSvgs.buttonUngroup, disabled: !this.props.groupingState, visible: this.props.loggedUser.canWrite },
          {name: "ungroup", onClick: this.ungroup, icon: InlinedSvgs.buttonUngroup, disabled: !NodeUtils.nodeIsGroup(this.props.nodeToDisplay), visible: this.props.loggedUser.canWrite }
        ]
      }
    ];
  }

  renderPanelButton = (panelButton, idx) => {
    const buttonClass = "espButton right-panel"
    return panelButton.dropzone ?
        <Dropzone key={idx} disableClick={panelButton.disabled === true} onDrop={panelButton.onClick}
                  className={"dropZone " + buttonClass + (panelButton.disabled === true ? " disabled" : "")}>

{panelButton.name}

          </Dropzone>
        :
        <button key={idx} type="button" className={buttonClass} disabled={panelButton.disabled === true}
                onClick={panelButton.onClick}>

 {panelButton.name}
        </button>
  }

  noChosenNode = (node) => {
    return NodeUtils.nodeIsProperties(node) || _.isEmpty(node)
  }

  isRunning = () => this.props.fetchedProcessDetails && (this.props.fetchedProcessDetails.currentlyDeployedAt || []).length > 0

  showProperties = () => {
    this.props.actions.displayModalNodeDetails(this.props.processToDisplay.properties)
  }

  save = () => {
    this.props.actions.toggleModalDialog(Dialogs.types.saveProcess)
  }

  migrate = () => {
    this.props.actions.toggleConfirmDialog(true, DialogMessages.migrate(this.processId(), this.props.featuresSettings.migration.targetEnvironmentId), () => {
      HttpService.migrateProcess(this.processId())
    })
  }

  deploy = () => {
    this.props.actions.toggleConfirmDialog(true, DialogMessages.deploy(this.processId()), () => {
      return HttpService.deploy(this.processId()).then((resp) => {
        this.fetchProcessDetails()
      })
    })
  }

  stop = () => {
    this.props.actions.toggleConfirmDialog(true, DialogMessages.stop(this.processId()), () => {
      return HttpService.stop(this.processId()).then((resp) => {
        this.fetchProcessDetails()
      })
    })
  }

  clearHistory = () => {
    return this.props.undoRedoActions.clear()
  }

  fetchProcessDetails = () => {
    this.props.actions.displayCurrentProcessVersion(this.processId())
  }

  processId = () => {
    return (this.props.processToDisplay || {}).id
  }

  versionId = () => this.props.fetchedProcessDetails.processVersionId

  showMetrics = () => {
    browserHistory.push('/metrics/' + this.processId())
  }

  exportProcess = () => {
    HttpService.exportProcess(this.processId(), this.versionId())
  }

  exportProcessToPdf = () => {
    const data = this.props.exportGraph()
    HttpService.exportProcessToPdf(this.processId(), this.versionId(), data, this.props.businessView)
  }

  generateData = () => {
    this.props.actions.toggleModalDialog(Dialogs.types.generateTestData)
  }

  compareVersions = () => {
    this.props.actions.toggleModalDialog(Dialogs.types.compareVersions)
  }

  fetchProcessCounts = () => {
    this.props.actions.toggleModalDialog(Dialogs.types.calculateCounts)
  }

  importProcess = (files) => {
    files.forEach((file)=>
      this.props.actions.importProcess(this.processId(), file)
    );
  }

  testProcess = (files) => {
    files.forEach((file)=>
      this.props.actions.testProcessFromFile(this.processId(), file, this.props.processToDisplay)
    );
  }

  hideRunProcessDetails = () => {
    this.props.actions.hideRunProcessDetails()
  }

  undo = () => {
    //this `if` should be closer to reducer?
    if (this.props.keyActionsAvailable) {
      this.props.undoRedoActions.undo()
    }
  }

  redo = () => {
    if (this.props.keyActionsAvailable) {
      this.props.undoRedoActions.redo()
    }
  }

  duplicateNode = () => {
    const duplicatedNodePosition = this.props.layout.find(node => node.id === this.props.nodeToDisplay.id) || {position: {x: 0, y: 0}}
    const visiblePosition = {x: duplicatedNodePosition.position.x -200, y: duplicatedNodePosition.position.y}
    this.props.actions.nodeAdded(this.props.nodeToDisplay, visiblePosition)
  }

  deleteNode = () => {
    this.props.actions.deleteNode(this.props.nodeToDisplay.id)
  }

  ungroup = () => {
    this.props.actions.ungroup(this.props.nodeToDisplay)
  }

  hasOneVersion = () => _.get(this.props.fetchedProcessDetails, 'history', []).length <= 1

}

function mapState(state) {
  const fetchedProcessDetails = state.graphReducer.fetchedProcessDetails
  return {
    isOpened: state.ui.rightPanelIsOpened,
    fetchedProcessDetails: fetchedProcessDetails,
    processToDisplay: state.graphReducer.processToDisplay,
    //TODO: now only needed for duplicate, maybe we can do it somehow differently?
    layout: state.graphReducer.layout || [],

    testCapabilities: state.graphReducer.testCapabilities || {},

    loggedUser: state.settings.loggedUser,
    nothingToSave: ProcessUtils.nothingToSave(state),
    showRunProcessDetails: !_.isEmpty(state.graphReducer.testResults) || !_.isEmpty(state.graphReducer.processCounts),
    keyActionsAvailable: state.ui.allModalsClosed,
    processIsLatestVersion: _.get(fetchedProcessDetails, 'isLatestVersion', false),
    nodeToDisplay: state.graphReducer.nodeToDisplay,
    groupingState: state.graphReducer.groupingState,
    featuresSettings: state.settings.featuresSettings,
    isSubprocess: _.get(fetchedProcessDetails, "properties.isSubprocess", false),
    businessView: state.graphReducer.businessView
  };
}

export default connect(mapState, ActionsUtils.mapDispatchWithEspActions)(UserRightPanel);
<|endoftext|>


<commit_before>/*
 * Copyright 2018 Expedia Group
 *
 *         Licensed under the Apache License, Version 2.0 (the "License");
 *         you may not use this file except in compliance with the License.
 *         You may obtain a copy of the License at
 *
 *             http://www.apache.org/licenses/LICENSE-2.0
 *
 *         Unless required by applicable law or agreed to in writing, software
 *         distributed under the License is distributed on an "AS IS" BASIS,
 *         WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *         See the License for the specific language governing permissions and
 *         limitations under the License.
 */

const Q = require('q');
const config = require('../../../config/config');
const _ = require('lodash');
const fetcher = require('../../operations/restFetcher');
const MetricpointNameEncoder = require('../../utils/encoders/MetricpointNameEncoder');

const trendsFetcher = fetcher('trends');

const connector = {};
const metricTankUrl = config.connectors.trends && config.connectors.trends.metricTankUrl;
const metricpointNameEncoder = new MetricpointNameEncoder(config.encoder);

function createServicesOperationsTarget(services, operations, timeWindow, metricStats, metricNames) {
    return encodeURIComponent(`seriesByTag('name=${metricNames}','serviceName=${services}','operationName=${operations}','interval=${timeWindow}','stat=${metricStats}')`);
}

function createOperationTarget(service, operationName, timeWindow, metricStats, metricNames) {
    return encodeURIComponent(`seriesByTag('name=${metricNames}','serviceName=${service}','operationName=${operationName}','interval=${timeWindow}','stat=${metricStats}')`);
}

function getServiceTargetStat(service, timeWindow, metricStats, metricNames) {
    return encodeURIComponent(`seriesByTag('name=${metricNames}','serviceName=${service}','interval=${timeWindow}','stat=${metricStats}')`);
}

function convertGranularityToTimeWindow(timespan) {
    switch (timespan) {
        case '60000':
            return 'OneMinute';
        case '300000':
            return 'FiveMinute';
        case '900000':
            return 'FifteenMinute';
        case '3600000':
            return 'OneHour';
        default:
            return 'OneMinute';
    }
}

function convertEpochTimeInSecondsToMillis(timestamp) {
    return timestamp * 1000;
}

function toMilliseconds(micro) {
    return Math.ceil(micro / 1000);
}

function groupResponseByServiceOperation(data) {
    return data.map((op) => {
        const tags = op.tags;

        const serviceName = tags.serviceName ? metricpointNameEncoder.decodeMetricpointName(tags.serviceName) : null;
        const operationName = tags.operationName ? metricpointNameEncoder.decodeMetricpointName(tags.operationName) : null;
        const trendStat = `${tags.stat}.${tags.name}`;

        return {
            serviceName,
            operationName,
            [trendStat]: op.datapoints.map(datapoint => ({
                value: datapoint[0] ? datapoint[0] : 0,
                timestamp: convertEpochTimeInSecondsToMillis(datapoint[1])
            }))
        };
    });
}

function fetchTrendValues(target, from, until) {
    return trendsFetcher
        .fetch(`${metricTankUrl}/render?target=${target}&from=${from}&to=${until}`, { 'x-org-id': 1})
        .then(data => groupResponseByServiceOperation(data));
}

function extractTrendPointsForSingleServiceOperation(operationTrends, trendStat) {
    const dataPoints = operationTrends.find(trend => trendStat in trend);
    const trendStatDataPoints = dataPoints ? dataPoints[trendStat] : [];

    if (trendStatDataPoints.length && trendStatDataPoints[trendStatDataPoints.length - 1].value === null) {
        trendStatDataPoints.pop();
    }

    return trendStatDataPoints;
}

function dataPointsSum(dataPoints) {
    return dataPoints.reduce(((accumulator, dataPoint) => (accumulator + dataPoint.value)), 0);
}

function toSuccessPercent(successPoints, failurePoints) {
    const successCount = successPoints.reduce(((accumulator, dataPoint) => (accumulator + dataPoint.value)), 0);
    const failureCount = failurePoints.reduce(((accumulator, dataPoint) => (accumulator + dataPoint.value)), 0);

    return 100 - ((failureCount / (successCount + failureCount)) * 100);
}

function toSuccessPercentPoints(successCount, failureCount) {
    const successTimestamps = successCount.map(point => point.timestamp);
    const failureTimestamps = failureCount.map(point => point.timestamp);
    const timestamps = _.uniq([...successTimestamps, ...failureTimestamps]);

    return _.compact(timestamps.map((timestamp) => {
        const successItem = _.find(successCount, x => (x.timestamp === timestamp));
        const successVal = (successItem && successItem.value) ? successItem.value : 0;

        const failureItem = _.find(failureCount, x => (x.timestamp === timestamp));
        const failureVal = (failureItem && failureItem.value) ? failureItem.value : 0;

        if (successVal + failureVal) {
            return {
                value: (100 - ((failureVal / (successVal + failureVal)) * 100)),
                timestamp
            };
        }
        return null;
    }));
}

function toCountPoints(successCount, failureCount) {
    const successTimestamps = successCount.map(point => point.timestamp);
    const failureTimestamps = failureCount.map(point => point.timestamp);
    const timestamps = _.uniq([...successTimestamps, ...failureTimestamps]);

    return _.compact(timestamps.map((timestamp) => {
        const successItem = _.find(successCount, x => (x.timestamp === timestamp));
        const successVal = (successItem && successItem.value) ? successItem.value : 0;

        const failureItem = _.find(failureCount, x => (x.timestamp === timestamp));
        const failureVal = (failureItem && failureItem.value) ? failureItem.value : 0;

        return {
            value: successVal + failureVal,
            timestamp
        };
    }));
}

function extractServicePerfStats({successValues, failureValues, tp99Values}) {
    const trendResults = [];

    const groupedByServiceName = _.groupBy(successValues.concat(successValues, failureValues, tp99Values), val => val.serviceName);
    Object.keys(groupedByServiceName).forEach((service) => {
        const serviceTrends = groupedByServiceName[service];
        const successCount = dataPointsSum(extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.success-span'));
        const failureCount = dataPointsSum(extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.failure-span'));
        const successPercent = ((successCount / (successCount + failureCount)) * 100);

        const opKV = {
            serviceName: service,
            successPercent,
            failureCount,
            successCount,
            totalCount: successCount + failureCount
        };

        trendResults.push(opKV);
    });
    return trendResults;
}

function extractServiceSummary(serviceTrends) {
    const successCount = extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.success-span');
    const failureCount = extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.failure-span');
    const countPoints = toCountPoints(successCount, failureCount);
    const tp99DurationPoints = extractTrendPointsForSingleServiceOperation(serviceTrends, '*_99.duration');
    const latestTp99DurationDatapoint = _.findLast(tp99DurationPoints, point => point.value);

    return [{
        type: 'Incoming Requests',
        totalCount: dataPointsSum(countPoints),
        countPoints,
        avgSuccessPercent: toSuccessPercent(successCount, failureCount),
        successPercentPoints: toSuccessPercentPoints(successCount, failureCount),
        latestTp99Duration: latestTp99DurationDatapoint && latestTp99DurationDatapoint.value,
        tp99DurationPoints
    }];
}

function extractOperationSummary(values) {
    const groupedByOperationName = _.groupBy(values, val => val.operationName);
    return Object.keys(groupedByOperationName).map((operationName) => {
        const operationTrends = groupedByOperationName[operationName];

        const successPoints = extractTrendPointsForSingleServiceOperation(operationTrends, 'count.success-span');
        const failurePoints = extractTrendPointsForSingleServiceOperation(operationTrends, 'count.failure-span');
        const countPoints = toCountPoints(successPoints, failurePoints);
        const tp99DurationPoints = extractTrendPointsForSingleServiceOperation(operationTrends, '*_99.duration');
        const latestTp99DurationDatapoint = _.findLast(tp99DurationPoints, point => point.value);

        return {
            operationName,
            totalCount: dataPointsSum(countPoints),
            countPoints,
            avgSuccessPercent: toSuccessPercent(successPoints, failurePoints),
            successPercentPoints: toSuccessPercentPoints(successPoints, failurePoints),
            latestTp99Duration: latestTp99DurationDatapoint && latestTp99DurationDatapoint.value,
            tp99DurationPoints,
            failurePoints
        };
    });
}

function getServicePerfStatsResults(timeWindow, from, until) {
    const SuccessTarget = getServiceTargetStat('~.*', timeWindow, 'count', 'success-span');
    const FailureTarget = getServiceTargetStat('~.*', timeWindow, 'count', 'failure-span');
    const tp99Target = getServiceTargetStat('~.*', timeWindow, '*_99', 'duration');

    return Q.all([
        fetchTrendValues(SuccessTarget, from, until),
        fetchTrendValues(FailureTarget, from, until),
        fetchTrendValues(tp99Target, from, until)
    ]).then(values => extractServicePerfStats({
            successValues: values[0],
            failureValues: values[1],
            tp99Values: values[2]
        })
    );
}

function getServiceSummaryResults(serviceName, timeWindow, from, until) {
    const target = getServiceTargetStat(serviceName, timeWindow, '~(count)|(\\*_99)', '~(success-span)|(failure-span)|(duration)');

    return fetchTrendValues(target, from, until)
        .then(values => extractServiceSummary(values));
}

function getServiceTrendResults(serviceName, timeWindow, from, until) {
    const target = getServiceTargetStat(serviceName, timeWindow, '~(count)|(mean)|(\\*_95)|(\\*_99)', '~(success-span)|(failure-span)|(duration)');

    return fetchTrendValues(target, from, until)
        .then((trends) => {
            const successCount = extractTrendPointsForSingleServiceOperation(trends, 'count.success-span');
            const failureCount = extractTrendPointsForSingleServiceOperation(trends, 'count.failure-span');
            return {
                count: toCountPoints(successCount, failureCount),
                successCount,
                failureCount,
                meanDuration: extractTrendPointsForSingleServiceOperation(trends, 'mean.duration'),
                tp95Duration: extractTrendPointsForSingleServiceOperation(trends, '*_95.duration'),
                tp99Duration: extractTrendPointsForSingleServiceOperation(trends, '*_99.duration')
            };
        });
}

function getOperationSummaryResults(service, timeWindow, from, until) {
    const target = createOperationTarget(service, '~.*', timeWindow, '~(count)|(\\*_99)', '~(received-span)|(success-span)|(failure-span)|(duration)');

    return fetchTrendValues(target, from, until)
        .then(values => extractOperationSummary(values));
}

function getOperationTrendResults(serviceName, operationName, timeWindow, from, until) {
    const target = createOperationTarget(serviceName, operationName, timeWindow, '~(count)|(mean)|(\\*_95)|(\\*_99)', '~(received-span)|(success-span)|(failure-span)|(duration)');

    return fetchTrendValues(target, from, until)
        .then((trends) => {
            const successCount = extractTrendPointsForSingleServiceOperation(trends, 'count.success-span');
            const failureCount = extractTrendPointsForSingleServiceOperation(trends, 'count.failure-span');
            return {
                count: toCountPoints(successCount, failureCount),
                successCount,
                failureCount,
                meanDuration: extractTrendPointsForSingleServiceOperation(trends, 'mean.duration'),
                tp95Duration: extractTrendPointsForSingleServiceOperation(trends, '*_95.duration'),
                tp99Duration: extractTrendPointsForSingleServiceOperation(trends, '*_99.duration')
            };
        });
}

function getEdgeLatencyTrendResults(edges, from, until) {
    const serviceNameRegex = edges.map(e => metricpointNameEncoder.encodeMetricpointName(e.serviceName)).join(',');
    const operationNameRegex = edges.map(e => metricpointNameEncoder.encodeMetricpointName(e.operationName)).join(',');

    const target = createServicesOperationsTarget(`~${serviceNameRegex}`, `~${operationNameRegex}`, 'OneHour', '~(mean)|(\\*_99)', 'latency');

    return fetchTrendValues(target, from, until)
        .then(trends => trends);
}

// api
connector.getServicePerfStats = (granularity, from, until) =>
    getServicePerfStatsResults(convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getServiceStats = (serviceName, granularity, from, until) =>
    getServiceSummaryResults(metricpointNameEncoder.encodeMetricpointName(serviceName), convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getServiceTrends = (serviceName, granularity, from, until) =>
    getServiceTrendResults(metricpointNameEncoder.encodeMetricpointName(serviceName), convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getOperationStats = (serviceName, granularity, from, until) =>
    getOperationSummaryResults(metricpointNameEncoder.encodeMetricpointName(serviceName), convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getOperationTrends = (serviceName, operationName, granularity, from, until) =>
    getOperationTrendResults(metricpointNameEncoder.encodeMetricpointName(serviceName), metricpointNameEncoder.encodeMetricpointName(operationName), convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getEdgeLatency = edges => getEdgeLatencyTrendResults(edges, Math.ceil(Date.now() / 1000) - (60 * 60), Math.ceil(Date.now() / 1000));

module.exports = connector;
<commit_msg>Bugfix for latency trends (#463)<commit_after>/*
 * Copyright 2018 Expedia Group
 *
 *         Licensed under the Apache License, Version 2.0 (the "License");
 *         you may not use this file except in compliance with the License.
 *         You may obtain a copy of the License at
 *
 *             http://www.apache.org/licenses/LICENSE-2.0
 *
 *         Unless required by applicable law or agreed to in writing, software
 *         distributed under the License is distributed on an "AS IS" BASIS,
 *         WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *         See the License for the specific language governing permissions and
 *         limitations under the License.
 */

const Q = require('q');
const config = require('../../../config/config');
const _ = require('lodash');
const fetcher = require('../../operations/restFetcher');
const MetricpointNameEncoder = require('../../utils/encoders/MetricpointNameEncoder');

const trendsFetcher = fetcher('trends');

const connector = {};
const metricTankUrl = config.connectors.trends && config.connectors.trends.metricTankUrl;
const metricpointNameEncoder = new MetricpointNameEncoder(config.encoder);

function createServicesOperationsTarget(services, operations, timeWindow, metricStats, metricNames) {
    return encodeURIComponent(`seriesByTag('name=${metricNames}','serviceName=${services}','operationName=${operations}','interval=${timeWindow}','stat=${metricStats}')`);
}

function createOperationTarget(service, operationName, timeWindow, metricStats, metricNames) {
    return encodeURIComponent(`seriesByTag('name=${metricNames}','serviceName=${service}','operationName=${operationName}','interval=${timeWindow}','stat=${metricStats}')`);
}

function getServiceTargetStat(service, timeWindow, metricStats, metricNames) {
    return encodeURIComponent(`seriesByTag('name=${metricNames}','serviceName=${service}','interval=${timeWindow}','stat=${metricStats}')`);
}

function convertGranularityToTimeWindow(timespan) {
    switch (timespan) {
        case '60000':
            return 'OneMinute';
        case '300000':
            return 'FiveMinute';
        case '900000':
            return 'FifteenMinute';
        case '3600000':
            return 'OneHour';
        default:
            return 'OneMinute';
    }
}

function convertEpochTimeInSecondsToMillis(timestamp) {
    return timestamp * 1000;
}

function toMilliseconds(micro) {
    return Math.ceil(micro / 1000);
}

function groupResponseByServiceOperation(data) {
    return data.map((op) => {
        const tags = op.tags;

        const serviceName = tags.serviceName ? metricpointNameEncoder.decodeMetricpointName(tags.serviceName) : null;
        const operationName = tags.operationName ? metricpointNameEncoder.decodeMetricpointName(tags.operationName) : null;
        const trendStat = `${tags.stat}.${tags.name}`;

        return {
            serviceName,
            operationName,
            [trendStat]: op.datapoints.map(datapoint => ({
                value: datapoint[0] ? datapoint[0] : 0,
                timestamp: convertEpochTimeInSecondsToMillis(datapoint[1])
            }))
        };
    });
}

function fetchTrendValues(target, from, until) {
    return trendsFetcher
        .fetch(`${metricTankUrl}/render?target=${target}&from=${from}&to=${until}`, { 'x-org-id': 1})
        .then(data => groupResponseByServiceOperation(data));
}

function extractTrendPointsForSingleServiceOperation(operationTrends, trendStat) {
    const dataPoints = operationTrends.find(trend => trendStat in trend);
    const trendStatDataPoints = dataPoints ? dataPoints[trendStat] : [];

    if (trendStatDataPoints.length && trendStatDataPoints[trendStatDataPoints.length - 1].value === null) {
        trendStatDataPoints.pop();
    }

    return trendStatDataPoints;
}

function dataPointsSum(dataPoints) {
    return dataPoints.reduce(((accumulator, dataPoint) => (accumulator + dataPoint.value)), 0);
}

function toSuccessPercent(successPoints, failurePoints) {
    const successCount = successPoints.reduce(((accumulator, dataPoint) => (accumulator + dataPoint.value)), 0);
    const failureCount = failurePoints.reduce(((accumulator, dataPoint) => (accumulator + dataPoint.value)), 0);

    return 100 - ((failureCount / (successCount + failureCount)) * 100);
}

function toSuccessPercentPoints(successCount, failureCount) {
    const successTimestamps = successCount.map(point => point.timestamp);
    const failureTimestamps = failureCount.map(point => point.timestamp);
    const timestamps = _.uniq([...successTimestamps, ...failureTimestamps]);

    return _.compact(timestamps.map((timestamp) => {
        const successItem = _.find(successCount, x => (x.timestamp === timestamp));
        const successVal = (successItem && successItem.value) ? successItem.value : 0;

        const failureItem = _.find(failureCount, x => (x.timestamp === timestamp));
        const failureVal = (failureItem && failureItem.value) ? failureItem.value : 0;

        if (successVal + failureVal) {
            return {
                value: (100 - ((failureVal / (successVal + failureVal)) * 100)),
                timestamp
            };
        }
        return null;
    }));
}

function toCountPoints(successCount, failureCount) {
    const successTimestamps = successCount.map(point => point.timestamp);
    const failureTimestamps = failureCount.map(point => point.timestamp);
    const timestamps = _.uniq([...successTimestamps, ...failureTimestamps]);

    return _.compact(timestamps.map((timestamp) => {
        const successItem = _.find(successCount, x => (x.timestamp === timestamp));
        const successVal = (successItem && successItem.value) ? successItem.value : 0;

        const failureItem = _.find(failureCount, x => (x.timestamp === timestamp));
        const failureVal = (failureItem && failureItem.value) ? failureItem.value : 0;

        return {
            value: successVal + failureVal,
            timestamp
        };
    }));
}

function extractServicePerfStats({successValues, failureValues, tp99Values}) {
    const trendResults = [];

    const groupedByServiceName = _.groupBy(successValues.concat(successValues, failureValues, tp99Values), val => val.serviceName);
    Object.keys(groupedByServiceName).forEach((service) => {
        const serviceTrends = groupedByServiceName[service];
        const successCount = dataPointsSum(extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.success-span'));
        const failureCount = dataPointsSum(extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.failure-span'));
        const successPercent = ((successCount / (successCount + failureCount)) * 100);

        const opKV = {
            serviceName: service,
            successPercent,
            failureCount,
            successCount,
            totalCount: successCount + failureCount
        };

        trendResults.push(opKV);
    });
    return trendResults;
}

function extractServiceSummary(serviceTrends) {
    const successCount = extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.success-span');
    const failureCount = extractTrendPointsForSingleServiceOperation(serviceTrends, 'count.failure-span');
    const countPoints = toCountPoints(successCount, failureCount);
    const tp99DurationPoints = extractTrendPointsForSingleServiceOperation(serviceTrends, '*_99.duration');
    const latestTp99DurationDatapoint = _.findLast(tp99DurationPoints, point => point.value);

    return [{
        type: 'Incoming Requests',
        totalCount: dataPointsSum(countPoints),
        countPoints,
        avgSuccessPercent: toSuccessPercent(successCount, failureCount),
        successPercentPoints: toSuccessPercentPoints(successCount, failureCount),
        latestTp99Duration: latestTp99DurationDatapoint && latestTp99DurationDatapoint.value,
        tp99DurationPoints
    }];
}

function extractOperationSummary(values) {
    const groupedByOperationName = _.groupBy(values, val => val.operationName);
    return Object.keys(groupedByOperationName).map((operationName) => {
        const operationTrends = groupedByOperationName[operationName];

        const successPoints = extractTrendPointsForSingleServiceOperation(operationTrends, 'count.success-span');
        const failurePoints = extractTrendPointsForSingleServiceOperation(operationTrends, 'count.failure-span');
        const countPoints = toCountPoints(successPoints, failurePoints);
        const tp99DurationPoints = extractTrendPointsForSingleServiceOperation(operationTrends, '*_99.duration');
        const latestTp99DurationDatapoint = _.findLast(tp99DurationPoints, point => point.value);

        return {
            operationName,
            totalCount: dataPointsSum(countPoints),
            countPoints,
            avgSuccessPercent: toSuccessPercent(successPoints, failurePoints),
            successPercentPoints: toSuccessPercentPoints(successPoints, failurePoints),
            latestTp99Duration: latestTp99DurationDatapoint && latestTp99DurationDatapoint.value,
            tp99DurationPoints,
            failurePoints
        };
    });
}

function getServicePerfStatsResults(timeWindow, from, until) {
    const SuccessTarget = getServiceTargetStat('~.*', timeWindow, 'count', 'success-span');
    const FailureTarget = getServiceTargetStat('~.*', timeWindow, 'count', 'failure-span');
    const tp99Target = getServiceTargetStat('~.*', timeWindow, '*_99', 'duration');

    return Q.all([
        fetchTrendValues(SuccessTarget, from, until),
        fetchTrendValues(FailureTarget, from, until),
        fetchTrendValues(tp99Target, from, until)
    ]).then(values => extractServicePerfStats({
            successValues: values[0],
            failureValues: values[1],
            tp99Values: values[2]
        })
    );
}

function getServiceSummaryResults(serviceName, timeWindow, from, until) {
    const target = getServiceTargetStat(serviceName, timeWindow, '~(count)|(\\*_99)', '~(success-span)|(failure-span)|(duration)');

    return fetchTrendValues(target, from, until)
        .then(values => extractServiceSummary(values));
}

function getServiceTrendResults(serviceName, timeWindow, from, until) {
    const target = getServiceTargetStat(serviceName, timeWindow, '~(count)|(mean)|(\\*_95)|(\\*_99)', '~(success-span)|(failure-span)|(duration)');

    return fetchTrendValues(target, from, until)
        .then((trends) => {
            const successCount = extractTrendPointsForSingleServiceOperation(trends, 'count.success-span');
            const failureCount = extractTrendPointsForSingleServiceOperation(trends, 'count.failure-span');
            return {
                count: toCountPoints(successCount, failureCount),
                successCount,
                failureCount,
                meanDuration: extractTrendPointsForSingleServiceOperation(trends, 'mean.duration'),
                tp95Duration: extractTrendPointsForSingleServiceOperation(trends, '*_95.duration'),
                tp99Duration: extractTrendPointsForSingleServiceOperation(trends, '*_99.duration')
            };
        });
}

function getOperationSummaryResults(service, timeWindow, from, until) {
    const target = createOperationTarget(service, '~.*', timeWindow, '~(count)|(\\*_99)', '~(received-span)|(success-span)|(failure-span)|(duration)');

    return fetchTrendValues(target, from, until)
        .then(values => extractOperationSummary(values));
}

function getOperationTrendResults(serviceName, operationName, timeWindow, from, until) {
    const target = createOperationTarget(serviceName, operationName, timeWindow, '~(count)|(mean)|(\\*_95)|(\\*_99)', '~(received-span)|(success-span)|(failure-span)|(duration)');

    return fetchTrendValues(target, from, until)
        .then((trends) => {
            const successCount = extractTrendPointsForSingleServiceOperation(trends, 'count.success-span');
            const failureCount = extractTrendPointsForSingleServiceOperation(trends, 'count.failure-span');
            return {
                count: toCountPoints(successCount, failureCount),
                successCount,
                failureCount,
                meanDuration: extractTrendPointsForSingleServiceOperation(trends, 'mean.duration'),
                tp95Duration: extractTrendPointsForSingleServiceOperation(trends, '*_95.duration'),
                tp99Duration: extractTrendPointsForSingleServiceOperation(trends, '*_99.duration')
            };
        });
}

function getEdgeLatencyTrendResults(edges, from, until) {
    const serviceNameRegex = edges.map(e => metricpointNameEncoder.encodeMetricpointName(e.serviceName)).join('|');
    const operationNameRegex = edges.map(e => metricpointNameEncoder.encodeMetricpointName(e.operationName)).join('|');

    const target = createServicesOperationsTarget(`~${serviceNameRegex}`, `~${operationNameRegex}`, 'OneHour', '~(mean)|(\\*_99)', 'latency');

    return fetchTrendValues(target, from, until)
        .then(trends => trends);
}

// api
connector.getServicePerfStats = (granularity, from, until) =>
    getServicePerfStatsResults(convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getServiceStats = (serviceName, granularity, from, until) =>
    getServiceSummaryResults(metricpointNameEncoder.encodeMetricpointName(serviceName), convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getServiceTrends = (serviceName, granularity, from, until) =>
    getServiceTrendResults(metricpointNameEncoder.encodeMetricpointName(serviceName), convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getOperationStats = (serviceName, granularity, from, until) =>
    getOperationSummaryResults(metricpointNameEncoder.encodeMetricpointName(serviceName), convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getOperationTrends = (serviceName, operationName, granularity, from, until) =>
    getOperationTrendResults(metricpointNameEncoder.encodeMetricpointName(serviceName), metricpointNameEncoder.encodeMetricpointName(operationName), convertGranularityToTimeWindow(granularity), toMilliseconds(from), toMilliseconds(until));

connector.getEdgeLatency = edges => getEdgeLatencyTrendResults(edges, Math.ceil(Date.now() / 1000) - (60 * 60), Math.ceil(Date.now() / 1000));

module.exports = connector;
<|endoftext|>


<commit_before>import { src, dest } from 'gulp';
import concat from 'gulp-concat';
import sourcemaps from 'gulp-sourcemaps';
import when from 'gulp-if';
import postcss from 'gulp-postcss';
import autoprefix from 'autoprefixer';
import bem from 'postcss-bem';
import colorFunction from 'postcss-color-function';
import cssnano from 'cssnano';
import imports from 'postcss-import';
import nested from 'postcss-nested';
import reduceCalc from 'postcss-calc';
import variables from 'postcss-simple-vars';

export default function compileCssTask({ minify = false, 'source-maps': useSourceMaps = true }) {
  const processors = [
    imports(),
    variables(),
    bem(),
    nested(),
    colorFunction(),
    reduceCalc(),
    autoprefix({
      remove: false,
      browsers: [ 'last 2 versions' ]
    })
  ];
  return src('src/**/*.css')
    .pipe(when(useSourceMaps, sourcemaps.init()))
      .pipe(postcss(processors))
      .pipe(concat('style.css'))
      .pipe(when(minify, postcss([ cssnano() ])))
    .pipe(when(useSourceMaps, sourcemaps.write()))
    .pipe(dest('lib/'));
}
<commit_msg>build: concat css before processing, such speed 🏎<commit_after>import { src, dest } from 'gulp';
import concat from 'gulp-concat';
import sourcemaps from 'gulp-sourcemaps';
import when from 'gulp-if';
import postcss from 'gulp-postcss';
import autoprefix from 'autoprefixer';
import bem from 'postcss-bem';
import colorFunction from 'postcss-color-function';
import cssnano from 'cssnano';
import imports from 'postcss-import';
import nested from 'postcss-nested';
import reduceCalc from 'postcss-calc';
import variables from 'postcss-simple-vars';

export default function compileCssTask({ minify = false, 'source-maps': useSourceMaps = true }) {
  const processors = [
    imports(),
    variables(),
    bem(),
    nested(),
    colorFunction(),
    reduceCalc(),
    autoprefix({
      remove: false,
      browsers: [ 'last 2 versions' ]
    })
  ];

  if (minify) {
    processors.push(cssnano());
  }

  return src('src/**/*.css')
    .pipe(when(useSourceMaps, sourcemaps.init()))
      .pipe(concat('style.css'))
      .pipe(postcss(processors))
    .pipe(when(useSourceMaps, sourcemaps.write()))
    .pipe(dest('lib/'));
}
<|endoftext|>