<commit_before>'use strict';

const dir = require('node-dir');
const yaml = require('js-yaml');
const globalConfig = require('config');
const path = require('path');
const utils = require('./utils');

/**
 * Gets all Workflows
 *
 * @param {string} wfpath path to workflow directory
 *
 * @returns {Promise} - An {Array} containing {Object} for each workflow, including `name` {String}, `id` name {String}, and steps {Object} loaded from YAML config file
 */
const load = (wfpath) => {
  const workflows = [];
  const ids = [];
  let workflowsPath = path.join(process.cwd(), 'workflows');

  if (globalConfig.hasOwnProperty('workflows')) {
    if (globalConfig.workflows.hasOwnProperty('directory')) {
      workflowsPath = globalConfig.workflows.directory;
    }
  }

  if (wfpath) {
    workflowsPath = wfpath;
  }

  return new Promise((resolve, reject) => {
    dir.readFiles(workflowsPath, {
      match: /.yml$/,
      exclude: /^\./,
      recursive: false,
    }, (err, content, next) => {
      if (err) {
        reject(err);
      }

      const config = yaml.safeLoad(content);

      if (utils.check(config) !== true) {
        reject(new Error(utils.check(config)));
      }

      if (ids.indexOf(config.id) === -1) {
        ids.push(config.id);
        workflows.push(config);
      }
      else {
        reject(new Error(`Workflow ${config.id} is duplicated!`));
      }

      next();
    }, (err) => {
      if (err) {
        reject(err);
      }
      resolve(workflows);
    });
  });
};

module.exports = load;
<commit_msg>:unamused: remove recursive blocking<commit_after>'use strict';

const dir = require('node-dir');
const yaml = require('js-yaml');
const globalConfig = require('config');
const path = require('path');
const utils = require('./utils');

/**
 * Gets all Workflows
 *
 * @param {string} wfpath path to workflow directory
 *
 * @returns {Promise} - An {Array} containing {Object} for each workflow, including `name` {String}, `id` name {String}, and steps {Object} loaded from YAML config file
 */
const load = (wfpath) => {
  const workflows = [];
  const ids = [];
  let workflowsPath = path.join(process.cwd(), 'workflows');

  if (globalConfig.hasOwnProperty('workflows')) {
    if (globalConfig.workflows.hasOwnProperty('directory')) {
      workflowsPath = globalConfig.workflows.directory;
    }
  }

  if (wfpath) {
    workflowsPath = wfpath;
  }

  return new Promise((resolve, reject) => {
    dir.readFiles(workflowsPath, {
      match: /.yml$/,
      exclude: /^\./,
    }, (err, content, next) => {
      if (err) {
        reject(err);
      }

      const config = yaml.safeLoad(content);

      if (utils.check(config) !== true) {
        reject(new Error(utils.check(config)));
      }

      if (ids.indexOf(config.id) === -1) {
        ids.push(config.id);
        workflows.push(config);
      }
      else {
        reject(new Error(`Workflow ${config.id} is duplicated!`));
      }

      next();
    }, (err) => {
      if (err) {
        reject(err);
      }
      resolve(workflows);
    });
  });
};

module.exports = load;
<|endoftext|>


<commit_before>'use babel';

import { parseString as parseStringOld } from 'xml2js';

export const parseString = (xml) => {
  return new Promise((resolve, reject) => {
    parseStringOld(xml, (err, result) => {
      console.log(xml);
      console.log(err);
      resolve(result);
    });
  });
};
<commit_msg>:fire: Removed console logging<commit_after>'use babel';

import { parseString as parseStringOld } from 'xml2js';

export const parseString = (xml) => {
  return new Promise((resolve) => {
    parseStringOld(xml, (err, result) => {
      resolve(result);
    });
  });
};
<|endoftext|>


<commit_before>const Router = require('koa-router');
const koaCompose = require('koa-compose');
const annoSetController = require('./controllers/annoSetController');
const frameController = require('./controllers/frameController');
const lexUnitController = require('./controllers/lexUnitController');
const patternController = require('./controllers/patternController');
const valenceUnitController = require('./controllers/valenceUnitController');
const processor = require('./middlewares/processor');
const validator = require('./middlewares/validator');

const pkgVersion = process.env.npm_package_version;
const apiVersion = `/v${pkgVersion.split('.')[0]}`;

const valencer = new Router({
  prefix: apiVersion,
});

const router = new Router();

const vpQueryValidator = koaCompose.compose([
  validator.validateQueryNotEmpty,
  validator.validateQueryVPnotEmpty,
  validator.validateQueryVPcontainsNoInvalidCharacters,
  validator.validateQueryVPcontainsNoInvalidSequence,
  validator.validateQueryVPvalenceUnitLength,
  validator.validateQueryPopulateParameter,
  validator.validateQueryStrictVUmatchingParameter,
  validator.validateQueryWithExtraCoreFEsParameter,
]);
const paramsQueryValidator = koaCompose.compose([
  validator.validateParamsNotEmpty,
  validator.validateParamsIDnotEmpty,
  validator.validateParamsIDisNumberOrObjectID,
  validator.validateQueryPopulateParameter,
  validator.validateQueryStrictVUmatchingParameter,
  validator.validateQueryWithExtraCoreFEsParameter,
]);

/**
 * @apiDefine NotFoundIDError
 * @apiVersion 3.0.0
 * @apiError (Error 404) NotFoundError The id was not found
 */

/**
 * @apiDefine NotFoundVPError
 * @apiVersion 3.0.0
 * @apiError (Error 404) NotFoundError A valence unit of the vp was not found
 */

/**
 * @apiDefine InvalidQuery
 * @apiVersion 3.0.0
 * @apiError (Error 400) InvalidQuery The specified query is null, empty or
 * combines :id and vp in request
 */

/**
 * @apiDefine InvalidQueryParamsID
 * @apiVersion 3.0.0
 * @apiError (Error 400) InvalidQueryParams The populate parameter is neither
 * true nor false or the specified :id is
 * neither a Number nor a valid ObjectID
 */

/**
 * @apiDefine InvalidQueryParamsVP
 * @apiVersion 3.0.0
 * @apiError (Error 400) InvalidQueryParams The populate parameter is neither
 * true nor false or the vp parameter contains either
 * an invalid character, an invalid sequence of characters or more than 3
 * tokens separated by a dot in at least one of its valence units
 */

/**
 * @apiDefine idParam
 * @apiVersion 3.0.0
 * @apiParam {Number}     id          The id
 * @apiParam {Boolean} [populate=false]    Specify whether documents
 * should be populated
 */

/**
 * @apiDefine vpParam
 * @apiVersion 3.0.0
 * @apiParam {String}     vp          The Valence Pattern: a
 * combination of triplets FE.PT.GF
 * @apiParam {Boolean}    [populate=false]     Specify whether documents
 * should be populated.
 * @apiParam {Boolean}    [strictVUMatching=false]     Specify whether
 * the number of valence units should match exactly. Ex: Querying for
 * Donor.NP.Ext with strictVUMatching=true will return all patterns with only
 * one valenceUnit
 * @apiParam {Boolean}    [withExtraCoreFEs=true] Specify whether, in cases of
 * non-strict valence unit matching, extra Frame Elements can be core FEs.
 */

/**
 * @apiDefine AnnotationSetSuccess
 * @apiVersion 3.0.0
 * @apiSuccess  {Number}   _id        The AnnotationSet id
 * @apiSuccess  {Number}   lexUnit    The LexUnit id
 * @apiSuccess  {Number}   sentence   The Sentence id
 * @apiSuccess  {Object[]} labels     The list of labels ObjectID
 */

/**
 * @apiDefine AnnotationSetSuccessPopulated
 * @apiVersion 3.0.0
 * @apiSuccess   {Number}    _id                          The AnnotationSet id
 * @apiSuccess   {Object}    lexUnit                      The LexUnit
 * @apiSuccess   {Number}    lexUnit._id                  The LexUnit id
 * @apiSuccess   {String}    lexUnit.name                 The LexUnit name
 * @apiSuccess   {String}    lexUnit.pos                  The LexUnit part of speech
 * @apiSuccess   {String}    lexUnit.definition           The LexUnit definition
 * @apiSuccess   {Number}    lexUnit.lemmaID              The LexUnit lemmaID
 * @apiSuccess   {Object}    lexUnit.frame                The LexUnit Frame
 * @apiSuccess   {Number}    lexUnit.frame._id            The Frame id
 * @apiSuccess   {String}    lexUnit.frame.name           The Frame name
 * @apiSuccess   {String}    lexUnit.frame.definition     The Frame definition
 * @apiSuccess   {String}    lexUnit.frame.cDate          The Frame creation date
 * @apiSuccess   {String}    lexUnit.frame.cBy            The Frame annotator
 * @apiSuccess   {Number[]}  lexUnit.frame.semTypes       The Frame SemTypes ids
 * @apiSuccess   {Object[]}  lexUnit.frame.lexUnits       The Frame LexUnits
 * @apiSuccess   {Number}    lexUnit.frame.lexUnits._id   The LexUnits ids
 * @apiSuccess   {String}    lexUnit.frame.lexUnits.name  The LexUnits names
 * @apiSuccess   {Number[]}  lexUnit.frame.feCoreSets     The Frame
 * minimal set of core FrameElements ids
 * @apiSuccess   {Object[]}  lexUnit.frame.frameElements  The Frame FrameElements
 * @apiSuccess   {Number}    lexUnit.frame.frameElements._id    The FrameElements ids
 * @apiSuccess   {String}    lexUnit.frame.frameElements.name   The FrameElements names
 * @apiSuccess   {String}    lexUnit.frame.frameElements.definition The FrameElements definitions
 * @apiSuccess   {String}    lexUnit.frame.frameElements.coreType The FrameElements coreTypes
 * @apiSuccess   {String}    lexUnit.frame.frameElements.cDate The FrameElements creation dates
 * @apiSuccess   {String}    lexUnit.frame.frameElements.cBy The FrameElements annotators
 * @apiSuccess   {String}    lexUnit.frame.frameElements.abbrev The FrameElements abbreviations
 * @apiSuccess   {Number[]}    lexUnit.frame.frameElements.semTypes The FrameElements SemTypes ids
 * @apiSuccess   {Number[]}    lexUnit.frame.frameElements.excludes The excluded FrameElements ids
 * @apiSuccess   {Number[]}    lexUnit.frame.frameElements.requires The required FrameElements ids
 * @apiSuccess   {String}    lexUnit.status         The LexUnit status
 * @apiSuccess   {String}    lexUnit.cBy            The LexUnit annotator
 * @apiSuccess   {String}    lexUnit.cDate          The LexUnit creation date
 * @apiSuccess   {Number[]}  lexUnit.semTypes       The LexUnit SemTypes ids
 * @apiSuccess   {Number[]}  lexUnit.lexemes        The LexUnit Lexemes ids
 * @apiSuccess   {Object}    sentence               The Sentence
 * @apiSuccess   {Number}    sentence._id           The Sentence id
 * @apiSuccess   {String}    sentence.name          The Sentence name
 * @apiSuccess   {Number}    sentence.sentenceNumber          The Sentence number
 * @apiSuccess   {Number}    sentence.paragraphNumber          The Sentence paragraph number
 * @apiSuccess   {Number}    sentence.aPos          The Sentence a-positional value
 * @apiSuccess   {Object}    pattern                The Pattern
 * @apiSuccess   {Object}    pattern._id            The Pattern ObjectID
 * @apiSuccess   {Object[]}  pattern.valenceUnits   The Pattern ValenceUnits
 * @apiSuccess   {Object}  pattern.valenceUnits._id   The Pattern ValenceUnits ObjectIDs
 * @apiSuccess   {String}  pattern.valenceUnits.FE   The Pattern ValenceUnits frame elements names
 * @apiSuccess   {String}  pattern.valenceUnits.PT   The Pattern ValenceUnits phrase types
 * @apiSuccess   {String}  pattern.valenceUnits.GF   The Pattern ValenceUnits grammatical functions
 * @apiSuccess   {Object[]}  labels             The Labels
 * @apiSuccess   {Object}   labels._id          The Labels ObjectIDs
 * @apiSuccess   {String}   labels.name         The Labels names
 * @apiSuccess   {String}   labels.type         The Labels types
 * @apiSuccess   {Number}   labels.startPos     The Labels start positions
 * @apiSuccess   {Number}   labels.endPos       The Labels end positions
 */

/**
 * @apiDefine FrameSuccess
 * @apiVersion 3.0.0
 * @apiSuccess   {Number}    _id            The Frame id
 * @apiSuccess   {String}    name           The Frame name
 * @apiSuccess   {String}    definition     The Frame definition
 * @apiSuccess   {String}    cDate          The Frame creation date
 * @apiSuccess   {String}    cBy            The Frame annotator
 * @apiSuccess   {Number[]}  semTypes       The Frame SemTypes ids
 * @apiSuccess   {Number[]}  lexUnits       The Frame LexUnits ids
 * @apiSuccess   {Number[]}  feCoreSets     The Frame
 * minimal set of core FrameElements ids
 * @apiSuccess   {Number[]}  frameElements  The Frame FrameElements ids
 */

/**
 * @apiDefine FrameSuccessPopulated
 * @apiVersion 3.0.0
 * @apiSuccess   {Number}    _id            The Frame id
 * @apiSuccess   {String}    name           The Frame name
 * @apiSuccess   {String}    definition     The Frame definition
 * @apiSuccess   {String}    cDate          The Frame creation date
 * @apiSuccess   {String}    cBy            The Frame annotator
 * @apiSuccess   {Number[]}  semTypes       The Frame SemTypes ids
 * @apiSuccess   {Object[]}  lexUnits       The Frame LexUnits
 * @apiSuccess   {Number}    lexUnits._id   The LexUnits ids
 * @apiSuccess   {String}    lexUnits.name  The LexUnits names
 * @apiSuccess   {Number[]}  feCoreSets     The Frame
 * minimal set of core FrameElements ids
 * @apiSuccess   {Object[]}  frameElements  The Frame FrameElements
 * @apiSuccess   {Number}    frameElements._id    The FrameElements ids
 * @apiSuccess   {String}    frameElements.name   The FrameElements names
 * @apiSuccess   {String}    frameElements.definition The FrameElements definitions
 * @apiSuccess   {String}    frameElements.coreType The FrameElements coreTypes
 * @apiSuccess   {String}    frameElements.cDate The FrameElements creation dates
 * @apiSuccess   {String}    frameElements.cBy The FrameElements annotators
 * @apiSuccess   {String}    frameElements.abbrev The FrameElements abbreviations
 * @apiSuccess   {Number[]}    frameElements.semTypes The FrameElements SemTypes ids
 * @apiSuccess   {Number[]}    frameElements.excludes The excluded FrameElements ids
 * @apiSuccess   {Number[]}    frameElements.requires The required FrameElements ids
 */

/**
 * @apiDefine LexUnitSuccess
 * @apiVersion 3.0.0
 * @apiSuccess   {Number}    _id                  The LexUnit id
 * @apiSuccess   {String}    name                 The LexUnit name
 * @apiSuccess   {String}    pos                  The LexUnit part of speech
 * @apiSuccess   {String}    definition           The LexUnit definition
 * @apiSuccess   {Number}    lemmaID              The LexUnit lemmaID
 * @apiSuccess   {Number}    frame                The LexUnit Frame id
 * @apiSuccess   {String}    status         The LexUnit status
 * @apiSuccess   {String}    cBy            The LexUnit annotator
 * @apiSuccess   {String}    cDate          The LexUnit creation date
 * @apiSuccess   {Number[]}  semTypes       The LexUnit SemTypes ids
 * @apiSuccess   {Number[]}  lexemes        The LexUnit Lexemes ids
 */

/**
 * @apiDefine LexUnitSuccessPopulated
 * @apiVersion 3.0.0
 * @apiSuccess   {Number}    _id                  The LexUnit id
 * @apiSuccess   {String}    name                 The LexUnit name
 * @apiSuccess   {String}    pos                  The LexUnit part of speech
 * @apiSuccess   {String}    definition           The LexUnit definition
 * @apiSuccess   {Number}    lemmaID              The LexUnit lemmaID
 * @apiSuccess   {Object}    frame                The LexUnit Frame
 * @apiSuccess   {Number}    frame._id            The Frame id
 * @apiSuccess   {String}    frame.name           The Frame name
 * @apiSuccess   {String}    frame.definition     The Frame definition
 * @apiSuccess   {String}    frame.cDate          The Frame creation date
 * @apiSuccess   {String}    frame.cBy            The Frame annotator
 * @apiSuccess   {Number[]}  frame.semTypes       The Frame SemTypes ids
 * @apiSuccess   {Object[]}  frame.lexUnits       The Frame LexUnits
 * @apiSuccess   {Number}    frame.lexUnits._id   The LexUnits ids
 * @apiSuccess   {String}    frame.lexUnits.name  The LexUnits names
 * @apiSuccess   {Number[]}  frame.feCoreSets     The Frame
 * minimal set of core FrameElements ids
 * @apiSuccess   {Object[]}  frame.frameElements  The Frame FrameElements
 * @apiSuccess   {Number}    frame.frameElements._id    The FrameElements ids
 * @apiSuccess   {String}    frame.frameElements.name   The FrameElements names
 * @apiSuccess   {String}    frame.frameElements.definition The FrameElements definitions
 * @apiSuccess   {String}    frame.frameElements.coreType The FrameElements coreTypes
 * @apiSuccess   {String}    frame.frameElements.cDate The FrameElements creation dates
 * @apiSuccess   {String}    frame.frameElements.cBy The FrameElements annotators
 * @apiSuccess   {String}    frame.frameElements.abbrev The FrameElements abbreviations
 * @apiSuccess   {Number[]}    frame.frameElements.semTypes The FrameElements SemTypes ids
 * @apiSuccess   {Number[]}    frame.frameElements.excludes The excluded FrameElements ids
 * @apiSuccess   {Number[]}    frame.frameElements.requires The required FrameElements ids
 * @apiSuccess   {String}    status         The LexUnit status
 * @apiSuccess   {String}    cBy            The LexUnit annotator
 * @apiSuccess   {String}    cDate          The LexUnit creation date
 * @apiSuccess   {Number[]}  semTypes       The LexUnit SemTypes ids
 * @apiSuccess   {Number[]}  lexemes        The LexUnit Lexemes ids
 */

/**
 * @apiDefine PatternSuccess
 * @apiVersion 3.0.0
 * @apiSuccess   {Object}    _id            The Pattern ObjectID
 * @apiSuccess   {Object[]}  valenceUnits   The Pattern ValenceUnits ObjectIDs
 */

/**
 * @apiDefine PatternSuccessPopulated
 * @apiVersion 3.0.0
 * @apiSuccess   {Object}    _id            The Pattern ObjectID
 * @apiSuccess   {Object[]}  valenceUnits   The Pattern ValenceUnits
 * @apiSuccess   {Object}  valenceUnits._id   The ValenceUnits ObjectIDs
 * @apiSuccess   {String}  valenceUnits.FE   The ValenceUnits frame elements names
 * @apiSuccess   {String}  valenceUnits.PT   The ValenceUnits phrase types
 * @apiSuccess   {String}  valenceUnits.GF   The ValenceUnits grammatical functions
 */

/**
 * @apiDefine ValenceUnitSuccess
 * @apiVersion 3.0.0
 * @apiSuccess   {Object}  _id  The ObjectID
 * @apiSuccess   {String}  FE   The frame element name
 * @apiSuccess   {String}  PT   The phrase type
 * @apiSuccess   {String}  GF   The grammatical function
 */

/**
 * @api {get} /annoSet/:id GetAnnoSet
 * @apiVersion 3.0.0
 * @apiName GetAnnoSet
 * @apiGroup AnnotationSet
 * @apiDescription Get AnnotationSet with id. Returns at most one
 * document and throws an error if not found. Sample success output
 * is given with populate=true
 * @apiUse idParam
 * @apiExample Example usage:
 * curl -i http://localhost/annoSet/123?populate=true
 * @apiUse AnnotationSetSuccessPopulated
 * @apiUse NotFoundIDError
 * @apiUse InvalidQuery
 * @apiUse InvalidQueryParamsID
 */
router.get('/annoSet/:id',
  validator.validate,
  processor.processid,
  annoSetController.getByID);

/**
 * @api {get} /annoSets GetAnnoSets
 * @apiVersion 3.0.0
 * @apiName GetAnnoSets
 * @apiGroup AnnotationSet
 * @apiDescription Get all AnnotationSets with pattern matching input
 * vp. Returns an empty array if no match is found. Sample success
 * output is given with (default) populate=false
 * @apiUse vpParam
 * @apiExample Example usage:
 * curl -i http://localhost/annoSets?vp=Donor.NP.Ext+Theme.NP.Obj
 * @apiUse AnnotationSetSuccess
 * @apiUse NotFoundVPError
 * @apiUse InvalidQuery
 * @apiUse InvalidQueryParamsVP
 */
router.get('/annoSets',
  validator.validate,
  formatter.formatValencePatternToArrayOfArrayOfTokens,
  formatter.replaceFrameElementNamesByFrameElementIds,
  validator.checkParameters,
  processor.retrieveValenceUnits,
  processor.retrievePatterns,
  annotationSets.getByValencePattern);

/**
 * @api {get} /frame/:id GetFrame
 * @apiVersion 3.0.0
 * @apiName GetFrame
 * @apiGroup Frame
 * @apiDescription Get Frame with id. Returns at most one
 * document and throws an error if not found. Sample success output
 * is given with populate=true
 * @apiUse idParam
 * @apiExample Example usage:
 * curl -i http://localhost/frame/123?populate=true
 * @apiUse FrameSuccessPopulated
 * @apiUse NotFoundIDError
 * @apiUse InvalidQuery
 * @apiUse InvalidQueryParamsID
 */
router.get('/frame/:id',
  validator.validate,
  processor.processid,
  frameController.getByID);

/**
 * @api {get} /frames GetFrames
 * @apiVersion 3.0.0
 * @apiName GetFrames
 * @apiGroup Frame
 * @apiDescription Get all Frames with pattern matching input vp. Returns an
 * empty array if no match is found. Sample success
 * output is given with (default) populate=false
 * @apiUse vpParam
 * @apiExample Example usage:
 * curl -i http://localhost/frames?vp=Donor.NP.Ext+Theme.NP.Obj
 * @apiUse FrameSuccess
 * @apiUse NotFoundVPError
 * @apiUse InvalidQuery
 * @apiUse InvalidQueryParamsVP
 */
router.get('/frames',
  validator.validate,
  processor.processvp,
  frameController.getByVP);

/**
 * @api {get} /lexUnit/:id GetLexUnit
 * @apiVersion 3.0.0
 * @apiName GetLexUnit
 * @apiGroup LexUnit
 * @apiDescription Get LexUnit with id. Returns at most one
 * document and throws an error if not found. Sample success output
 * is given with populate=true
 * @apiUse idParam
 * @apiExample Example usage:
 * curl -i http://localhost:3030/lexUnit/123
 * @apiUse LexUnitSuccessPopulated
 * @apiUse NotFoundIDError
 * @apiUse InvalidQuery
 * @apiUse InvalidQueryParamsID
 */
router.get('/lexUnit/:id',
  validator.validate,
  processor.processid,
  lexUnitController.getByID);

/**
 * @api {get} /lexUnits GetLexUnits
 * @apiVersion 3.0.0
 * @apiName GetLexUnits
 * @apiGroup LexUnit
 * @apiDescription Get all LexUnits with pattern matching input vp. Returns an
 * empty array if no match is found. Sample success
 * output is given with (default) populate=false
 * @apiUse vpParam
 * @apiExample Example usage:
 * curl -i http://localhost:3030/lexUnits?vp=Donor.NP.Ext+Theme.NP.Obj
 * @apiUse LexUnitSuccess
 * @apiUse NotFoundVPError
 * @apiUse InvalidQuery
 * @apiUse InvalidQueryParamsVP
 */
router.get('/lexUnits',
  validator.validate,
  processor.processvp,
  lexUnitController.getByVP);

/**
 * @api {get} /pattern/:id GetPattern
 * @apiVersion 3.0.0
 * @apiName GetPattern
 * @apiGroup Pattern
 * @apiDescription Get Pattern with id. Returns at most one
 * document and throws an error if not found. Sample success output
 * is given with populate=true
 * @apiUse idParam
 * @apiExample Example usage:
 * curl -i http://localhost:3030/pattern/123
 * @apiUse PatternSuccessPopulated
 * @apiUse NotFoundIDError
 * @apiUse InvalidQuery
 * @apiUse InvalidQueryParamsID
 */
router.get('/pattern/:id',
  validator.validate,
  processor.processid,
  patternController.getByID);

/**
 * @api {get} /patterns GetPatterns
 * @apiVersion 3.0.0
 * @apiName GetPatterns
 * @apiGroup Pattern
 * @apiDescription Get all Patterns with pattern matching input vp. Returns an
 * empty array if no match is found. Sample success
 * output is given with (default) populate=false
 * @apiUse vpParam
 * @apiExample Example usage:
 * curl -i http://localhost:3030/patterns?vp=Donor.NP.Ext+Theme.NP.Obj
 * @apiUse PatternSuccess
 * @apiUse NotFoundVPError
 * @apiUse InvalidQuery
 * @apiUse InvalidQueryParamsVP
 */
router.get('/patterns',
  validator.validate,
  processor.processvp,
  patternController.getByVP);

/**
 * @api {get} /valenceUnit/:id GetValenceUnit
 * @apiVersion 3.0.0
 * @apiName GetValenceUnit
 * @apiGroup ValenceUnit
 * @apiDescription Get ValenceUnit with id. Returns at most one
 * document and throws an error if not found.
 * @apiUse idParam
 * @apiExample Example usage:
 * curl -i http://localhost:3030/valenceUnit/123
 * @apiUse ValenceUnitSuccess
 * @apiUse NotFoundIDError
 * @apiUse InvalidQuery
 * @apiUse InvalidQueryParamsID
 */
router.get('/valenceUnit/:id',
  validator.validate,
  processor.processid,
  valenceUnitController.getByID);

/**
 * @api {get} /valenceUnits GetValenceUnits
 * @apiVersion 3.0.0
 * @apiName GetValenceUnits
 * @apiGroup ValenceUnit
 * @apiDescription Get all ValenceUnits with pattern matching input vp. Returns
 * an empty array if no match is found.
 * @apiUse vpParam
 * @apiExample Example usage:
 * curl -i http://localhost:3030/valenceUnits?vp=Donor.NP.Ext+Theme.NP.Obj
 * @apiUse ValenceUnitSuccess
 * @apiUse NotFoundVPError
 * @apiUse InvalidQuery
 * @apiUse InvalidQueryParamsVP
 */
router.get('/valenceUnits',
//TODO: add specific validator
  validator.validate,
  processor.processvp,
  valenceUnitController.getByVP);

valencer.use('/:lang_iso_code/:dataset_version', router.routes());

module.exports = valencer;
<commit_msg>Removed unused routes<commit_after><|endoftext|>


<commit_before>/**
 * @class Oskari.mapframework.bundle.myplacesimport.Flyout
 */
Oskari.clazz.define('Oskari.mapframework.bundle.myplacesimport.Flyout',

    /**
     * @method create called automatically on construction
     * @static
     * @param {Oskari.mapframework.bundle.myplacesimport.MyPlacesImportBundleInstance} instance
     *      reference to component that created the flyout
     */

    function (instance, locale) {
        this.instance = instance;
        this.locale = locale;
        this.container = undefined;
        this.template = undefined;
    }, {
        __name: 'Oskari.mapframework.bundle.myplacesimport.Flyout',
        __templates: {
            iframe: '',
            base: '
' +
                    '

' +
                    '

' +
                '
',
            file: '
' +
                    '<form id="myplacesimport-form" method="post" enctype="multipart/form-data" target="myplacesimport-target">' +
                        '<input type="file" name="file-import"></input>' +
                        '
Name<input type="text" name="layer-name" />
' +
                        '
Description<input type="text" name="layer-desc" />
' +
                        '
Data source<input type="text" name="layer-source" />
' +
                        '
' +
                            'Layer style' +
                            '

' +
                            '<input type="hidden" name="layer-style" />' +
                        '
' +
                        '<input type="submit" value="Submit" class="primary" />' +
                    '</form>' +
                '
'
        },
        /**
         * @method getName
         * @return {String} the name for the component
         */
        getName: function () {
            return this.__name;
        },
        /**
         * @method setTemplate
         * @param {jQuery} template
         */
        setTemplate: function(template) {
            if (this.template && template) {
                this.template.replaceWith(template);
            }
            this.template = template;
        },
        /**
         * @method getTemplate
         * @return {jQuery}
         */
        getTemplate: function() {
            return this.template;
        },
        /**
         * Interface method implementation, assigns the HTML templates
         * that will be used to create the UI
         *
         * @method startPlugin
         */
        startPlugin: function () {
            var container = this.getEl(),
                iframe = jQuery(this.__templates.iframe).clone();

            container.addClass('myplacesimport');
            container.append(iframe);

            this.setTemplate(this.createUi());
            container.append(this.getTemplate());
        },
        /**
         * Interface method implementation
         *
         * @method stopPlugin
         */
        stopPlugin: function () {
            if (this.template) this.template.empty();
            this.setTemplate(undefined);
            if (this.container) this.container.empty();
            this.container = undefined;
        },
        /**
         * Creates a new UI from scratch
         *
         * @method refresh
         */
        refresh: function() {
            this.container
                .find('iframe')
                .replaceWith(jQuery(this.__templates.iframe).clone());

            this.setTemplate(this.createUi());
        },
        /**
         * Creates the UI for a fresh start.
         *
         * @method createUi
         * @return {jQuery} returns the template to place on the DOM
         */
        createUi: function () {
            var locale = this.getLocalization(),
                template = jQuery(this.__templates.base).clone();

            template.find('div.info')
                .html(locale.description);
            template.find('div.state')
                .html(this.__createFileImportTemplate(locale));

            return template;
        },
        /**
         * Creates the template for file upload form
         *
         * @method __createFileImportTemplate
         * @private
         * @param  {Object} locale
         * @return {jQuery}
         */
        __createFileImportTemplate: function(locale) {
            var me = this,
                file = jQuery(this.__templates.file).clone(),
                action = this.instance.getService().getFileImportUrl(),
                styleForm = Oskari.clazz.create(
                    'Oskari.userinterface.component.VisualizationForm'
                );

            file.find('div.name label').html(locale.layer.name);
            file.find('div.desc label').html(locale.layer.desc);
            file.find('div.source label').html(locale.layer.source);
            file.find('div.style label').html(locale.layer.style);
            file.find('div.style-form').html(styleForm.getForm());
            file.find('form')
                .attr('action', action)
                .find('input[type=submit]')
                    .val(locale.file.submit)
                    .on('click', function(e) {
                        var form = jQuery(this).parent(),
                            styleJson = JSON.stringify(me.__getStyleValues(styleForm));
                        // Set the value of the hidden style field
                        form.find('input[name=layer-style]').val(styleJson);
                        // Prevent from sending if there were missing fields
                        if (me.__validateForm(form, locale)) {
                            e.preventDefault();
                        }
                    });

            this.container.find('iframe').on('load', function() {
                me.__finish(jQuery(this), locale);
            });

            return file;
        },
        /**
         * Returns the visualization form's values.
         *
         * @method __getStyleValues
         * @private
         * @param  {Oskari.userinterface.component.VisualizationForm} styleForm
         * @return {Object}
         */
        __getStyleValues: function(styleForm) {
            var formValues = styleForm.getValues(),
                values = {};

            if (formValues) {
                values.dot = {
                    size: formValues.dot.size,
                    color: '#' + formValues.dot.color,
                    shape: formValues.dot.shape
                };
                values.line = {
                    size: formValues.line.width,
                    color: '#' + formValues.line.color,
                    cap: formValues.line.cap,
                    corner: formValues.line.corner,
                    style: formValues.line.style
                };
                values.area = {
                    size: formValues.area.lineWidth,
                    lineColor: '#' + formValues.area.lineColor,
                    fillColor: '#' + formValues.area.fillColor,
                    lineStyle: formValues.area.lineStyle,
                    fillStyle: formValues.area.fillStyle,
                    lineCorner: formValues.area.lineCorner
                };
            }

            return values;
        },
        /**
         * Validates the form inputs (currently that the name and file are present).
         * Returns true if there were any errors (missing values).
         *
         * @method __validateForm
         * @private
         * @param  {jQuery} form
         * @param  {Object} locale
         * @return {Boolean}
         */
        __validateForm: function(form, locale) {
            var fileInput = form.find('input[type=file]'),
                nameInput = form.find('input[name=layer-name]'),
                errors = false,
                errorTitle, errorMsg;

            if (!fileInput.val() || !nameInput.val()) {
                errors = true;
                errorTitle = locale.validations.error.title;
                errorMsg = locale.validations.error.message;
                this.__showMessage(errorTitle, errorMsg);
            }

            return errors;
        },
        /**
         * Sends the layer data to the backend and shows a message.
         * Also refreshes the UI
         *
         * @method __finish
         * @private
         * @param {jQuery} iframe
         * @param {Object} locale
         */
        __finish: function(iframe, locale) {
            var title = locale.finish.success.title,
                msg = locale.finish.success.message,
                json = undefined, success = true;

            try {
                json = JSON.parse(iframe.contents().find('pre').html());
                if (json === null || json === undefined) success = false;
            } catch(error) {
                this.instance
                    .getSandbox()
                    .printWarn('Error whilst parsing user layer json', error);
                success = false;
            }

            if (success === true) {
                this.instance.addUserLayer(json);
            } else {
                title = null, msg = locale.finish.failure.message;
            }

            this.__showMessage(title, msg);
            this.refresh();
        },
        /**
         * Displays a message on the screen
         *
         * @method __showMessage
         * @private
         * @param  {String} title
         * @param  {String} message
         */
        __showMessage: function (title, message) {
            var me = this,
                loc = this._locale,
                dialog = Oskari.clazz.create('Oskari.userinterface.component.Popup');

            dialog.show(title, message);
            dialog.fadeout(5000);
        },
    }, {
        "extend": ["Oskari.userinterface.extension.DefaultFlyout"]
    });<commit_msg>IE should no longer unnecessarily bind the 'load' event to the iframe<commit_after>/**
 * @class Oskari.mapframework.bundle.myplacesimport.Flyout
 */
Oskari.clazz.define('Oskari.mapframework.bundle.myplacesimport.Flyout',

    /**
     * @method create called automatically on construction
     * @static
     * @param {Oskari.mapframework.bundle.myplacesimport.MyPlacesImportBundleInstance} instance
     *      reference to component that created the flyout
     */

    function (instance, locale) {
        this.instance = instance;
        this.locale = locale;
        this.container = undefined;
        this.template = undefined;
    }, {
        __name: 'Oskari.mapframework.bundle.myplacesimport.Flyout',
        __templates: {
            iframe: '',
            base: '
' +
                    '

' +
                    '

' +
                '
',
            file: '
' +
                    '<form id="myplacesimport-form" method="post" enctype="multipart/form-data" target="myplacesimport-target">' +
                        '<input type="file" name="file-import"></input>' +
                        '
Name<input type="text" name="layer-name" />
' +
                        '
Description<input type="text" name="layer-desc" />
' +
                        '
Data source<input type="text" name="layer-source" />
' +
                        '
' +
                            'Layer style' +
                            '

' +
                            '<input type="hidden" name="layer-style" />' +
                        '
' +
                        '<input type="submit" value="Submit" class="primary" />' +
                    '</form>' +
                '
'
        },
        /**
         * @method getName
         * @return {String} the name for the component
         */
        getName: function () {
            return this.__name;
        },
        /**
         * @method setTemplate
         * @param {jQuery} template
         */
        setTemplate: function(template) {
            if (this.template && template) {
                this.template.replaceWith(template);
            }
            this.template = template;
        },
        /**
         * @method getTemplate
         * @return {jQuery}
         */
        getTemplate: function() {
            return this.template;
        },
        /**
         * Interface method implementation, assigns the HTML templates
         * that will be used to create the UI
         *
         * @method startPlugin
         */
        startPlugin: function () {
            var container = this.getEl(),
                iframe = jQuery(this.__templates.iframe).clone();

            container.addClass('myplacesimport');
            container.append(iframe);

            this.setTemplate(this.createUi());
            container.append(this.getTemplate());
        },
        /**
         * Interface method implementation
         *
         * @method stopPlugin
         */
        stopPlugin: function () {
            if (this.template) this.template.empty();
            this.setTemplate(undefined);
            if (this.container) this.container.empty();
            this.container = undefined;
        },
        /**
         * Creates a new UI from scratch
         *
         * @method refresh
         */
        refresh: function() {
            this.container
                .find('iframe')
                .replaceWith(jQuery(this.__templates.iframe).clone());

            this.setTemplate(this.createUi());
        },
        /**
         * Creates the UI for a fresh start.
         *
         * @method createUi
         * @return {jQuery} returns the template to place on the DOM
         */
        createUi: function () {
            var locale = this.getLocalization(),
                template = jQuery(this.__templates.base).clone();

            template.find('div.info')
                .html(locale.description);
            template.find('div.state')
                .html(this.__createFileImportTemplate(locale));

            return template;
        },
        /**
         * Creates the template for file upload form
         *
         * @method __createFileImportTemplate
         * @private
         * @param  {Object} locale
         * @return {jQuery}
         */
        __createFileImportTemplate: function(locale) {
            var me = this,
                file = jQuery(this.__templates.file).clone(),
                action = this.instance.getService().getFileImportUrl(),
                styleForm = Oskari.clazz.create(
                    'Oskari.userinterface.component.VisualizationForm'
                );

            file.find('div.name label').html(locale.layer.name);
            file.find('div.desc label').html(locale.layer.desc);
            file.find('div.source label').html(locale.layer.source);
            file.find('div.style label').html(locale.layer.style);
            file.find('div.style-form').html(styleForm.getForm());
            file.find('form')
                .attr('action', action)
                .find('input[type=submit]')
                    .val(locale.file.submit)
                    .on('click', function(e) {
                        var form = jQuery(this).parent(),
                            styleJson = JSON.stringify(me.__getStyleValues(styleForm));
                        // Set the value of the hidden style field
                        form.find('input[name=layer-style]').val(styleJson);
                        // Prevent from sending if there were missing fields
                        if (me.__validateForm(form, locale)) {
                            e.preventDefault();
                        } else {
                            me.container.find('iframe').on('load', function() {
                                me.__finish(jQuery(this), locale);
                            });
                        }
                    });

            return file;
        },
        /**
         * Returns the visualization form's values.
         *
         * @method __getStyleValues
         * @private
         * @param  {Oskari.userinterface.component.VisualizationForm} styleForm
         * @return {Object}
         */
        __getStyleValues: function(styleForm) {
            var formValues = styleForm.getValues(),
                values = {};

            if (formValues) {
                values.dot = {
                    size: formValues.dot.size,
                    color: '#' + formValues.dot.color,
                    shape: formValues.dot.shape
                };
                values.line = {
                    size: formValues.line.width,
                    color: '#' + formValues.line.color,
                    cap: formValues.line.cap,
                    corner: formValues.line.corner,
                    style: formValues.line.style
                };
                values.area = {
                    size: formValues.area.lineWidth,
                    lineColor: '#' + formValues.area.lineColor,
                    fillColor: '#' + formValues.area.fillColor,
                    lineStyle: formValues.area.lineStyle,
                    fillStyle: formValues.area.fillStyle,
                    lineCorner: formValues.area.lineCorner
                };
            }

            return values;
        },
        /**
         * Validates the form inputs (currently that the name and file are present).
         * Returns true if there were any errors (missing values).
         *
         * @method __validateForm
         * @private
         * @param  {jQuery} form
         * @param  {Object} locale
         * @return {Boolean}
         */
        __validateForm: function(form, locale) {
            var fileInput = form.find('input[type=file]'),
                nameInput = form.find('input[name=layer-name]'),
                errors = false,
                errorTitle, errorMsg;

            if (!fileInput.val() || !nameInput.val()) {
                errors = true;
                errorTitle = locale.validations.error.title;
                errorMsg = locale.validations.error.message;
                this.__showMessage(errorTitle, errorMsg);
            }

            return errors;
        },
        /**
         * Sends the layer data to the backend and shows a message.
         * Also refreshes the UI
         *
         * @method __finish
         * @private
         * @param {jQuery} iframe
         * @param {Object} locale
         */
        __finish: function(iframe, locale) {
            var title = locale.finish.success.title,
                msg = locale.finish.success.message,
                json = undefined, success = true;

            try {
                json = JSON.parse(iframe.contents().find('pre').html());

                if (this.__jsonError(json)) {
                    success = false;
                }
            } catch(error) {
                this.instance
                    .getSandbox()
                    .printWarn('Error whilst parsing user layer json', error);
                success = false;
            }

            if (success === true) {
                this.instance.addUserLayer(json);
            } else {
                title = null, msg = locale.finish.failure.message;
            }

            this.__showMessage(title, msg);
            this.refresh();
        },
        /**
         * Displays a message on the screen
         *
         * @method __showMessage
         * @private
         * @param  {String} title
         * @param  {String} message
         */
        __showMessage: function (title, message) {
            var me = this,
                loc = this._locale,
                dialog = Oskari.clazz.create('Oskari.userinterface.component.Popup');

            dialog.show(title, message);
            dialog.fadeout(5000);
        },
        /**
         * Checks if json is null or undefined or if it has a key `error`.
         *
         * @method __jsonError
         * @private
         * @param  {JSON} json
         * @return {Boolean}
         */
        __jsonError: function(json) {
            var error = false;

            if (json === null || json === undefined) error = true;
            else if (json.error) error = true;

            return error;
        }
    }, {
        "extend": ["Oskari.userinterface.extension.DefaultFlyout"]
    });<|endoftext|>


<commit_before>var productsApp = angular.module('bulk_product_update', [])

productsApp.config(["$httpProvider", function(provider) {
  provider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content');
}]);

productsApp.directive('ngDecimal', function () {
	return {
		require: 'ngModel',
		link: function(scope, element, attrs, ngModel) {
			var numRegExp = /^\d+(\.\d+)?$/;

			element.bind('blur', function() {
				scope.$apply(ngModel.$setViewValue(ngModel.$modelValue));
				ngModel.$render();
			});

			ngModel.$setValidity('notADecimalError', function(){
				if (angular.isString(ngModel.$modelValue) && numRegExp.test(ngModel.$modelValue)){
					return true;
				}
				else{
					return false;
				}
			});

			ngModel.$parsers.push(function(viewValue){
				if (angular.isString(viewValue) && numRegExp.test(viewValue)){
					if (viewValue.indexOf(".") == -1){
						return viewValue+".0";
					}
				}
				return viewValue;
			});
		}
	}
});

productsApp.directive('ngTrackProduct', function(){
	return {
		require: 'ngModel',
		link: function(scope, element, attrs, ngModel) {
			var property = attrs.ngTrackProduct;
			var clean_value = angular.copy(scope.product[property]);
			element.bind('blur', function() {
				if (scope.product[property] == clean_value) removeCleanProperty(scope.dirtyProducts, scope.product.id, property);
				else addDirtyProperty(scope.dirtyProducts, scope.product.id, property, scope.product[property]);
				scope.$apply(scope.displayDirtyProducts());
			});
		}
	}
});

productsApp.directive('ngTrackVariant', function(){
	return {
		require: 'ngModel',
		link: function(scope, element, attrs, ngModel) {
			var property = attrs.ngTrackVariant;
			var clean_value = angular.copy(scope.variant[property]);
			element.bind('blur', function() {
				var dirtyVariants = {};
				if (scope.dirtyProducts.hasOwnProperty(scope.product.id) && scope.dirtyProducts[scope.product.id].hasOwnProperty("variants")) dirtyVariants = scope.dirtyProducts[scope.product.id].variants;
				if (scope.variant[property] == clean_value){
					removeCleanProperty(dirtyVariants, scope.variant.id, property);
					if (dirtyVariants == {}) removeCleanProperty(scope.dirtyProducts, scope.product.id, "variants");
				}
				else {
					addDirtyProperty(dirtyVariants, scope.variant.id, property, scope.variant[property]);
					addDirtyProperty(scope.dirtyProducts, scope.product.id, "variants", dirtyVariants);
				}
				scope.$apply(scope.displayDirtyProducts());
			});
		}
	}
});

productsApp.controller('AdminBulkProductsCtrl', function($scope, $timeout, $http, dataFetcher) {
	$scope.dirtyProducts = {};

	$scope.updateStatusMessage = {
		text: "",
		style: {}
	}

	$scope.refreshSuppliers = function(){
		dataFetcher('/enterprises/suppliers.json').then(function(data){
			$scope.suppliers = data;
		});
	};

	$scope.refreshProducts = function(){
		dataFetcher('/admin/products/bulk_index.json').then(function(data){
			$scope.products = toObjectWithIDKeys(data);
		});
	};

	$scope.updateOnHand = function(product){
		product.on_hand = onHand(product);
	}

	$scope.deleteProduct = function(product){
		$http({
			method: 'DELETE',
			url: '/admin/products/'+product.permalink_live+".js"
		})
		.success(function(data){
			delete $scope.products[product.id]
			if ($scope.dirtyProducts.hasOwnProperty(product.id)) delete $scope.dirtyProducts[product.id]
		})
	}

	$scope.deleteVariant = function(product,variant){
		$http({
			method: 'DELETE',
			url: '/admin/products/'+product.permalink_live+"/variants/"+variant.id+".js"
		})
		.success(function(data){
			delete $scope.products[product.id].variants[variant.id]
			if ($scope.dirtyProducts.hasOwnProperty(product.id) && $scope.dirtyProducts[product.id].hasOwnProperty("variants") && $scope.dirtyProducts[product.id].variants.hasOwnProperty(variant.id)) delete $scope.dirtyProducts[product.id].variants[variant.id]
		})
	}

	$scope.hasVariants = function(product){
		return !angular.equals(product.variants,{});
	}

	$scope.updateProducts = function(productsToSubmit){
		$scope.displayUpdating();
		$http({
			method: 'POST',
			url: '/admin/products/bulk_update',
			data: productsToSubmit
		})
		.success(function(data){
			data = toObjectWithIDKeys(data);
			if (angular.toJson($scope.products) == angular.toJson(data)){
				$scope.products = data;
				$scope.displaySuccess();
			}
			else{
				$scope.displayFailure("Product lists do not match.");
			}
		})
		.error(function(data,status){
			$scope.displayFailure("Server returned with error status: "+status);
		});
	}

	$scope.prepareProductsForSubmit = function(){
		var productsToSubmit = filterSubmitProducts($scope.dirtyProducts);
		$scope.updateProducts(productsToSubmit);
	}

	$scope.setMessage = function(model,text,style,timeout){
		model.text = text;
		model.style = style;
		if (timeout){
			$timeout(function() { $scope.setMessage(model,"",{},false); }, timeout, true);
		}
	}

	$scope.displayUpdating = function(){
		$scope.setMessage($scope.updateStatusMessage,"Updating...",{ color: "orange" },false);
	}

	$scope.displaySuccess = function(){
		$scope.setMessage($scope.updateStatusMessage,"Update complete",{ color: "green" },3000);
	}

	$scope.displayFailure = function(failMessage){
		$scope.setMessage($scope.updateStatusMessage,"Updating failed. "+failMessage,{ color: "red" },10000);
	}

	$scope.displayDirtyProducts = function(){
		var changedProductCount = Object.keys($scope.dirtyProducts).length;
		if (changedProductCount > 0) $scope.setMessage($scope.updateStatusMessage,"Changes to "+Object.keys($scope.dirtyProducts).length+" products remain unsaved.",{ color: "gray" },false);
		else $scope.setMessage($scope.updateStatusMessage,"",{},false);
	}
});

productsApp.factory('dataFetcher', function($http,$q){
	return function(dataLocation){
		var deferred = $q.defer();
		$http.get(dataLocation).success(function(data) {
			deferred.resolve(data);
		}).error(function(){
			deferred.reject();
		});
		return deferred.promise;
	};
});

function onHand(product){
	var onHand = 0;
	if(product.hasOwnProperty('variants') && product.variants instanceof Object){
		angular.forEach(product.variants, function(variant) {
			onHand = parseInt( onHand ) + parseInt( variant.on_hand > 0 ? variant.on_hand : 0 );
		});
	}
	else{
		onHand = 'error';
	}
	return onHand;
}

function filterSubmitProducts(productsToFilter){
	var filteredProducts= [];

	if (productsToFilter instanceof Object){
		var productKeys = Object.keys(productsToFilter);
		for (i in productKeys) {
			if (productsToFilter[productKeys[i]].hasOwnProperty("id")){
				var filteredProduct = {};
				var filteredVariants = [];

				if (productsToFilter[productKeys[i]].hasOwnProperty("variants")){
					var variantKeys = Object.keys(productsToFilter[productKeys[i]].variants);
					for (j in variantKeys){
						if (productsToFilter[productKeys[i]].variants[variantKeys[j]].deleted_at == null && productsToFilter[productKeys[i]].variants[variantKeys[j]].hasOwnProperty("id")){
							filteredVariants[j] = {};
							filteredVariants[j].id = productsToFilter[productKeys[i]].variants[variantKeys[j]].id;
							if (productsToFilter[productKeys[i]].variants[variantKeys[j]].hasOwnProperty("on_hand")) filteredVariants[j].on_hand = productsToFilter[productKeys[i]].variants[variantKeys[j]].on_hand;
							if (productsToFilter[productKeys[i]].variants[variantKeys[j]].hasOwnProperty("price")) filteredVariants[j].price = productsToFilter[productKeys[i]].variants[variantKeys[j]].price;
						}
					}
				}

				var hasUpdatableProperty = false;
				filteredProduct.id = productsToFilter[productKeys[i]].id;
				if (productsToFilter[productKeys[i]].hasOwnProperty("name")) { filteredProduct.name = productsToFilter[productKeys[i]].name; hasUpdatableProperty = true; }
				if (productsToFilter[productKeys[i]].hasOwnProperty("supplier_id")) { filteredProduct.supplier_id = productsToFilter[productKeys[i]].supplier_id; hasUpdatableProperty = true; }
				//if (productsToFilter[productKeys[i]].hasOwnProperty("master")) filteredProduct.master_attributes = productsToFilter[productKeys[i]].master
				if (productsToFilter[productKeys[i]].hasOwnProperty("price")) { filteredProduct.price = productsToFilter[productKeys[i]].price; hasUpdatableProperty = true; }
				if (productsToFilter[productKeys[i]].hasOwnProperty("on_hand") && filteredVariants.length == 0) { filteredProduct.on_hand = productsToFilter[productKeys[i]].on_hand; hasUpdatableProperty = true; } //only update if no variants present
				if (productsToFilter[productKeys[i]].hasOwnProperty("available_on")) { filteredProduct.available_on = productsToFilter[productKeys[i]].available_on; hasUpdatableProperty = true; }
				if (filteredVariants.length > 0) { filteredProduct.variants_attributes = filteredVariants; hasUpdatableProperty = true; } // Note that the name of the property changes to enable mass assignment of variants attributes with rails

				if (hasUpdatableProperty) filteredProducts.push(filteredProduct);
			}
		}
	}
	return filteredProducts;
}

function addDirtyProperty(dirtyObjects, objectID, propertyName, propertyValue){
	if (dirtyObjects.hasOwnProperty(objectID)){
		dirtyObjects[objectID][propertyName] = propertyValue;
	}
	else {
		dirtyObjects[objectID] = {};
		dirtyObjects[objectID]["id"] = objectID;
		dirtyObjects[objectID][propertyName] = propertyValue;
	}
}

function removeCleanProperty(dirtyObjects, objectID, propertyName){
	if (dirtyObjects.hasOwnProperty(objectID) && dirtyObjects[objectID].hasOwnProperty(propertyName)) delete dirtyObjects[objectID][propertyName];
	if (dirtyObjects.hasOwnProperty(objectID) && Object.keys(dirtyObjects[objectID]).length <= 1) delete dirtyObjects[objectID];
}

function toObjectWithIDKeys(array){
	var object = {};
	//if (array instanceof Array){
		for (i in array){
			if (array[i] instanceof Object && array[i].hasOwnProperty("id")){
				object[array[i].id] = angular.copy(array[i]);
				if (array[i].hasOwnProperty("variants") && array[i].variants instanceof Array){
					object[array[i].id].variants = toObjectWithIDKeys(array[i].variants);
				}
			}
		}
	//}
	return object;
}<commit_msg>BPUR: refactor of filterSubmitProducts<commit_after>var productsApp = angular.module('bulk_product_update', [])

productsApp.config(["$httpProvider", function(provider) {
  provider.defaults.headers.common['X-CSRF-Token'] = $('meta[name=csrf-token]').attr('content');
}]);

productsApp.directive('ngDecimal', function () {
	return {
		require: 'ngModel',
		link: function(scope, element, attrs, ngModel) {
			var numRegExp = /^\d+(\.\d+)?$/;

			element.bind('blur', function() {
				scope.$apply(ngModel.$setViewValue(ngModel.$modelValue));
				ngModel.$render();
			});

			ngModel.$setValidity('notADecimalError', function(){
				if (angular.isString(ngModel.$modelValue) && numRegExp.test(ngModel.$modelValue)){
					return true;
				}
				else{
					return false;
				}
			});

			ngModel.$parsers.push(function(viewValue){
				if (angular.isString(viewValue) && numRegExp.test(viewValue)){
					if (viewValue.indexOf(".") == -1){
						return viewValue+".0";
					}
				}
				return viewValue;
			});
		}
	}
});

productsApp.directive('ngTrackProduct', function(){
	return {
		require: 'ngModel',
		link: function(scope, element, attrs, ngModel) {
			var property = attrs.ngTrackProduct;
			var clean_value = angular.copy(scope.product[property]);
			element.bind('blur', function() {
				if (scope.product[property] == clean_value) removeCleanProperty(scope.dirtyProducts, scope.product.id, property);
				else addDirtyProperty(scope.dirtyProducts, scope.product.id, property, scope.product[property]);
				scope.$apply(scope.displayDirtyProducts());
			});
		}
	}
});

productsApp.directive('ngTrackVariant', function(){
	return {
		require: 'ngModel',
		link: function(scope, element, attrs, ngModel) {
			var property = attrs.ngTrackVariant;
			var clean_value = angular.copy(scope.variant[property]);
			element.bind('blur', function() {
				var dirtyVariants = {};
				if (scope.dirtyProducts.hasOwnProperty(scope.product.id) && scope.dirtyProducts[scope.product.id].hasOwnProperty("variants")) dirtyVariants = scope.dirtyProducts[scope.product.id].variants;
				if (scope.variant[property] == clean_value){
					removeCleanProperty(dirtyVariants, scope.variant.id, property);
					if (dirtyVariants == {}) removeCleanProperty(scope.dirtyProducts, scope.product.id, "variants");
				}
				else {
					addDirtyProperty(dirtyVariants, scope.variant.id, property, scope.variant[property]);
					addDirtyProperty(scope.dirtyProducts, scope.product.id, "variants", dirtyVariants);
				}
				scope.$apply(scope.displayDirtyProducts());
			});
		}
	}
});

productsApp.controller('AdminBulkProductsCtrl', function($scope, $timeout, $http, dataFetcher) {
	$scope.dirtyProducts = {};

	$scope.updateStatusMessage = {
		text: "",
		style: {}
	}

	$scope.refreshSuppliers = function(){
		dataFetcher('/enterprises/suppliers.json').then(function(data){
			$scope.suppliers = data;
		});
	};

	$scope.refreshProducts = function(){
		dataFetcher('/admin/products/bulk_index.json').then(function(data){
			$scope.products = toObjectWithIDKeys(data);
		});
	};

	$scope.updateOnHand = function(product){
		product.on_hand = onHand(product);
	}

	$scope.deleteProduct = function(product){
		$http({
			method: 'DELETE',
			url: '/admin/products/'+product.permalink_live+".js"
		})
		.success(function(data){
			delete $scope.products[product.id]
			if ($scope.dirtyProducts.hasOwnProperty(product.id)) delete $scope.dirtyProducts[product.id]
		})
	}

	$scope.deleteVariant = function(product,variant){
		$http({
			method: 'DELETE',
			url: '/admin/products/'+product.permalink_live+"/variants/"+variant.id+".js"
		})
		.success(function(data){
			delete $scope.products[product.id].variants[variant.id]
			if ($scope.dirtyProducts.hasOwnProperty(product.id) && $scope.dirtyProducts[product.id].hasOwnProperty("variants") && $scope.dirtyProducts[product.id].variants.hasOwnProperty(variant.id)) delete $scope.dirtyProducts[product.id].variants[variant.id]
		})
	}

	$scope.hasVariants = function(product){
		return !angular.equals(product.variants,{});
	}

	$scope.updateProducts = function(productsToSubmit){
		$scope.displayUpdating();
		$http({
			method: 'POST',
			url: '/admin/products/bulk_update',
			data: productsToSubmit
		})
		.success(function(data){
			data = toObjectWithIDKeys(data);
			if (angular.toJson($scope.products) == angular.toJson(data)){
				$scope.products = data;
				$scope.displaySuccess();
			}
			else{
				$scope.displayFailure("Product lists do not match.");
			}
		})
		.error(function(data,status){
			$scope.displayFailure("Server returned with error status: "+status);
		});
	}

	$scope.prepareProductsForSubmit = function(){
		var productsToSubmit = filterSubmitProducts($scope.dirtyProducts);
		$scope.updateProducts(productsToSubmit);
	}

	$scope.setMessage = function(model,text,style,timeout){
		model.text = text;
		model.style = style;
		if (timeout){
			$timeout(function() { $scope.setMessage(model,"",{},false); }, timeout, true);
		}
	}

	$scope.displayUpdating = function(){
		$scope.setMessage($scope.updateStatusMessage,"Updating...",{ color: "orange" },false);
	}

	$scope.displaySuccess = function(){
		$scope.setMessage($scope.updateStatusMessage,"Update complete",{ color: "green" },3000);
	}

	$scope.displayFailure = function(failMessage){
		$scope.setMessage($scope.updateStatusMessage,"Updating failed. "+failMessage,{ color: "red" },10000);
	}

	$scope.displayDirtyProducts = function(){
		var changedProductCount = Object.keys($scope.dirtyProducts).length;
		if (changedProductCount > 0) $scope.setMessage($scope.updateStatusMessage,"Changes to "+Object.keys($scope.dirtyProducts).length+" products remain unsaved.",{ color: "gray" },false);
		else $scope.setMessage($scope.updateStatusMessage,"",{},false);
	}
});

productsApp.factory('dataFetcher', function($http,$q){
	return function(dataLocation){
		var deferred = $q.defer();
		$http.get(dataLocation).success(function(data) {
			deferred.resolve(data);
		}).error(function(){
			deferred.reject();
		});
		return deferred.promise;
	};
});

function onHand(product){
	var onHand = 0;
	if(product.hasOwnProperty('variants') && product.variants instanceof Object){
		angular.forEach(product.variants, function(variant) {
			onHand = parseInt( onHand ) + parseInt( variant.on_hand > 0 ? variant.on_hand : 0 );
		});
	}
	else{
		onHand = 'error';
	}
	return onHand;
}

function filterSubmitProducts(productsToFilter){
	var filteredProducts= [];

	if (productsToFilter instanceof Object){
		angular.forEach(productsToFilter, function(product){
		//var productKeys = Object.keys(productsToFilter);
		//for (i in productKeys) {
			if (product.hasOwnProperty("id")){
				var filteredProduct = {};
				var filteredVariants = [];

				if (product.hasOwnProperty("variants")){
					angular.forEach(product.variants, function(variant){
					//var variantKeys = Object.keys(product.variants);
					//for (j in variantKeys){
						if (variant.deleted_at == null && variant.hasOwnProperty("id")){
							var hasUpdateableProperty = false;
							var filteredVariant = {};
							filteredVariant.id = variant.id;
							if (variant.hasOwnProperty("on_hand")) { filteredVariant.on_hand = variant.on_hand; hasUpdatableProperty = true; }
							if (variant.hasOwnProperty("price")) { filteredVariant.price = variant.price; hasUpdatableProperty = true; }
							if (hasUpdatableProperty) filteredVariants.push(filteredVariant);
						}
					//}
					});
				}

				var hasUpdatableProperty = false;
				filteredProduct.id = product.id;
				if (product.hasOwnProperty("name")) { filteredProduct.name = product.name; hasUpdatableProperty = true; }
				if (product.hasOwnProperty("supplier_id")) { filteredProduct.supplier_id = product.supplier_id; hasUpdatableProperty = true; }
				//if (product.hasOwnProperty("master")) filteredProduct.master_attributes = product.master
				if (product.hasOwnProperty("price")) { filteredProduct.price = product.price; hasUpdatableProperty = true; }
				if (product.hasOwnProperty("on_hand") && filteredVariants.length == 0) { filteredProduct.on_hand = product.on_hand; hasUpdatableProperty = true; } //only update if no variants present
				if (product.hasOwnProperty("available_on")) { filteredProduct.available_on = product.available_on; hasUpdatableProperty = true; }
				if (filteredVariants.length > 0) { filteredProduct.variants_attributes = filteredVariants; hasUpdatableProperty = true; } // Note that the name of the property changes to enable mass assignment of variants attributes with rails

				if (hasUpdatableProperty) filteredProducts.push(filteredProduct);
			}
		//}
		});
	}
	return filteredProducts;
}

function addDirtyProperty(dirtyObjects, objectID, propertyName, propertyValue){
	if (dirtyObjects.hasOwnProperty(objectID)){
		dirtyObjects[objectID][propertyName] = propertyValue;
	}
	else {
		dirtyObjects[objectID] = {};
		dirtyObjects[objectID]["id"] = objectID;
		dirtyObjects[objectID][propertyName] = propertyValue;
	}
}

function removeCleanProperty(dirtyObjects, objectID, propertyName){
	if (dirtyObjects.hasOwnProperty(objectID) && dirtyObjects[objectID].hasOwnProperty(propertyName)) delete dirtyObjects[objectID][propertyName];
	if (dirtyObjects.hasOwnProperty(objectID) && Object.keys(dirtyObjects[objectID]).length <= 1) delete dirtyObjects[objectID];
}

function toObjectWithIDKeys(array){
	var object = {};
	//if (array instanceof Array){
		for (i in array){
			if (array[i] instanceof Object && array[i].hasOwnProperty("id")){
				object[array[i].id] = angular.copy(array[i]);
				if (array[i].hasOwnProperty("variants") && array[i].variants instanceof Array){
					object[array[i].id].variants = toObjectWithIDKeys(array[i].variants);
				}
			}
		}
	//}
	return object;
}<|endoftext|>


<commit_before>import hue from "node-hue-api";
import config from "../config/index.js";
import parseColor from "parse-color";
import convertColor from "color-convert";
import { poll } from "./sensors.js";

const hueApi = hue.v3.api;
const LightState = hue.v3.lightStates.LightState;

let __api;
const getApi = async () => {
	if (__api) {
		return __api;
	}
	const hostname = await config.hostname();
	__api = await new hueApi.createLocal(hostname).connect(config.username);
	return __api;
};

const execute = async (key, job) => {
	let api;
	try {
		api = await getApi()
	} catch (err) {
		console.error(err.message);
		return
	}

	// Does the job have a condition
	if (job.condition) {
		const conditionMet = await checkCondition(job.condition);
		if (!conditionMet) {
			return
		}
	}

	// Now execute
	if (job.action) {
		let lights = await api.lights.getAll();
		let actionLights = job.action.lights;

		// First make sure we're dealing with an array of lights
		if (actionLights.constructor !== Array) {
			actionLights = [actionLights]
		}

		// Create map of names to lights
		const lightNames = lights.reduce((map, light) => {
			map[light.name] = light;
			return map
		}, {});

		// Look up light objects
		actionLights = actionLights.map(light => lightNames[light] ? lightNames[light] : light);

		// Filter any that we didn't find
		const missingLights = actionLights.filter(light => typeof(light) === 'string');
		actionLights = actionLights.filter(light => typeof(light) !== 'string');

		if (missingLights.length>0) {
			console.warn(`Could not resolve the following light names: ${missingLights}`)
		}

		// console.log(`Executing job ${key} on ${actionLights.length} lights.`);

		switch (job.action.method) {
			case 'colour':
				await executeColour(actionLights, job);
				break;
			case 'dim':
				await executeDim(actionLights, job);
				break;
			case 'flash':
				await executeFlash(actionLights, job);
				break;
			case 'random':
				await executeRandom(actionLights, job);
				break;
			case 'pollSensors':
				await poll();
				break;
			default:
				console.log(`Unknown action: ${job.action.method}`)
		}

	}
};

const checkCondition = async condition => {
	const api = await getApi();
	let groups = await api.groups.getAll();
	groups = groups.filter((group) => group.name.toUpperCase() === condition.room.toUpperCase());
	if (groups.length === 0) {
		console.log(`Room ${condition.room} not found for condition. Failing.`);
		return false
	}

	const group = groups[0];
	switch(condition.status) {
		case 'all_on':
		case 'any_on':
			return group.state[condition.status];
		case 'all_off':
			return !group.state.any_on;
		default:
			return false
		}
};

const executeDim = async (lights, job) => {
	const api = await getApi();
	const target = job.action.target ? job.action.target : 0;
	const step = job.action.step ? job.action.step : 10;
	//For each light that is above the target, reduce the brightness until it hits the target
	await Promise.all(lights.map((light) => {
		if (light.state.on && light.state.bri > target) {
			const lightTarget = Math.max(target, light.state.bri - step);
			console.log(`Reducing ${light.id} from ${light.state.bri} to ${lightTarget}. Target ${target}.`);
			api.lights.setLightState(light.id, {'bri': lightTarget});
		}
	}));

};

const executeRandom = async (lights, job) => {
	const api = await getApi();
	const color = Math.floor(Math.random() * 360);
	const rgb = convertColor.hsv.rgb(color, 100, 100);
	let newState = new LightState().on().rgb(rgb).brightness(100);

	if (job.action.transition) {
		newState = newState.transition(job.action.transition)
	}

	//For each light that is above the target, reduce the brightness until it hits the target
	await Promise.all(lights.map((light) =>
		api.lights.setLightState(light.id, newState)
	));
};

const executeFlash = async (lights, job) => {
	const api = await getApi();
	const colour = parseColor(job.action.colour);
	const timeout = job.action.timeout ? parseInt(job.action.timeout) : 5000;

	const originalStates = lights.map(light => {return {id: light.id, state: light.state}});

	// Return to original state
	const resetState = () => {
		Promise.all(originalStates.map((light) => {
			light.state.alert = 'none';
			api.lights.setLightState(light.id, light.state)
		}))
	};

	// Create the alert state
	const state = new LightState().on().rgb(colour.rgb).alert('lselect');

	// Execute chain
	await Promise.all(lights.map((light) => api.lights.setLightState(light.id, state)))
		.then(() => setTimeout(resetState, timeout))
};

const executeColour = async (lights, job) => {
	const api = await getApi();
	const groups = job.action.groups ? job.action.groups : lights.length;

	let colours = job.action.colours.map(c => {
		if (c === 'random') {
			const hsv = Math.floor(Math.random() * 360);
			c = '#' + convertColor.hsv.hex(hsv, 100, 100);
		}
		return c;
	});
	colours = colours.map(c => parseColor(c));

	await Promise.all(lights.map((light, ix) => {
		ix = ix % groups;
		const c = colours[(job.invocation + ix) % colours.length];

		let state = new LightState()

		if (!light.state.on) {
			if (job.action.on) {
				state = state.on();
			} else {
				return Promise.resolve();
			}
		}

		if (job.action.brightness) {
			state = state.brightness(job.action.brightness);
		}

		if (light.state.colormode === "xy") {
			state = state.rgb(c.rgb)
		}

		if (job.action.transition) {
			state = state.transition(job.action.transition)
		}

		return api.lights.setLightState(light.id, state)
	}));
};

export default {
	api: getApi,
	execute: execute
};
<commit_msg>Fix some tiny bugs...<commit_after>import hue from "node-hue-api";
import config from "../config/index.js";
import parseColor from "parse-color";
import convertColor from "color-convert";
import { poll } from "./sensors.js";

const hueApi = hue.v3.api;
const LightState = hue.v3.lightStates.LightState;

let __api;
const getApi = async () => {
	if (__api) {
		return __api;
	}
	const hostname = await config.hostname();
	__api = await new hueApi.createLocal(hostname).connect(config.username);
	return __api;
};

const execute = async (key, job) => {
	let api;
	try {
		api = await getApi()
	} catch (err) {
		console.error(err.message);
		return
	}

	// Does the job have a condition
	if (job.condition) {
		const conditionMet = await checkCondition(job.condition);
		if (!conditionMet) {
			return
		}
	}

	// Now execute
	if (job.action) {
		let lights = await api.lights.getAll();
		let actionLights = job.action.lights;

		// First make sure we're dealing with an array of lights
		if (actionLights.constructor !== Array) {
			actionLights = [actionLights]
		}

		// Create map of names to lights
		const lightNames = lights.reduce((map, light) => {
			map[light.name] = light;
			map[light.id] = light;
			return map
		}, {});

		// Look up light objects
		actionLights = actionLights.map(light => lightNames[light] ? lightNames[light] : light);

		// Filter any that we didn't find
		const missingLights = actionLights.filter(light => typeof(light) === 'string');
		actionLights = actionLights.filter(light => typeof(light) !== 'string');

		if (missingLights.length>0) {
			console.warn(`Could not resolve the following light names: ${missingLights}`)
		}

		// console.log(`Executing job ${key} on ${actionLights.length} lights.`);

		switch (job.action.method) {
			case 'colour':
				await executeColour(actionLights, job);
				break;
			case 'dim':
				await executeDim(actionLights, job);
				break;
			case 'flash':
				await executeFlash(actionLights, job);
				break;
			case 'random':
				await executeRandom(actionLights, job);
				break;
			case 'pollSensors':
				await poll();
				break;
			default:
				console.log(`Unknown action: ${job.action.method}`)
		}

	}
};

const checkCondition = async condition => {
	const api = await getApi();
	let groups = await api.groups.getAll();
	groups = groups.filter((group) => group.name.toUpperCase() === condition.room.toUpperCase());
	if (groups.length === 0) {
		console.log(`Room ${condition.room} not found for condition. Failing.`);
		return false
	}

	const group = groups[0];
	switch(condition.status) {
		case 'all_on':
		case 'any_on':
			return group.state[condition.status];
		case 'all_off':
			return !group.state.any_on;
		default:
			return false
		}
};

const executeDim = async (lights, job) => {
	const api = await getApi();
	const target = job.action.target ? job.action.target : 0;
	const step = job.action.step ? job.action.step : 10;
	//For each light that is above the target, reduce the brightness until it hits the target
	await Promise.all(lights.map((light) => {
		const promises = []
		if (light.state && light.state.on && light.state.bri > target) {
			const lightTarget = Math.max(target, light.state.bri - step);
			console.log(`Reducing ${light.name} from ${light.state.bri} to ${lightTarget}. Target ${target}.`);
			promises.push(api.lights.setLightState(light.id, {'bri': lightTarget}));
		}
		return promises;
	}));

};

const executeRandom = async (lights, job) => {
	const api = await getApi();
	const color = Math.floor(Math.random() * 360);
	const rgb = convertColor.hsv.rgb(color, 100, 100);
	let newState = new LightState().on().rgb(rgb).brightness(100);

	if (job.action.transition) {
		newState = newState.transition(job.action.transition)
	}

	//For each light that is above the target, reduce the brightness until it hits the target
	await Promise.all(lights.map((light) =>
		api.lights.setLightState(light.id, newState)
	));
};

const executeFlash = async (lights, job) => {
	const api = await getApi();
	const colour = parseColor(job.action.colour);
	const timeout = job.action.timeout ? parseInt(job.action.timeout) : 5000;

	const originalStates = lights.map(light => {return {id: light.id, state: light.state}});

	// Return to original state
	const resetState = () => {
		Promise.all(originalStates.map((light) => {
			light.state.alert = 'none';
			api.lights.setLightState(light.id, light.state)
		}))
	};

	// Create the alert state
	const state = new LightState().on().rgb(colour.rgb).alert('lselect');

	// Execute chain
	await Promise.all(lights.map((light) => api.lights.setLightState(light.id, state)))
		.then(() => setTimeout(resetState, timeout))
};

const executeColour = async (lights, job) => {
	const api = await getApi();
	const groups = job.action.groups ? job.action.groups : lights.length;

	let colours = job.action.colours.map(c => {
		if (c === 'random') {
			const hsv = Math.floor(Math.random() * 360);
			c = '#' + convertColor.hsv.hex(hsv, 100, 100);
		}
		return c;
	});
	colours = colours.map(c => parseColor(c));

	await Promise.all(lights.map((light, ix) => {
		ix = ix % groups;
		const c = colours[(job.invocation + ix) % colours.length];

		let state = new LightState()

		if (!light.state.on) {
			if (job.action.on) {
				state = state.on();
			} else {
				return Promise.resolve();
			}
		}

		if (job.action.brightness) {
			state = state.brightness(job.action.brightness);
		}

		if (light.state.colormode === "xy") {
			state = state.rgb(c.rgb)
		}

		if (job.action.transition) {
			state = state.transition(job.action.transition)
		}

		return api.lights.setLightState(light.id, state)
	}));
};

export default {
	api: getApi,
	execute: execute
};
<|endoftext|>


<commit_before>// @flow
/* eslint-disable jsx-a11y/href-no-hash */

import _ from "lodash";
import * as React from "react";
import { Table, Icon, Spin, Button, Input, Modal } from "antd";
import Utils from "libs/utils";
import messages from "messages";
import CreateTeamModal from "admin/team/create_team_modal_view.js";
import { getEditableTeams, deleteTeam } from "admin/admin_rest_api";
import Persistence from "libs/persistence";
import { PropTypes } from "@scalableminds/prop-types";
import { withRouter } from "react-router-dom";
import type { APITeamType } from "admin/api_flow_types";
import type { RouterHistory } from "react-router-dom";

const { Column } = Table;
const { Search } = Input;

type Props = {
  history: RouterHistory,
};

type State = {
  isLoading: boolean,
  teams: Array<APITeamType>,
  searchQuery: string,
  isTeamCreationModalVisible: boolean,
};

const persistence: Persistence<State> = new Persistence(
  { searchQuery: PropTypes.string },
  "teamList",
);

class TeamListView extends React.PureComponent<Props, State> {
  state = {
    isLoading: true,
    teams: [],
    searchQuery: "",
    isTeamCreationModalVisible: false,
  };

  componentWillMount() {
    this.setState(persistence.load(this.props.history));
  }

  componentDidMount() {
    this.fetchData();
  }

  componentWillUpdate(nextProps, nextState) {
    persistence.persist(this.props.history, nextState);
  }

  async fetchData(): Promise<void> {
    const teams = await getEditableTeams();

    this.setState({
      isLoading: false,
      teams,
    });
  }

  handleSearch = (event: SyntheticInputEvent<>): void => {
    this.setState({ searchQuery: event.target.value });
  };

  deleteTeam = (team: APITeamType) => {
    Modal.confirm({
      title: messages["team.delete"],
      onOk: async () => {
        this.setState({
          isLoading: true,
        });

        deleteTeam(team.id).then(
          () => {
            this.setState({
              isLoading: false,
              teams: this.state.teams.filter(t => t.id !== team.id),
            });
          },
          () => {
            this.setState({
              isLoading: false,
            });
          },
        );
      },
    });
  };

  createTeam = async (newTeam: APITeamType) => {
    this.setState({
      isTeamCreationModalVisible: false,
      teams: this.state.teams.concat([newTeam]),
    });
  };

  render() {
    const marginRight = { marginRight: 20 };

    return (

            <Button
              icon="plus"
              style={marginRight}
              type="primary"
              onClick={() => this.setState({ isTeamCreationModalVisible: true })}
            >
              Add Team
            </Button>
            <Search
              style={{ width: 200 }}
              onPressEnter={this.handleSearch}
              onChange={this.handleSearch}
              value={this.state.searchQuery}
            />

Teams

          <Spin spinning={this.state.isLoading} size="large">

              <Column
                title="Name"
                dataIndex="name"
                key="name"
                sorter={Utils.localeCompareBy("name")}
              />
              <Column
                title="Action"
                key="actions"
                render={(__, script: APITeamType) => (

                    <Icon type="delete" />Delete

                )}
              />

          </Spin>
          <CreateTeamModal
            teams={this.state.teams}
            isVisible={this.state.isTeamCreationModalVisible}
            onOk={this.createTeam}
            onCancel={() => this.setState({ isTeamCreationModalVisible: false })}
          />

    );
  }
}

export default withRouter(TeamListView);
<commit_msg>Fix team search (#2573)<commit_after>// @flow
/* eslint-disable jsx-a11y/href-no-hash */

import _ from "lodash";
import * as React from "react";
import { Table, Icon, Spin, Button, Input, Modal } from "antd";
import Utils from "libs/utils";
import messages from "messages";
import CreateTeamModal from "admin/team/create_team_modal_view.js";
import { getEditableTeams, deleteTeam } from "admin/admin_rest_api";
import Persistence from "libs/persistence";
import { PropTypes } from "@scalableminds/prop-types";
import { withRouter } from "react-router-dom";
import type { APITeamType } from "admin/api_flow_types";
import type { RouterHistory } from "react-router-dom";

const { Column } = Table;
const { Search } = Input;

type Props = {
  history: RouterHistory,
};

type State = {
  isLoading: boolean,
  teams: Array<APITeamType>,
  searchQuery: string,
  isTeamCreationModalVisible: boolean,
};

const persistence: Persistence<State> = new Persistence(
  { searchQuery: PropTypes.string },
  "teamList",
);

class TeamListView extends React.PureComponent<Props, State> {
  state = {
    isLoading: true,
    teams: [],
    searchQuery: "",
    isTeamCreationModalVisible: false,
  };

  componentWillMount() {
    this.setState(persistence.load(this.props.history));
  }

  componentDidMount() {
    this.fetchData();
  }

  componentWillUpdate(nextProps, nextState) {
    persistence.persist(this.props.history, nextState);
  }

  async fetchData(): Promise<void> {
    const teams = await getEditableTeams();

    this.setState({
      isLoading: false,
      teams,
    });
  }

  handleSearch = (event: SyntheticInputEvent<>): void => {
    this.setState({ searchQuery: event.target.value });
  };

  deleteTeam = (team: APITeamType) => {
    Modal.confirm({
      title: messages["team.delete"],
      onOk: async () => {
        this.setState({
          isLoading: true,
        });

        deleteTeam(team.id).then(
          () => {
            this.setState({
              isLoading: false,
              teams: this.state.teams.filter(t => t.id !== team.id),
            });
          },
          () => {
            this.setState({
              isLoading: false,
            });
          },
        );
      },
    });
  };

  createTeam = async (newTeam: APITeamType) => {
    this.setState({
      isTeamCreationModalVisible: false,
      teams: this.state.teams.concat([newTeam]),
    });
  };

  render() {
    const marginRight = { marginRight: 20 };

    return (

            <Button
              icon="plus"
              style={marginRight}
              type="primary"
              onClick={() => this.setState({ isTeamCreationModalVisible: true })}
            >
              Add Team
            </Button>
            <Search
              style={{ width: 200 }}
              onPressEnter={this.handleSearch}
              onChange={this.handleSearch}
              value={this.state.searchQuery}
            />

Teams

          <Spin spinning={this.state.isLoading} size="large">

              <Column
                title="Name"
                dataIndex="name"
                key="name"
                sorter={Utils.localeCompareBy("name")}
              />
              <Column
                title="Action"
                key="actions"
                render={(__, script: APITeamType) => (

                    <Icon type="delete" />Delete

                )}
              />

          </Spin>
          <CreateTeamModal
            teams={this.state.teams}
            isVisible={this.state.isTeamCreationModalVisible}
            onOk={this.createTeam}
            onCancel={() => this.setState({ isTeamCreationModalVisible: false })}
          />

    );
  }
}

export default withRouter(TeamListView);
<|endoftext|>


<commit_before>// A hook that logs service method before, after and error
const logger = require('winston')

export function log (hook) {
  let message = `${hook.type}: ${hook.path} - Method: ${hook.method}`

  if (hook.type === 'error') {
    message += `: ${hook.error.message}`
  }

  if (hook.error) {
    logger.error(message)
  } else {
    logger.debug(message)
  }

  logger.silly('hook.data', hook.data)
  logger.silly('hook.params', hook.params)

  if (hook.result) {
    logger.silly('hook.result', hook.result)
  }
}
<commit_msg>Added stack trace on error to help debug<commit_after>// A hook that logs service method before, after and error
const logger = require('winston')

export function log (hook) {
  let message = `${hook.type}: ${hook.path} - Method: ${hook.method}`

  if (hook.type === 'error') {
    message += `: ${hook.error.message}`
  }

  if (hook.error) {
    logger.error(message, hook.error.stack)
  } else {
    logger.debug(message)
  }

  logger.silly('hook.data', hook.data)
  logger.silly('hook.params', hook.params)

  if (hook.result) {
    logger.silly('hook.result', hook.result)
  }
}
<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require ckeditor/override
//= require ckeditor/init
//= require_tree .
<commit_msg>remove ckeditor override, as the latest version doesn't require it<commit_after>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require ckeditor/init
//= require_tree .
<|endoftext|>


<commit_before>// Control-flow utilities.
var cadence = require('cadence')
var assert = require('assert')

// An evented message queue.
var Procession = require('procession')

// Evented stream reading and writing.
var Staccato = require('staccato')

// JSON for use in packets.
var Jacket = require('nascent.jacket')

// Orderly destruction of complicated objects.
var Destructible = require('destructible')

// Evented semaphore.
var Signal = require('signal')

// Return the first not null-like value.
var coalesce = require('extant')

// An error-frist callback work queue.
var Turnstile = require('turnstile/redux')
Turnstile.Queue = require('turnstile/queue')

function Conduit (input, output, receiver) {
    this.destroyed = false

    this._destructible = new Destructible('conduit/connection')
    this._destructible.markDestroyed(this)

    this._input = new Staccato.Readable(input)
    this._destructible.addDestructor('input', this._input, 'destroy')

    this._output = new Staccato.Writable(output)
    this._destructible.addDestructor('output', this._output, 'destroy')

    this._turnstile = new Turnstile
    this._queue = new Turnstile.Queue(this, '_write', this._turnstile)
    this._destructible.addDestructor('turnstile', this._turnstile, 'pause')

    this.receiver = receiver
    var pump = this.receiver.read.shifter().pump(this._queue, 'push')
    this._destructible.addDestructor('pump', pump, 'cancel')

    this._slices = []

    this._record = new Jacket

    this._closed = new Signal
    this._destructible.addDestructor('closed', this._closed, 'unlatch')

    this.ready = new Signal
    this._destructible.addDestructor('ready', this.ready, 'unlatch')
}

Conduit.prototype._pump = cadence(function (async, buffer) {
    async(function () {
        this._parse(coalesce(buffer, new Buffer(0)), async())
        this.ready.unlatch()
    }, function () {
        this._read(async())
    }, function () {
        this._closed.wait(async())
    })
})

Conduit.prototype._buffer = cadence(function (async, buffer, start, end) {
    async(function () {
        var length = Math.min(buffer.length - start, this._chunk.length)
        var slice = buffer.slice(start, start + length)
        start += length
        this._chunk.length -= length
        if (this._chunk.length == 0) {
            if (this._slices.length) {
                this._slices.push(slice)
                slice = Buffer.concat(this._slices)
                this._slices.length = 0
            } else {
                slice = new Buffer(slice)
            }
            var envelope = this._chunk.body
            var e = envelope
            while (e.body != null) {
                e = e.body
            }
            e.body = slice
            this._chunk = null
            this._record = new Jacket
            this.receiver.write.enqueue(envelope, async())
        } else {
            this._slices.push(new Buffer(slice))
        }
    }, function () {
        return start
    })
})

Conduit.prototype._json = cadence(function (async, buffer, start, end) {
    start = this._record.parse(buffer, start, end)
    async(function () {
        if (this._record.object != null) {
            var envelope = this._record.object
            switch (envelope.method) {
            case 'envelope':
                this.receiver.write.enqueue(envelope.body, async())
                break
            case 'chunk':
                this._chunk = this._record.object
                break
            case 'trailer':
                // var socket = this._sockets[envelope.to]
                this.receiver.write.enqueue(null, async())
                // delete this._sockets[envelope.to]
                break
            }
            this._record = new Jacket
        }
    }, function () {
        return start
    })
})

Conduit.prototype._parse = cadence(function (async, buffer) {
    var parse = async(function (start) {
        if (start == buffer.length) {
            return [ parse.break ]
        }
        if (this._chunk != null) {
            this._buffer(buffer, start, buffer.length, async())
        } else {
            this._json(buffer, start, buffer.length, async())
        }
    })(0)
})

Conduit.prototype._read = cadence(function (async) {
    var read = async(function () {
        this._input.read(async())
    }, function (buffer) {
        if (buffer == null) {
            return [ read.break ]
        }
        this._parse(buffer, async())
    })()
})

Conduit.prototype._write = cadence(function (async, envelope) {
    envelope = envelope.body
    async(function () {
        if (envelope == null) {
            // TODO And then destroy the conduit.
            this._output.write(JSON.stringify({
                module: 'conduit',
                method: 'trailer',
                body: null
            }) + '\n', async())
            this._closed.unlatch()
        } else {
            var e = envelope
            while (e.body != null && typeof e.body == 'object' && !Buffer.isBuffer(e.body)) {
                e = e.body
            }
            if (Buffer.isBuffer(e.body)) {
                var body = e.body
                e.body = null
                var packet = JSON.stringify({
                    module: 'conduit',
                    method: 'chunk',
                    length: body.length,
                    body: envelope
                }) + '\n'
                e.body = body
                this._output.write(packet, async())
                this._output.write(e.body, async())
            } else {
                this._output.write(JSON.stringify({
                    module: 'conduit',
                    method: 'envelope',
                    body: envelope
                }) + '\n', async())
            }
        }
    }, function () {
        return []
    })
})

Conduit.prototype.listen = function (buffer, callback) {
    this._queue.turnstile.listen(this._destructible.monitor('turnstile'))
    this._pump(buffer, this._destructible.monitor('pump'))
    this._destructible.completed(callback)
}

Conduit.prototype.destroy = function () {
    this._destructible.destroy()
}

module.exports = Conduit
<commit_msg>Send connect message.<commit_after>// Control-flow utilities.
var cadence = require('cadence')
var assert = require('assert')

// An evented message queue.
var Procession = require('procession')

// Evented stream reading and writing.
var Staccato = require('staccato')

// JSON for use in packets.
var Jacket = require('nascent.jacket')

// Orderly destruction of complicated objects.
var Destructible = require('destructible')

// Evented semaphore.
var Signal = require('signal')

// Return the first not null-like value.
var coalesce = require('extant')

// An error-frist callback work queue.
var Turnstile = require('turnstile/redux')
Turnstile.Queue = require('turnstile/queue')

function Conduit (input, output, receiver) {
    this.destroyed = false

    this._destructible = new Destructible('conduit/connection')
    this._destructible.markDestroyed(this)

    this._input = new Staccato.Readable(input)
    this._destructible.addDestructor('input', this._input, 'destroy')

    this._output = new Staccato.Writable(output)
    this._destructible.addDestructor('output', this._output, 'destroy')

    this._turnstile = new Turnstile
    this._queue = new Turnstile.Queue(this, '_write', this._turnstile)
    this._destructible.addDestructor('turnstile', this._turnstile, 'pause')

    this.receiver = receiver
    var pump = this.receiver.read.shifter().pump(this._queue, 'push')
    this._destructible.addDestructor('pump', pump, 'cancel')

    this._slices = []

    this._record = new Jacket

    this._closed = new Signal
    this._destructible.addDestructor('closed', this._closed, 'unlatch')

    this.ready = new Signal
    this._destructible.addDestructor('ready', this.ready, 'unlatch')
}

Conduit.prototype._pump = cadence(function (async, buffer) {
    async(function () {
        this._parse(coalesce(buffer, new Buffer(0)), async())
        this.ready.unlatch()
    }, function () {
        this._read(async())
    }, function () {
        this._closed.wait(async())
    })
})

Conduit.prototype._buffer = cadence(function (async, buffer, start, end) {
    async(function () {
        var length = Math.min(buffer.length - start, this._chunk.length)
        var slice = buffer.slice(start, start + length)
        start += length
        this._chunk.length -= length
        if (this._chunk.length == 0) {
            if (this._slices.length) {
                this._slices.push(slice)
                slice = Buffer.concat(this._slices)
                this._slices.length = 0
            } else {
                slice = new Buffer(slice)
            }
            var envelope = this._chunk.body
            var e = envelope
            while (e.body != null) {
                e = e.body
            }
            e.body = slice
            this._chunk = null
            this._record = new Jacket
            this.receiver.write.enqueue(envelope, async())
        } else {
            this._slices.push(new Buffer(slice))
        }
    }, function () {
        return start
    })
})

Conduit.prototype._json = cadence(function (async, buffer, start, end) {
    start = this._record.parse(buffer, start, end)
    async(function () {
        if (this._record.object != null) {
            var envelope = this._record.object
            switch (envelope.method) {
            case 'envelope':
                this.receiver.write.enqueue(envelope.body, async())
                break
            case 'chunk':
                this._chunk = this._record.object
                break
            case 'trailer':
                // var socket = this._sockets[envelope.to]
                this.receiver.write.enqueue(null, async())
                // delete this._sockets[envelope.to]
                break
            }
            this._record = new Jacket
        }
    }, function () {
        return start
    })
})

Conduit.prototype._parse = cadence(function (async, buffer) {
    var parse = async(function (start) {
        if (start == buffer.length) {
            return [ parse.break ]
        }
        if (this._chunk != null) {
            this._buffer(buffer, start, buffer.length, async())
        } else {
            this._json(buffer, start, buffer.length, async())
        }
    })(0)
})

Conduit.prototype._read = cadence(function (async) {
    var read = async(function () {
        this._input.read(async())
    }, function (buffer) {
        if (buffer == null) {
            return [ read.break ]
        }
        this._parse(buffer, async())
    })()
})

Conduit.prototype._write = cadence(function (async, envelope) {
    envelope = envelope.body
    async(function () {
        if (envelope == null) {
            // TODO And then destroy the conduit.
            this._output.write(JSON.stringify({
                module: 'conduit',
                method: 'trailer',
                body: null
            }) + '\n', async())
            this._closed.unlatch()
        } else {
            var e = envelope
            while (e.body != null && typeof e.body == 'object' && !Buffer.isBuffer(e.body)) {
                e = e.body
            }
            if (Buffer.isBuffer(e.body)) {
                var body = e.body
                e.body = null
                var packet = JSON.stringify({
                    module: 'conduit',
                    method: 'chunk',
                    length: body.length,
                    body: envelope
                }) + '\n'
                e.body = body
                this._output.write(packet, async())
                this._output.write(e.body, async())
            } else {
                this._output.write(JSON.stringify({
                    module: 'conduit',
                    method: 'envelope',
                    body: envelope
                }) + '\n', async())
            }
        }
    }, function () {
        return []
    })
})

Conduit.prototype.listen = function (buffer, callback) {
    this.receiver.write.push({ module: 'conduit', method: 'connect' })
    this._queue.turnstile.listen(this._destructible.monitor('turnstile'))
    this._pump(buffer, this._destructible.monitor('pump'))
    this._destructible.completed(callback)
}

Conduit.prototype.destroy = function () {
    this._destructible.destroy()
}

module.exports = Conduit
<|endoftext|>