<commit_before>"use strict";
var stamp = require('swarm-stamp');
var Op = require('./Op');
var Syncable = require('./Syncable');
var LamportTimestamp = stamp.LamportTimestamp;

// This most basic class is a key-value JavaScript-style object.
// It is also also an example of how to implement Syncables.
// Model's distributed/concurrent behavior is of a very classic
// Last-Write-Wins object where all changes are timestamped and
// the greater timestamp "wins" (LWW is a corner-case CRDT too).
// Note that changes are merged in the sense that a change may
// leave values untouched: !time1.set {x:1, y:1}, then
// !time2.set {y:2}, results in {x:1, y:2}.
function Model (values, owner) {
    var init_op = null;
    if (values) {
        var bad_keys = Object.keys(values).some(function(key){
            return !Syncable.reFieldName.test(key);
        });
        if (bad_keys) {
            throw new Error('invalid keys');
        }
        init_op = new Op('.set', JSON.stringify(values)); // TODO 1.0 refs
    }
    // _owner _id _events
    Syncable.call(this, init_op, owner);
}
Model.prototype = Object.create( Syncable.prototype );
Model.prototype.constructor = Model;

// The API method for the .set op has the same name as the op, which is
// not always the case. It composes the op and submits it for execution.
Model.prototype.set = function (keys_values) {
    var bad = Object.keys(keys_values).some(function(key){
        return !Syncable.reFieldName.test(key);
    });
    for(var key in keys_values) {
        var val = keys_values[key];  // FIXME ugly
        if (val._type) {
            keys_values[key] = {ref: val.typeid()};
        }
    }
    if (bad) {
        throw new Error("malformed field name");
    }
    return this._owner.submit( this, 'set', JSON.stringify(keys_values) );
};

// The API user may directly modify the outer state and invoke save(),
// which converts de-facto changes into proper ops. ops change the
// inner state, the outer state gets regenerated, ops propagate
// to other replicas.
Model.prototype.save = function (inner) {
    var dirty = this.toPojo();
    var dirty_keys = this.keys();
    this.rebuild(this.getInnerState());
    var clean_keys = this.keys();
    var all_keys = dirty_keys.concat(clean_keys);
    var changes = {}, changed = false;
    // compare de-facto "dirty" values to the last version's values
    for(var i=0; i<all_keys.length; i++) {
        var key = all_keys[i];
        if (key.charAt(0)==='_') {continue;}
        if (Syncable.toPojo(this[key])!==dirty[key]) { // FIXME nesteds, deep compare
            // a change detected
            changes[key] = dirty[key]; // FIXME flatten
            changed = true;
        }
    }
    // finally, commit the changes
    changed && this.set(changes);
};

// A bit of syntactic sugar for Model listeners.
// Invoke model.onFieldChange('field', cb) to listen to that particular field.
Model.prototype.onFieldChange = function (field, callback, context) {
    /*if (filter.constructor===Function) {  ?
        context = callback;
        callback = filter;
        filter = null;
    }*/
    var filter = function (ev) {
        return field in ev.value;
    };
    Syncable.prototype.on.call(this, filter, callback, context);
};

Model.prototype.keys = function () {
    return Object.keys(this).filter(function(key){
        return Syncable.reFieldName.test(key);
    });
};

// The API exposes the outer state of a syncable. But, its
// inner state is its "true" state that also includes all
// the (CRDT) metadata. The outer state can always be generated
// from the inner state (see Syncable.rebuild()).
// This method is a constructor for the inner state.
// It either creates the default state or deserializes a .state
// op value.
function LWWObject (string) {
    // { stamp: {key:value} }
    this._version = null;
    var parsed = string ? JSON.parse(string) : {};
    var values = this.values = Object.create(null);
    // some paranoid checks are very much relevant here
    var stamps = Object.keys(parsed).filter(LamportTimestamp.is).sort();
    stamps.forEach(function (stamp){ // invert wire-format into mem-format
        var set = parsed[stamp];
        var keys=Object.keys(set).filter(LWWObject.is_field_name);
        keys.forEach(function(key) {
            values[key] = new StampedValue(set[key], stamp);
        });
    });
}
LWWObject.prototype = Object.create( Syncable.Inner.prototype );
LWWObject.prototype.constructor = LWWObject;
LWWObject.reFieldName = /^[a-z]\w*$/;
LWWObject.is_field_name = function (name) {
    return LWWObject.reFieldName.test(name);
};

function StampedValue (value, stamp) {
    this.value = value;
    this.stamp = stamp;
}

// This class implements just one kind of an op: set({key:value}).
// To implement your own ops you need to understand
// implications of partial order. Ops may be applied in slightly
// different orders at different replicas, but the result must
// converge. (see Model.playOpLog())
// An op is a method for an inner state object that consumes an
// op, changes inner state, no side effects allowed.
LWWObject.prototype.set = function (values, stamp) {
    var keys = Object.keys(values), self=this;
    keys = keys.filter(LWWObject.is_field_name);
    keys.forEach(function(key){
        var entry = self.values[key];
        if (entry===undefined || entry.stamp<stamp) {
            self.values[key] = new StampedValue(values[key], stamp);
        }
    });
};

LWWObject.prototype.write = function (op) {
    switch (op.op()) {
    case 'set': this.set(JSON.parse(op.value), op.stamp()); break;
    default:    throw new Error('operation unknown'); // FIXME
    }
};

// Produces the outer state from the inner state.
LWWObject.prototype.updateSyncable = function (syncable) {
    var values = this.values;
    Object.keys(values).forEach(function(k){
        var val = values[k].value;
        if (val.constructor===Object && val.ref) {
            val = syncable._owner.get(val.ref); // FIXME ugly
        }
        syncable[k] = val;
    });
    var missing_keys = Object.keys(syncable).
        filter(LWWObject.is_field_name).
        filter(function(key) { !(key in values); });
    missing_keys.forEach(function(key){
        delete syncable[key];
    });
};

// Serializes the inner state to a string. The constructor must
// be able to parse this later.
LWWObject.prototype.toString = function () {
    var wire = Object.create(null), values = this.values;
    var keys = Object.keys(this.values);
    keys.forEach(function(key){
        var entry = values[key], change = wire[entry.stamp];
        if (!change) {
            change = wire[entry.stamp] = Object.create(null);
        }
        change[key] = entry.value;
    });
    return JSON.stringify(wire);
};

Model.Inner = LWWObject;
Syncable.registerType('Model', Model);
module.exports = Model;
<commit_msg>model: add toString() method<commit_after>"use strict";
var stamp = require('swarm-stamp');
var Op = require('./Op');
var Syncable = require('./Syncable');
var LamportTimestamp = stamp.LamportTimestamp;

// This most basic class is a key-value JavaScript-style object.
// It is also also an example of how to implement Syncables.
// Model's distributed/concurrent behavior is of a very classic
// Last-Write-Wins object where all changes are timestamped and
// the greater timestamp "wins" (LWW is a corner-case CRDT too).
// Note that changes are merged in the sense that a change may
// leave values untouched: !time1.set {x:1, y:1}, then
// !time2.set {y:2}, results in {x:1, y:2}.
function Model (values, owner) {
    var init_op = null;
    if (values) {
        var bad_keys = Object.keys(values).some(function(key){
            return !Syncable.reFieldName.test(key);
        });
        if (bad_keys) {
            throw new Error('invalid keys');
        }
        init_op = new Op('.set', JSON.stringify(values)); // TODO 1.0 refs
    }
    // _owner _id _events
    Syncable.call(this, init_op, owner);
}
Model.prototype = Object.create( Syncable.prototype );
Model.prototype.constructor = Model;

// The API method for the .set op has the same name as the op, which is
// not always the case. It composes the op and submits it for execution.
Model.prototype.set = function (keys_values) {
    var bad = Object.keys(keys_values).some(function(key){
        return !Syncable.reFieldName.test(key);
    });
    for(var key in keys_values) {
        var val = keys_values[key];  // FIXME ugly
        if (val._type) {
            keys_values[key] = {ref: val.typeid()};
        }
    }
    if (bad) {
        throw new Error("malformed field name");
    }
    return this._owner.submit( this, 'set', JSON.stringify(keys_values) );
};

// The API user may directly modify the outer state and invoke save(),
// which converts de-facto changes into proper ops. ops change the
// inner state, the outer state gets regenerated, ops propagate
// to other replicas.
Model.prototype.save = function (inner) {
    var dirty = this.toPojo();
    var dirty_keys = this.keys();
    this.rebuild(this.getInnerState());
    var clean_keys = this.keys();
    var all_keys = dirty_keys.concat(clean_keys);
    var changes = {}, changed = false;
    // compare de-facto "dirty" values to the last version's values
    for(var i=0; i<all_keys.length; i++) {
        var key = all_keys[i];
        if (key.charAt(0)==='_') {continue;}
        if (Syncable.toPojo(this[key])!==dirty[key]) { // FIXME nesteds, deep compare
            // a change detected
            changes[key] = dirty[key]; // FIXME flatten
            changed = true;
        }
    }
    // finally, commit the changes
    changed && this.set(changes);
};

// A bit of syntactic sugar for Model listeners.
// Invoke model.onFieldChange('field', cb) to listen to that particular field.
Model.prototype.onFieldChange = function (field, callback, context) {
    /*if (filter.constructor===Function) {  ?
        context = callback;
        callback = filter;
        filter = null;
    }*/
    var filter = function (ev) {
        return field in ev.value;
    };
    Syncable.prototype.on.call(this, filter, callback, context);
};

Model.prototype.keys = function () {
    return Object.keys(this).filter(function(key){
        return Syncable.reFieldName.test(key);
    });
};

Model.prototype.toString = function () {
    return JSON.stringify(this, this.keys());
};

// The API exposes the outer state of a syncable. But, its
// inner state is its "true" state that also includes all
// the (CRDT) metadata. The outer state can always be generated
// from the inner state (see Syncable.rebuild()).
// This method is a constructor for the inner state.
// It either creates the default state or deserializes a .state
// op value.
function LWWObject (string) {
    // { stamp: {key:value} }
    this._version = null;
    var parsed = string ? JSON.parse(string) : {};
    var values = this.values = Object.create(null);
    // some paranoid checks are very much relevant here
    var stamps = Object.keys(parsed).filter(LamportTimestamp.is).sort();
    stamps.forEach(function (stamp){ // invert wire-format into mem-format
        var set = parsed[stamp];
        var keys=Object.keys(set).filter(LWWObject.is_field_name);
        keys.forEach(function(key) {
            values[key] = new StampedValue(set[key], stamp);
        });
    });
}
LWWObject.prototype = Object.create( Syncable.Inner.prototype );
LWWObject.prototype.constructor = LWWObject;
LWWObject.reFieldName = /^[a-z]\w*$/;
LWWObject.is_field_name = function (name) {
    return LWWObject.reFieldName.test(name);
};

function StampedValue (value, stamp) {
    this.value = value;
    this.stamp = stamp;
}

// This class implements just one kind of an op: set({key:value}).
// To implement your own ops you need to understand
// implications of partial order. Ops may be applied in slightly
// different orders at different replicas, but the result must
// converge. (see Model.playOpLog())
// An op is a method for an inner state object that consumes an
// op, changes inner state, no side effects allowed.
LWWObject.prototype.set = function (values, stamp) {
    var keys = Object.keys(values), self=this;
    keys = keys.filter(LWWObject.is_field_name);
    keys.forEach(function(key){
        var entry = self.values[key];
        if (entry===undefined || entry.stamp<stamp) {
            self.values[key] = new StampedValue(values[key], stamp);
        }
    });
};

LWWObject.prototype.write = function (op) {
    switch (op.op()) {
    case 'set': this.set(JSON.parse(op.value), op.stamp()); break;
    default:    throw new Error('operation unknown'); // FIXME
    }
};

// Produces the outer state from the inner state.
LWWObject.prototype.updateSyncable = function (syncable) {
    var values = this.values;
    Object.keys(values).forEach(function(k){
        var val = values[k].value;
        if (val.constructor===Object && val.ref) {
            val = syncable._owner.get(val.ref); // FIXME ugly
        }
        syncable[k] = val;
    });
    var missing_keys = Object.keys(syncable).
        filter(LWWObject.is_field_name).
        filter(function(key) { !(key in values); });
    missing_keys.forEach(function(key){
        delete syncable[key];
    });
};

// Serializes the inner state to a string. The constructor must
// be able to parse this later.
LWWObject.prototype.toString = function () {
    var wire = Object.create(null), values = this.values;
    var keys = Object.keys(this.values);
    keys.forEach(function(key){
        var entry = values[key], change = wire[entry.stamp];
        if (!change) {
            change = wire[entry.stamp] = Object.create(null);
        }
        change[key] = entry.value;
    });
    return JSON.stringify(wire);
};

Model.Inner = LWWObject;
Syncable.registerType('Model', Model);
module.exports = Model;
<|endoftext|>


<commit_before>const majorColors = {
	'content-background-color': '#FFFFFF',
	'primary-background-color': '#04436A',
	'primary-font-color': '#444444',
	'primary-action-color': '#13679A', // was action-buttons-color
	'secondary-background-color': '#F4F4F4',
	'secondary-font-color': '#A0A0A0',
	'secondary-action-color': '#DDDDDD',
	'component-color': '#EAEAEA',
	'success-color': '#4dff4d',
	'pending-color': '#FCB316',
	'error-color': '#BC2031',
	'selection-color': '#02ACEC',
	'attention-color': '#9C27B0'
};

// Minor colours implement major colours by default, but can be overruled
// const minorColors = {
// 	'tertiary-background-color': '@component-color',
// 	'tertiary-font-color': '@transparent-lightest',
// 	'link-font-color': '@primary-action-color',
// 	'info-font-color': '@secondary-font-color',
// 	'custom-scrollbar-color': '@transparent-darker',
// 	'status-online': '@success-color',
// 	'status-away': '@pending-color',
// 	'status-busy': '@error-color',
// 	'status-offline': '@transparent-darker'
// };

const newvariables = {
	'content-background-color': 'rc-color-primary-lightest',
	'primary-background-color': 'rc-color-primary',
	'success-color': 'rc-color-success',
	'pending-color': 'rc-color-alert',
	'error-color': 'rc-color-error',
	'status-online': 'rc-color-success',
	'status-away': 'rc-color-alert',
	'status-busy': 'rc-color-error',
	'status-offline': 'rc-color-primary-darkest'
};

function lightenDarkenColor(col, amt) {
	let usePound = false;
	if (col[0] === '#') {
		col = col.slice(1);
		usePound = true;
	}

	const num = parseInt(col, 16);
	let r = (num >> 16) + amt;

	if (r > 255) { r = 255; } else if (r < 0) { r = 0; }

	let b = ((num >> 8) & 0x00FF) + amt;

	if (b > 255) { b = 255; } else if (b < 0) { b = 0; }

	let g = (num & 0x0000FF) + amt;

	if (g > 255) { g = 255; } else if (g < 0) { g = 0; }

	return (usePound ? '#' : '') + (g | (b << 8) | (r << 16)).toString(16);
}

RocketChat.Migrations.add({
	version: 103,
	up() {
		Object.keys(majorColors).forEach(function(_id) {
			const color = RocketChat.models.Settings.findOne({_id: `theme-color-${ _id }`});
			const key = newvariables[_id];
			if (color && color.value !== majorColors[_id] && key) {
				if (/^@.+/.test(color.value)) {
					color.value = newvariables[color.value.replace('@', '')];
				}
				const id = `theme-color-${ key }`;
				RocketChat.models.Settings.update({_id: id}, {$set: { value : color.value, editor: /^#.+/.test(color.value) ? 'color' : 'expression' }});
				if (key === 'rc-color-primary') {
					RocketChat.models.Settings.update({_id: 'theme-color-rc-color-primary-darkest'}, {$set: {editor: 'color', value: lightenDarkenColor(color.value, -16)}});
					RocketChat.models.Settings.update({_id: 'theme-color-rc-color-primary-dark'}, {$set: {editor: 'color', value: lightenDarkenColor(color.value, 18)}});
					RocketChat.models.Settings.update({_id: 'theme-color-rc-color-primary-light'}, {$set: {editor: 'color', value: lightenDarkenColor(color.value, 110)}});
					RocketChat.models.Settings.update({_id: 'theme-color-rc-color-primary-light-medium'}, {$set: {editor: 'color', value: lightenDarkenColor(color.value, 156)}});
					RocketChat.models.Settings.update({_id: 'theme-color-rc-color-primary-lightest'}, {$set: {editor: 'color', value: lightenDarkenColor(color.value, 200)}});
				}
			}
		});
	}
});
<commit_msg>Force Travis<commit_after>const majorColors = {
	'content-background-color': '#FFFFFF',
	'primary-background-color': '#04436A',
	'primary-font-color': '#444444',
	'primary-action-color': '#13679A', // was action-buttons-color
	'secondary-background-color': '#F4F4F4',
	'secondary-font-color': '#A0A0A0',
	'secondary-action-color': '#DDDDDD',
	'component-color': '#EAEAEA',
	'success-color': '#4dff4d',
	'pending-color': '#FCB316',
	'error-color': '#BC2031',
	'selection-color': '#02ACEC',
	'attention-color': '#9C27B0'
};

// Minor colours implement major colours by default, but can be overruled
// const minorColors = {
// 	'tertiary-background-color': '@component-color',
// 	'tertiary-font-color': '@transparent-lightest',
// 	'link-font-color': '@primary-action-color',
// 	'info-font-color': '@secondary-font-color',
// 	'custom-scrollbar-color': '@transparent-darker',
// 	'status-online': '@success-color',
// 	'status-away': '@pending-color',
// 	'status-busy': '@error-color',
// 	'status-offline': '@transparent-darker'
// };

const newvariables = {
	'content-background-color': 'rc-color-primary-lightest',
	'primary-background-color': 'rc-color-primary',
	'success-color': 'rc-color-success',
	'pending-color': 'rc-color-alert',
	'error-color': 'rc-color-error',
	'status-online': 'rc-color-success',
	'status-away': 'rc-color-alert',
	'status-busy': 'rc-color-error',
	'status-offline': 'rc-color-primary-darkest'
};

function lightenDarkenColor(col, amt) {
	let usePound = false;
	if (col[0] === '#') {
		col = col.slice(1);
		usePound = true;
	}

	const num = parseInt(col, 16);
	let r = (num >> 16) + amt;

	if (r > 255) { r = 255; } else if (r < 0) { r = 0; }

	let b = ((num >> 8) & 0x00FF) + amt;

	if (b > 255) { b = 255; } else if (b < 0) { b = 0; }

	let g = (num & 0x0000FF) + amt;

	if (g > 255) { g = 255; } else if (g < 0) { g = 0; }

	return (usePound ? '#' : '') + (g | (b << 8) | (r << 16)).toString(16);
}

RocketChat.Migrations.add({
	version: 103,
	up() {
		Object.keys(majorColors).forEach(function(_id) {
			const color = RocketChat.models.Settings.findOne({_id: `theme-color-${ _id }`});
			const key = newvariables[_id];
			if (color && color.value !== majorColors[_id] && key) {
				if (/^@.+/.test(color.value)) {
					color.value = newvariables[color.value.replace('@', '')];
				}
				const id = `theme-color-${ key }`;
				RocketChat.models.Settings.update({_id: id}, {$set: { value : color.value, editor: /^#.+/.test(color.value) ? 'color' : 'expression' }});
				if (key === 'rc-color-primary') {
					RocketChat.models.Settings.update({_id: 'theme-color-rc-color-primary-darkest'}, {$set: {editor: 'color', value: lightenDarkenColor(color.value, -16)}});
					RocketChat.models.Settings.update({_id: 'theme-color-rc-color-primary-dark'}, {$set: {editor: 'color', value: lightenDarkenColor(color.value, 18)}});
					RocketChat.models.Settings.update({_id: 'theme-color-rc-color-primary-light'}, {$set: {editor: 'color', value: lightenDarkenColor(color.value, 110)}});
					RocketChat.models.Settings.update({_id: 'theme-color-rc-color-primary-light-medium'}, {$set: {editor: 'color', value: lightenDarkenColor(color.value, 156)}});
					RocketChat.models.Settings.update({_id: 'theme-color-rc-color-primary-lightest'}, {$set: {editor: 'color', value: lightenDarkenColor(color.value, 200)}});
				}
			}
		});
	}

});
<|endoftext|>


<commit_before>import Service, { inject as service } from '@ember/service'
import config from "../config/environment"

const { baseURL } = config.pbApi
const log = () => { } // console.log

const logger = topic => message => log(topic, message)

export default Service.extend({
  storage: service(),
  paperToaster: service(),
  pilasBloquesAnalytics: service(),
  platform: service(),
  loading: {},
  connected: true, //Necesita empezar en true para que el _send() haga el ping inicial.

  // SOLUTIONS
  openChallenge(challengeId) {
    this._send('POST', 'challenges', { challengeId }, false).catch(logger('openChallenge'))
  },

  lastSolution(challengeId) {
    if (!this.getUser()) return null
    return this._send('GET', `challenges/${challengeId}/solution`, undefined, false).catch(() => null)
  },

  runProgram(challengeId, metadata) {
    const solutionId = uuidv4()
    const data = {
      challengeId,
      solutionId,
      ...metadata
    }
    this._send('POST', 'solutions', data, false).catch(logger('runProgram'))

    return solutionId
  },

  executionFinished(solutionId, executionResult) {
    this._send('PUT', `solutions/${solutionId}`, { executionResult }, false).catch(logger('executionFinished'))
  },

  // LOGIN - REGISTER
  async login(credentials) {
    return this._send('POST', 'login', credentials)
      .then(user => this.storage.saveUser(user))
  },

  async register(data) {
    const { username, avatarURL } = data
    const profile = {
      nickName: username,
      avatarURL
    }
    return this._send('POST', 'register', { ...data, profile })
      .then(user => this.storage.saveUser(user))
  },

  async changePassword(newCredentials) {
    return this._send('PUT', 'credentials', newCredentials)
      .then(user => this.storage.saveUser(user))
  },

  async userExists(username) {
    return this._send('GET', `users/exists?username=${username}`)
  },

  async newAnswer(data) {
    return this._send('POST', `answers`, data)
      .then(user => this.storage.saveUser(user))
  },

  logout() {
    this.pilasBloquesAnalytics.logout()
    this.storage.saveUser(null)
  },

  getUser() { return this.storage.getUser() },

  async _send(method, resource, body, critical = true) {
    if (!this.connected) {
      if (critical) this._alertServerError()
      return;
    }
    const user = this.getUser()
    if (body) {
      body.context = this.pilasBloquesAnalytics.context()
      body.timestamp = new Date()
    } //TODO: Move user to Analytics / use id instead of nickname / rename Analytics to session related approach

    const url = `${baseURL}/${resource}`
    const flag = `loading.${resource.split('?')[0].replace('/', '-')}`
    const headers = {
      'Content-Type': 'application/json',
      'Authorization': user ? `Bearer ${user.token}` : null
    }

    this.set(flag, true)
    return fetch(url, {
      method,
      body: JSON.stringify(body),
      headers
    })
      .catch(connectionErr => {
        if (critical) this._alertServerError()
        // console.log({ connectionErr })
        throw connectionErr
      })
      .then(res => {
        if (res.status >= 400) { return res.text().then(message => { throw { status: res.status, message } }) }
        else { return res.json().catch(() => { /** if not body present */ }) }
      })
      .finally(() => this.set(flag, false))
  },

  _alertServerError() {
    this.paperToaster.show("Problemas con el servidor, intentar más tarde", {
      duration: 4000,
      position: "top"
    })
  },

  init() {
    this._super.apply(this, arguments)
    if (!this.platform.online()) { // Avoiding unnecessary requests when in website
      this._send('GET', 'ping', undefined, false)
        .then(() => { this.set('connected', true) })
        .catch(() => { this.set('connected', false) })
    }

  },
})
<commit_msg>Haciendo que siempre se actualice el connected<commit_after>import Service, { inject as service } from '@ember/service'
import config from "../config/environment"

const { baseURL } = config.pbApi
const log = () => { } // console.log

const logger = topic => message => log(topic, message)

export default Service.extend({
  storage: service(),
  paperToaster: service(),
  pilasBloquesAnalytics: service(),
  platform: service(),
  loading: {},
  connected: null,

  // SOLUTIONS
  openChallenge(challengeId) {
    this._send('POST', 'challenges', { challengeId }, false).catch(logger('openChallenge'))
  },

  lastSolution(challengeId) {
    if (!this.getUser()) return null
    return this._send('GET', `challenges/${challengeId}/solution`, undefined, false).catch(() => null)
  },

  runProgram(challengeId, metadata) {
    const solutionId = uuidv4()
    const data = {
      challengeId,
      solutionId,
      ...metadata
    }
    this._send('POST', 'solutions', data, false).catch(logger('runProgram'))

    return solutionId
  },

  executionFinished(solutionId, executionResult) {
    this._send('PUT', `solutions/${solutionId}`, { executionResult }, false).catch(logger('executionFinished'))
  },

  // LOGIN - REGISTER
  async login(credentials) {
    return this._send('POST', 'login', credentials)
      .then(user => this.storage.saveUser(user))
  },

  async register(data) {
    const { username, avatarURL } = data
    const profile = {
      nickName: username,
      avatarURL
    }
    return this._send('POST', 'register', { ...data, profile })
      .then(user => this.storage.saveUser(user))
  },

  async changePassword(newCredentials) {
    return this._send('PUT', 'credentials', newCredentials)
      .then(user => this.storage.saveUser(user))
  },

  async userExists(username) {
    return this._send('GET', `users/exists?username=${username}`)
  },

  async newAnswer(data) {
    return this._send('POST', `answers`, data)
      .then(user => this.storage.saveUser(user))
  },

  logout() {
    this.pilasBloquesAnalytics.logout()
    this.storage.saveUser(null)
  },

  getUser() { return this.storage.getUser() },

  async _send(method, resource, body, critical = true) {
    const user = this.getUser()
    if (body) {
      body.context = this.pilasBloquesAnalytics.context()
      body.timestamp = new Date()
    } //TODO: Move user to Analytics / use id instead of nickname / rename Analytics to session related approach

    const url = `${baseURL}/${resource}`
    const flag = `loading.${resource.split('?')[0].replace('/', '-')}`
    const headers = {
      'Content-Type': 'application/json',
      'Authorization': user ? `Bearer ${user.token}` : null
    }

    this.set(flag, true)
    return fetch(url, {
      method,
      body: JSON.stringify(body),
      headers
    })
      .catch(connectionErr => {
        if (critical) this._alertServerError()
        this.set('connected', false)
        // console.log({ connectionErr })
        throw connectionErr
      })
      .then(res => {
        this.set('connected', true)
        if (res.status >= 400) { return res.text().then(message => { throw { status: res.status, message } }) }
        else { return res.json().catch(() => { /** if not body present */ }) }
      })
      .finally(() => this.set(flag, false))
  },

  _alertServerError() {
    this.paperToaster.show("Problemas con el servidor, intentar más tarde", {
      duration: 4000,
      position: "top"
    })
  },

  init() {
    this._super.apply(this, arguments)
    if (this.platform.inElectron()) { // Avoiding unnecessary requests when in website
      this._send('GET', 'ping', undefined, false) // forces setting of "connected"
    } else {
      this.set('connected', true)
    }
  },
})
<|endoftext|>


<commit_before>'use strict';
const { datastore } = require('../shared');
const readline = require('readline');
const fs = require('fs');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});
/*
 * change metaanalysis from an old format to the new one on load from datastore, if need be
 */
async function migrateMetaanalysis(metaanalysis) {
  // 2017-02-23: move columnOrder to columns
  //     when all is migrated: just remove this code
  if (metaanalysis.columnOrder) {
    metaanalysis.columns = metaanalysis.columnOrder;
    delete metaanalysis.columnOrder;
    metaanalysis.migrated = true;
  }

  // 2017-05-02: move graph aggregates to graphs
  //     when all is migrated: just remove this code
  // migrate aggregate graphs to graphs
  if (metaanalysis.aggregates) {
    const oldGraphs = ['forestPlotNumberAggr', 'forestPlotPercentAggr',
      'grapeChartNumberAggr', 'grapeChartPercentAggr'];
    // going backwards so we can safely delete array elements
    for (let i = metaanalysis.aggregates.length - 1; i >= 0; i -= 1) {
      const formulaName = metaanalysis.aggregates[i].formula.split('(')[0];
      if (oldGraphs.indexOf(formulaName) !== -1) {
        const graph = metaanalysis.aggregates[i];
        metaanalysis.aggregates.splice(i, 1);
        graph.formula = graph.formula.replace('Aggr', 'Graph');
        if (!metaanalysis.graphs) {
          metaanalysis.graphs = [];
        }
        metaanalysis.graphs.unshift(graph);
        metaanalysis.migrated = true;
      }
    }
  }

  // 2017-06-28: migrate global columns to private columns
  //     when all is migrated:
  //       remove this code,
  //       update tests to check migration no longer does this
  //       remove all remaining mentions of global columns
  //       remove columns from datastore
  // prepare the papers this metaanalysis depends on
  const maPapers = await Promise.all(metaanalysis.paperOrder.map(async (paperId) => {
    // find the paper with the matching ID
    const query = datastore.createQuery('Paper').filter('id', '=', paperId);
    const [retval] = await datastore.runQuery(query);
    if (retval.length === 0) {
      throw new Error(`metaanalysis ${metaanalysis.title} has a paper ${paperId} that isn't in the datastore`);
    }
    return retval[0];
  }));

  // convert columns from string to object
  if (!metaanalysis.columns) metaanalysis.columns = [];
  let maxId = 0;
  metaanalysis.columns.forEach(async (col, colIndex) => {
    if (typeof col === 'string') {
      // migrate the string into an object
      const query = datastore.createQuery('Column').filter('id', '=', col);
      const [retval] = await datastore.runQuery(query);
      if (retval.length === 0) throw new Error(`metaanalysis ${metaanalysis.title} uses nonexistent column ${col}`);
      const column = retval[0];
      const colObject = {
        id: '' + (maxId += 1),
        title: column.title,
        description: column.description,
        type: column.type,
      };

      // add sourceColumnMap { paperId: columnId }
      colObject.sourceColumnMap = {};

      maPapers.forEach((paper) => {
        // go through paper's columns, find the one whose obsolete id matches col, use its ID in this map
        const paperCol = paper.columns.find((paperColObject) => paperColObject.obsoleteIDForMigration === col);
        if (paperCol) colObject.sourceColumnMap[paper.id] = paperCol.id;
        // if the paper doesn't have such a column, just don't have a mapping;
        // the column in the paper, and the mapping here, will get added when
        // the metaanalysis is being edited and the user puts in a datum in this column
      });
      metaanalysis.columns[colIndex] = colObject;
      // migrate hidden columns so it uses the right column ID
      if (metaanalysis.hiddenCols) {
        const colPos = metaanalysis.hiddenCols.indexOf(col);
        if (colPos !== -1) {
          metaanalysis.hiddenCols[colPos] = colObject.id;
        }
      }
      // convert grouping column to new ID
      if (metaanalysis.groupingColumn === col) metaanalysis.groupingColumn = colObject.id;
      // migrate every parameter in computed anything into the right ID
      for (const fieldName of ['columns', 'aggregates', 'groupingAggregates', 'graphs']) {
        if (metaanalysis[fieldName]) {
          metaanalysis[fieldName].forEach((computed) => {
            if (!computed.formula) return; // not computed
            // replace all occurrences of col in the formula with colObject.id
            computed.formula = computed.formula.split(col).join(colObject.id);
          });
        }
      }
      metaanalysis.migrated = true;
    }
  });
  // check that every computed thing's formula doesn't contain '/id/col/',
  // remove offending ones because they don't have data in the metaanalysis anyway so no loss
  // also migrate customName to title and add type: result
  for (const fieldName of ['columns', 'aggregates', 'groupingAggregates', 'graphs']) {
    if (metaanalysis[fieldName]) {
      let computedIndex = metaanalysis[fieldName].length - 1;
      while (computedIndex >= 0) {
        const computed = metaanalysis[fieldName][computedIndex];
        if (computed.formula) {
          if (computed.formula.indexOf('/id/col/') !== -1) {
            metaanalysis[fieldName].splice(computedIndex, 1);
            metaanalysis.migrated = true;
          }
          if (computed.customName) {
            computed.title = computed.customName;
            delete computed.customName;
            metaanalysis.migrated = true;
          }
          if (!computed.type && fieldName === 'columns') {
            computed.type = 'result';
            metaanalysis.migrated = true;
          }
        }
        computedIndex -= 1;
      }
    }
  }
  // if we have a hiddenColumn that's not migrated, drop it
  if (metaanalysis.hiddenCols) {
    let hiddenIndex = metaanalysis.hiddenCols.length - 1;
    while (hiddenIndex >= 0) {
      if (metaanalysis.hiddenCols[hiddenIndex].startsWith('/id/col/')) {
        metaanalysis.hiddenCols.splice(hiddenIndex, 1);
        metaanalysis.migrated = true;
      }
      hiddenIndex -= 1;
    }
  }

  return metaanalysis;
}

/*
 * change paper from an old format to the new one on load from datastore, if need be
 */
function migratePaper(paper, columns) {
  // 2017-02-23: move columnOrder to columns
  //     when all is migrated: just remove this code
  if (paper.columnOrder) {
    paper.columns = paper.columnOrder;
    delete paper.columnOrder;
    paper.migrated = true;
  }

  // 2017-06-27: migrate global columns to private columns
  //     when all is migrated:
  //       remove this code,
  //       remove obsoleteIDForMigration from api.js
  //       update tests to check migration no longer does this
  //       remove all remaining mentions of global columns
  //       remove columns from datastore

  // if we have a datum whose column ID isn't in paper.columns then add it there
  if (!paper.columns) paper.columns = [];
  if (paper.experiments) {
    paper.experiments.forEach((exp) => {
      if (exp.data) {
        Object.keys(exp.data).forEach((colId) => {
          if (colId.startsWith('/id/col/') && paper.columns.indexOf(colId) === -1) {
            paper.columns.push(colId);
            paper.migrated = true;
          }
        });
      }
    });
  }

  let maxId = 0;
  paper.columns.forEach((col, colIndex) => {
    if (typeof col === 'string') {
      // migrate the string into an object
      if (!columns[col]) throw new Error(`paper ${paper.title} uses nonexistent column ${col}`);
      const colObject = {
        id: '' + (maxId += 1),
        title: columns[col].title,
        description: columns[col].description,
        type: columns[col].type,
        obsoleteIDForMigration: col,
      };
      paper.columns[colIndex] = colObject;
      // migrate experiment data so it uses the column ID
      if (paper.experiments) {
        paper.experiments.forEach((exp) => {
          if (exp.data && col in exp.data) {
            exp.data[colObject.id] = exp.data[col];
            delete exp.data[col];
          }
        });
      }
      // migrate hidden columns so it uses the right column ID
      if (paper.hiddenCols) {
        const colPos = paper.hiddenCols.indexOf(col);
        if (colPos !== -1) {
          paper.hiddenCols[colPos] = colObject.id;
        }
      }
      // migrate every parameter in computed anything into the right ID
      paper.columns.forEach((computed) => {
        if (!computed.formula) return; // not computed
        // replace all occurrences of col in the formula with colObject.id
        computed.formula = computed.formula.split(col).join(colObject.id);
      });
      paper.migrated = true;
    }
  });
  // check that every computed column's formula doesn't contain '/id/col/',
  // remove offending ones because they don't have data in the paper anyway so no loss
  // also migrate customName to title and add type: result
  let computedIndex = paper.columns.length - 1;
  while (computedIndex >= 0) {
    const computed = paper.columns[computedIndex];
    if (computed.formula) {
      if (computed.formula.indexOf('/id/col/') !== -1) {
        paper.columns.splice(computedIndex, 1);
        paper.migrated = true;
      }
      if (computed.customName || !computed.type) {
        computed.title = computed.customName;
        delete computed.customName;
        computed.type = 'result';
        paper.migrated = true;
      }
    }
    computedIndex -= 1;
  }
  // if we have a hiddenColumn that's not migrated, drop it
  if (paper.hiddenCols) {
    let hiddenIndex = paper.hiddenCols.length - 1;
    while (hiddenIndex >= 0) {
      if (paper.hiddenCols[hiddenIndex].startsWith('/id/col/')) {
        paper.hiddenCols.splice(hiddenIndex, 1);
        paper.migrated = true;
      }
      hiddenIndex -= 1;
    }
  }

  return paper;
}

/*
 * change user from an old format to the new one on load from datastore, if need be
 */
function migrateUser(user) {
  // 2017-06-08: Only store limited user information
  //     when all is migrated: just remove this code
  if (user.emails) {
    user.email = user.emails[0].value;
    delete user.emails;
    delete user.CHECKid;
    delete user.id;
    delete user.name;
    delete user.provider;
    user.migrated = true;
  }
  return user;
}

async function getAllColumns() {
  try {
    console.log('getAllColumns: making a datastore request');
    const retval = {};
    const [results] = await datastore.createQuery('Column').run();

    results.forEach((result) => {
      try {
        retval[result.id] = result;
      } catch (error) {
        console.error('error in a column entity (ignoring)', error);
      }
    });

    console.log(`getAllColumns: ${Object.keys(retval).length} done`);
    return retval;
  } catch (error) {
    console.error('error retrieving columns', error);
    setTimeout(getAllColumns, 60 * 1000); // try loading again in a minute
    throw error;
  }
}

async function migrateAllUsers() {
  const kind = 'User';
  const [retval] = await datastore.createQuery(kind).run();
  const entities = [];
  retval.forEach(user => {
    const val = migrateUser(user);
    const userKey = datastore.key([kind, val.email]);
    entities.push({
      key: userKey,
      data: val,
    });
  });
  await datastore.upsert(entities);

}

async function migrateAllPapers() {
  const columns = await getAllColumns();
  const kind = 'Paper';
  const [retval] = await datastore.createQuery(kind).run();
  const entities = [];
  retval.forEach(element => {
    const val = migratePaper(element, columns);
    const paperKey = datastore.key([kind, element.id]);
    entities.push({
      key: paperKey,
      data: val,
    });
  });
  await datastore.upsert(entities);
}

async function migrateAllMetaanalysis() {
  const kind = 'Metaanalysis';
  const [retval] = await datastore.createQuery(kind).run();
  const entities = [];
  for (const element of retval) {
    try {
      const val = await migrateMetaanalysis(element);
      const metaanalysisKey = datastore.key([kind, val.id]);
      entities.push({
        key: metaanalysisKey,
        data: val,
      });
    } catch (error) {
      console.log(error);
    }
  }
  await datastore.upsert(entities);
}

async function migrate() {
  await migrateAllUsers();
  await migrateAllPapers();
  await migrateAllMetaanalysis();
  console.log('Migration complete');
}

migrate();
<commit_msg>:shirt:<commit_after>'use strict';
const { datastore } = require('../shared');

/*
 * change metaanalysis from an old format to the new one on load from datastore, if need be
 */
async function migrateMetaanalysis(metaanalysis) {
  // 2017-02-23: move columnOrder to columns
  //     when all is migrated: just remove this code
  if (metaanalysis.columnOrder) {
    metaanalysis.columns = metaanalysis.columnOrder;
    delete metaanalysis.columnOrder;
    metaanalysis.migrated = true;
  }

  // 2017-05-02: move graph aggregates to graphs
  //     when all is migrated: just remove this code
  // migrate aggregate graphs to graphs
  if (metaanalysis.aggregates) {
    const oldGraphs = ['forestPlotNumberAggr', 'forestPlotPercentAggr',
      'grapeChartNumberAggr', 'grapeChartPercentAggr'];
    // going backwards so we can safely delete array elements
    for (let i = metaanalysis.aggregates.length - 1; i >= 0; i -= 1) {
      const formulaName = metaanalysis.aggregates[i].formula.split('(')[0];
      if (oldGraphs.indexOf(formulaName) !== -1) {
        const graph = metaanalysis.aggregates[i];
        metaanalysis.aggregates.splice(i, 1);
        graph.formula = graph.formula.replace('Aggr', 'Graph');
        if (!metaanalysis.graphs) {
          metaanalysis.graphs = [];
        }
        metaanalysis.graphs.unshift(graph);
        metaanalysis.migrated = true;
      }
    }
  }

  // 2017-06-28: migrate global columns to private columns
  //     when all is migrated:
  //       remove this code,
  //       update tests to check migration no longer does this
  //       remove all remaining mentions of global columns
  //       remove columns from datastore
  // prepare the papers this metaanalysis depends on
  const maPapers = await Promise.all(metaanalysis.paperOrder.map(async (paperId) => {
    // find the paper with the matching ID
    const query = datastore.createQuery('Paper').filter('id', '=', paperId);
    const [retval] = await datastore.runQuery(query);
    if (retval.length === 0) {
      throw new Error(`metaanalysis ${metaanalysis.title} has a paper ${paperId} that isn't in the datastore`);
    }
    return retval[0];
  }));

  // convert columns from string to object
  if (!metaanalysis.columns) metaanalysis.columns = [];
  let maxId = 0;
  metaanalysis.columns.forEach(async (col, colIndex) => {
    if (typeof col === 'string') {
      // migrate the string into an object
      const query = datastore.createQuery('Column').filter('id', '=', col);
      const [retval] = await datastore.runQuery(query);
      if (retval.length === 0) throw new Error(`metaanalysis ${metaanalysis.title} uses nonexistent column ${col}`);
      const column = retval[0];
      const colObject = {
        id: '' + (maxId += 1),
        title: column.title,
        description: column.description,
        type: column.type,
      };

      // add sourceColumnMap { paperId: columnId }
      colObject.sourceColumnMap = {};

      maPapers.forEach((paper) => {
        // go through paper's columns, find the one whose obsolete id matches col, use its ID in this map
        const paperCol = paper.columns.find((paperColObject) => paperColObject.obsoleteIDForMigration === col);
        if (paperCol) colObject.sourceColumnMap[paper.id] = paperCol.id;
        // if the paper doesn't have such a column, just don't have a mapping;
        // the column in the paper, and the mapping here, will get added when
        // the metaanalysis is being edited and the user puts in a datum in this column
      });
      metaanalysis.columns[colIndex] = colObject;
      // migrate hidden columns so it uses the right column ID
      if (metaanalysis.hiddenCols) {
        const colPos = metaanalysis.hiddenCols.indexOf(col);
        if (colPos !== -1) {
          metaanalysis.hiddenCols[colPos] = colObject.id;
        }
      }
      // convert grouping column to new ID
      if (metaanalysis.groupingColumn === col) metaanalysis.groupingColumn = colObject.id;
      // migrate every parameter in computed anything into the right ID
      for (const fieldName of ['columns', 'aggregates', 'groupingAggregates', 'graphs']) {
        if (metaanalysis[fieldName]) {
          metaanalysis[fieldName].forEach((computed) => {
            if (!computed.formula) return; // not computed
            // replace all occurrences of col in the formula with colObject.id
            computed.formula = computed.formula.split(col).join(colObject.id);
          });
        }
      }
      metaanalysis.migrated = true;
    }
  });
  // check that every computed thing's formula doesn't contain '/id/col/',
  // remove offending ones because they don't have data in the metaanalysis anyway so no loss
  // also migrate customName to title and add type: result
  for (const fieldName of ['columns', 'aggregates', 'groupingAggregates', 'graphs']) {
    if (metaanalysis[fieldName]) {
      let computedIndex = metaanalysis[fieldName].length - 1;
      while (computedIndex >= 0) {
        const computed = metaanalysis[fieldName][computedIndex];
        if (computed.formula) {
          if (computed.formula.indexOf('/id/col/') !== -1) {
            metaanalysis[fieldName].splice(computedIndex, 1);
            metaanalysis.migrated = true;
          }
          if (computed.customName) {
            computed.title = computed.customName;
            delete computed.customName;
            metaanalysis.migrated = true;
          }
          if (!computed.type && fieldName === 'columns') {
            computed.type = 'result';
            metaanalysis.migrated = true;
          }
        }
        computedIndex -= 1;
      }
    }
  }
  // if we have a hiddenColumn that's not migrated, drop it
  if (metaanalysis.hiddenCols) {
    let hiddenIndex = metaanalysis.hiddenCols.length - 1;
    while (hiddenIndex >= 0) {
      if (metaanalysis.hiddenCols[hiddenIndex].startsWith('/id/col/')) {
        metaanalysis.hiddenCols.splice(hiddenIndex, 1);
        metaanalysis.migrated = true;
      }
      hiddenIndex -= 1;
    }
  }

  return metaanalysis;
}

/*
 * change paper from an old format to the new one on load from datastore, if need be
 */
function migratePaper(paper, columns) {
  // 2017-02-23: move columnOrder to columns
  //     when all is migrated: just remove this code
  if (paper.columnOrder) {
    paper.columns = paper.columnOrder;
    delete paper.columnOrder;
    paper.migrated = true;
  }

  // 2017-06-27: migrate global columns to private columns
  //     when all is migrated:
  //       remove this code,
  //       remove obsoleteIDForMigration from api.js
  //       update tests to check migration no longer does this
  //       remove all remaining mentions of global columns
  //       remove columns from datastore

  // if we have a datum whose column ID isn't in paper.columns then add it there
  if (!paper.columns) paper.columns = [];
  if (paper.experiments) {
    paper.experiments.forEach((exp) => {
      if (exp.data) {
        Object.keys(exp.data).forEach((colId) => {
          if (colId.startsWith('/id/col/') && paper.columns.indexOf(colId) === -1) {
            paper.columns.push(colId);
            paper.migrated = true;
          }
        });
      }
    });
  }

  let maxId = 0;
  paper.columns.forEach((col, colIndex) => {
    if (typeof col === 'string') {
      // migrate the string into an object
      if (!columns[col]) throw new Error(`paper ${paper.title} uses nonexistent column ${col}`);
      const colObject = {
        id: '' + (maxId += 1),
        title: columns[col].title,
        description: columns[col].description,
        type: columns[col].type,
        obsoleteIDForMigration: col,
      };
      paper.columns[colIndex] = colObject;
      // migrate experiment data so it uses the column ID
      if (paper.experiments) {
        paper.experiments.forEach((exp) => {
          if (exp.data && col in exp.data) {
            exp.data[colObject.id] = exp.data[col];
            delete exp.data[col];
          }
        });
      }
      // migrate hidden columns so it uses the right column ID
      if (paper.hiddenCols) {
        const colPos = paper.hiddenCols.indexOf(col);
        if (colPos !== -1) {
          paper.hiddenCols[colPos] = colObject.id;
        }
      }
      // migrate every parameter in computed anything into the right ID
      paper.columns.forEach((computed) => {
        if (!computed.formula) return; // not computed
        // replace all occurrences of col in the formula with colObject.id
        computed.formula = computed.formula.split(col).join(colObject.id);
      });
      paper.migrated = true;
    }
  });
  // check that every computed column's formula doesn't contain '/id/col/',
  // remove offending ones because they don't have data in the paper anyway so no loss
  // also migrate customName to title and add type: result
  let computedIndex = paper.columns.length - 1;
  while (computedIndex >= 0) {
    const computed = paper.columns[computedIndex];
    if (computed.formula) {
      if (computed.formula.indexOf('/id/col/') !== -1) {
        paper.columns.splice(computedIndex, 1);
        paper.migrated = true;
      }
      if (computed.customName || !computed.type) {
        computed.title = computed.customName;
        delete computed.customName;
        computed.type = 'result';
        paper.migrated = true;
      }
    }
    computedIndex -= 1;
  }
  // if we have a hiddenColumn that's not migrated, drop it
  if (paper.hiddenCols) {
    let hiddenIndex = paper.hiddenCols.length - 1;
    while (hiddenIndex >= 0) {
      if (paper.hiddenCols[hiddenIndex].startsWith('/id/col/')) {
        paper.hiddenCols.splice(hiddenIndex, 1);
        paper.migrated = true;
      }
      hiddenIndex -= 1;
    }
  }

  return paper;
}

/*
 * change user from an old format to the new one on load from datastore, if need be
 */
function migrateUser(user) {
  // 2017-06-08: Only store limited user information
  //     when all is migrated: just remove this code
  if (user.emails) {
    user.email = user.emails[0].value;
    delete user.emails;
    delete user.CHECKid;
    delete user.id;
    delete user.name;
    delete user.provider;
    user.migrated = true;
  }
  return user;
}

async function getAllColumns() {
  try {
    console.log('getAllColumns: making a datastore request');
    const retval = {};
    const [results] = await datastore.createQuery('Column').run();

    results.forEach((result) => {
      try {
        retval[result.id] = result;
      } catch (error) {
        console.error('error in a column entity (ignoring)', error);
      }
    });

    console.log(`getAllColumns: ${Object.keys(retval).length} done`);
    return retval;
  } catch (error) {
    console.error('error retrieving columns', error);
    setTimeout(getAllColumns, 60 * 1000); // try loading again in a minute
    throw error;
  }
}

async function migrateAllUsers() {
  const kind = 'User';
  const [retval] = await datastore.createQuery(kind).run();
  const entities = [];
  retval.forEach(user => {
    const val = migrateUser(user);
    const userKey = datastore.key([kind, val.email]);
    entities.push({
      key: userKey,
      data: val,
    });
  });
  await datastore.upsert(entities);
}

async function migrateAllPapers() {
  const columns = await getAllColumns();
  const kind = 'Paper';
  const [retval] = await datastore.createQuery(kind).run();
  const entities = [];
  retval.forEach(element => {
    const val = migratePaper(element, columns);
    const paperKey = datastore.key([kind, element.id]);
    entities.push({
      key: paperKey,
      data: val,
    });
  });
  await datastore.upsert(entities);
}

async function migrateAllMetaanalysis() {
  const kind = 'Metaanalysis';
  const [retval] = await datastore.createQuery(kind).run();
  const entities = [];
  for (const element of retval) {
    try {
      const val = await migrateMetaanalysis(element);
      const metaanalysisKey = datastore.key([kind, val.id]);
      entities.push({
        key: metaanalysisKey,
        data: val,
      });
    } catch (error) {
      console.log(error);
    }
  }
  await datastore.upsert(entities);
}

async function migrate() {
  await migrateAllUsers();
  await migrateAllPapers();
  await migrateAllMetaanalysis();
  console.log('Migration complete');
}

migrate();
<|endoftext|>


<commit_before>import { NOTIFS, ITEM } from '../consts/types';
import rantscript from '../consts/rantscript';
import { openModal } from './modal';
import { fetchUser } from './user';

const { ipcRenderer } = require('electron');
const currentWindow = require('electron').remote.getCurrentWindow();

// This is used to prevent notif being fetched when a notif clearing is in progress
let clearingNotifs = false;
let fetching = false;

/**
 * Fetches notifications
 *
 */
const fetchNotifs = () => (dispatch, getState) => {
  if (clearingNotifs || fetching) {
    return;
  }
  const auth = getState().auth;
  if (!auth.user) {
    return;
  }
  const prevNotifs = getState().notifs;
  const lastCheckTime = 1;
  fetching = true;
  rantscript
  .notifications(auth.user.authToken, lastCheckTime)
  .then((res) => {
    fetching = false;
    let noChange = true;

    // if (prevNotifs.num_unread === res.data.num_unread) {
    const nextnotifItems = res.data.items;
    let j = 0;
    while (j < nextnotifItems.length) {
      const prevItem = prevNotifs.items[j];
      const nextItem = nextnotifItems[j];
      if (
        prevItem.rant_id !== nextItem.rant_id
        || prevItem.read !== nextItem.read
      ) {
        noChange = false;
        break;
      }
      j += 1;
    }
    // } else {
    //   noChange = false;
    // }

    if (noChange) {
      console.log('returning');
      return;
    }
    const notifs = {
      items: res.data.items,
      check_time: res.data.check_time,
      username_map: res.data.username_map,
      num_unread: res.data.num_unread,
    };
    dispatch({
      type: NOTIFS.FETCH,
      notifs,
    });
  })
  .catch(() => {
    fetching = false;
  });
};

/**
 * Marks all the notif as read
 *
 */
const clearNotifs = () => (dispatch, getState) => {
  dispatch({
    type: NOTIFS.CLEARALL,
  });
  const auth = getState().auth;
  if (!auth.user) {
    return;
  }
  clearingNotifs = true;
  rantscript
  .clearNotifications(auth.user.authToken)
  .then(() => {
    clearingNotifs = false;
  })
  .catch(() => {
    clearingNotifs = false;
  });
};

/**
 * This shows a OS notification using HTML5 Notification API
 * This also shows a quick reply window if enabled in the settings
 *
 * @param {object} notif
 */
const showNotifs = notif => (dispatch, getState) => {
  const notifSettings = getState().settings.general.notifications.options;

  /**
   * This is done to update the user score (and the profile)
   * Okay, so this updates the profile but the main purpose is to
   * update the score
   */
  dispatch(fetchUser());

  if (!notifSettings.notif_enabled.value) {
    return;
  }
  if (notifSettings[notif.content.type].value === true) {
    // Show quick reply notif instead
    if (
      (notif.content.type === 'comment_content'
      || notif.content.type === 'comment_mention')
      && notifSettings.quick_reply_enabled.value
    ) {
      ipcRenderer.send('showQRNotif', notif);
      return;
    }
    const commentID = notif.content.comment_id || 0;

    const myNotification = new Notification('devRantron', {
      body: notif.body,
      data: { id: notif.id, commentID },
      icon: 'http://i.imgur.com/iikd00P.png',
      silent: !notifSettings.notif_sound_enabled.value,
    });

    // Open a modal when a user clicks on the notif
    myNotification.onclick = (e) => {
      dispatch(openModal(ITEM.RANT.NAME, e.target.data.id, { commentID: e.target.data.commentID }));
      if (!currentWindow.isVisible()) {
        currentWindow.show();
      }
      currentWindow.focus();
    };
  }
};

export { fetchNotifs, clearNotifs, showNotifs };
<commit_msg>Fixed update breaking notif<commit_after>import { NOTIFS, ITEM } from '../consts/types';
import rantscript from '../consts/rantscript';
import { openModal } from './modal';
import { fetchUser } from './user';

const { ipcRenderer } = require('electron');
const currentWindow = require('electron').remote.getCurrentWindow();

// This is used to prevent notif being fetched when a notif clearing is in progress
let clearingNotifs = false;
let fetching = false;

/**
 * Fetches notifications
 *
 */
const fetchNotifs = () => (dispatch, getState) => {
  if (clearingNotifs || fetching) {
    return;
  }
  const auth = getState().auth;
  if (!auth.user) {
    return;
  }
  const prevNotifs = getState().notifs;
  const lastCheckTime = 1;
  fetching = true;
  rantscript
  .notifications(auth.user.authToken, lastCheckTime)
  .then((res) => {
    fetching = false;
    let noChange = true;

    if (prevNotifs.num_unread === res.data.num_unread) {
      const nextnotifItems = res.data.items;
      let j = 0;
      while (j < nextnotifItems.length) {
        const prevItem = prevNotifs.items[j];
        const nextItem = nextnotifItems[j];
        if (
        prevItem.rant_id !== nextItem.rant_id
        || prevItem.read !== nextItem.read
      ) {
          noChange = false;
          break;
        }
        j += 1;
      }
    } else {
      noChange = false;
    }

    if (noChange) {
      return;
    }
    const notifs = {
      items: res.data.items,
      check_time: res.data.check_time,
      username_map: res.data.username_map,
      num_unread: res.data.num_unread,
    };
    dispatch({
      type: NOTIFS.FETCH,
      notifs,
    });
  })
  .catch(() => {
    fetching = false;
  });
};

/**
 * Marks all the notif as read
 *
 */
const clearNotifs = () => (dispatch, getState) => {
  dispatch({
    type: NOTIFS.CLEARALL,
  });
  const auth = getState().auth;
  if (!auth.user) {
    return;
  }
  clearingNotifs = true;
  rantscript
  .clearNotifications(auth.user.authToken)
  .then(() => {
    clearingNotifs = false;
  })
  .catch(() => {
    clearingNotifs = false;
  });
};

/**
 * This shows a OS notification using HTML5 Notification API
 * This also shows a quick reply window if enabled in the settings
 *
 * @param {object} notif
 */
const showNotifs = notif => (dispatch, getState) => {
  const notifSettings = getState().settings.general.notifications.options;

  /**
   * This is done to update the user score (and the profile)
   * Okay, so this updates the profile but the main purpose is to
   * update the score
   */
  dispatch(fetchUser());

  if (!notifSettings.notif_enabled.value) {
    return;
  }
  if (notifSettings[notif.content.type].value === true) {
    // Show quick reply notif instead
    if (
      (notif.content.type === 'comment_content'
      || notif.content.type === 'comment_mention')
      && notifSettings.quick_reply_enabled.value
    ) {
      ipcRenderer.send('showQRNotif', notif);
      return;
    }
    const commentID = notif.content.comment_id || 0;

    const myNotification = new Notification('devRantron', {
      body: notif.body,
      data: { id: notif.id, commentID },
      icon: 'http://i.imgur.com/iikd00P.png',
      silent: !notifSettings.notif_sound_enabled.value,
    });

    // Open a modal when a user clicks on the notif
    myNotification.onclick = (e) => {
      dispatch(openModal(ITEM.RANT.NAME, e.target.data.id, { commentID: e.target.data.commentID }));
      if (!currentWindow.isVisible()) {
        currentWindow.show();
      }
      currentWindow.focus();
    };
  }
};

export { fetchNotifs, clearNotifs, showNotifs };
<|endoftext|>


<commit_before>window.onload = function() {

  // Remove default 'no javascript' message
  document.getElementById('map').innerText = '';

  // Check that cooridinate data is present
  if (indexData &&
      (
        (indexData.geometry && indexData.geometry.coordinates && indexData.geometry.coordinates.length > 0) ||
        (indexData.features && indexData.features[0].geometry && indexData.features[0].geometry.coordinates && indexData.features[0].geometry.coordinates.length > 0)
      )
    ) {

    // Define coordinate system using PROJ4 standards
    var bng = new L.Proj.CRS('EPSG:27700',
      '+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000' +
      ' +ellps=airy +datum=OSGB36 +units=m +no_defs',
      {
        resolutions: [2500, 1000, 500, 200, 100, 50, 25, 10, 5, 2.5, 1],
        bounds: L.bounds([1300000,0],[700000,0])
      }
    );

    proj4.defs("urn:ogc:def:crs:EPSG:27700", "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +datum=OSGB36 +units=m +no_defs");

    // set up control and options
    options = {
      continuousWorld: true,
      worldCopyJump: false,
      minZoom: 15,
      maxZoom: 19,

      // controls
      dragging: false,
      touchZoom: false,
      doubleClickZoom: false,
      scrollWheelZoom: false,
      boxZoom: false,
      keyboard: false,
      tap: false,
      zoomControl: true,
      attributionControl: false
    };

    // set up the map
    var map = new L.Map('map', options);

    // create the tile layer with correct attribution
    var osmUrl='http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
    var osm = new L.TileLayer(osmUrl);
    map.addLayer(osm);

    //Add a scale control to the map
    L.control.scale().addTo(map);

    //Index stlye
    var indexStyle = {
      fillcolor: 'blue',
      fillOpacity: 0.5,
      opacity: 0
    };

    //Create the index layer
    var indexGeoJson = L.Proj.geoJson(indexData, {
      style: indexStyle
    });

    indexGeoJson.addTo(map);

    //Center map view on geojson polygon
    var bounds = indexGeoJson.getBounds();

    map.fitBounds(bounds, {maxZoom: 18, animate: false});
  } else {
    document.getElementById('map').innerText = 'No map information available';
  }
};
<commit_msg>Change projection definition to match openstreetmap documentation<commit_after>window.onload = function() {

  // Remove default 'no javascript' message
  document.getElementById('map').innerText = '';

  // Check that cooridinate data is present
  if (indexData &&
      (
        (indexData.geometry && indexData.geometry.coordinates && indexData.geometry.coordinates.length > 0) ||
        (indexData.features && indexData.features[0].geometry && indexData.features[0].geometry.coordinates && indexData.features[0].geometry.coordinates.length > 0)
      )
  ) {

    // Define coordinate system using PROJ4 standards
    projection_name = 'EPSG:27700'
    // see https://wiki.openstreetmap.org/wiki/Converting_to_WGS84#Great_Britain
    projection_def = '+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=375,-111,431,0,0,0,0 +units=m +no_defs'

    new L.Proj.CRS(projection_name, projection_def,
      {
        resolutions: [2500, 1000, 500, 200, 100, 50, 25, 10, 5, 2.5, 1],
        bounds: L.bounds([1300000,0],[700000,0])
      }
    );

    // set up control and options
    options = {
      continuousWorld: true,
      worldCopyJump: false,
      minZoom: 15,
      maxZoom: 19,

      // controls
      dragging: false,
      touchZoom: false,
      doubleClickZoom: false,
      scrollWheelZoom: false,
      boxZoom: false,
      keyboard: false,
      tap: false,
      zoomControl: true,
      attributionControl: false
    };

    // set up the map
    var map = new L.Map('map', options);

    // create the tile layer with correct attribution
    var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
    var osm = new L.TileLayer(osmUrl);
    map.addLayer(osm);

    //Add a scale control to the map
    L.control.scale().addTo(map);

    //Index stlye
    var indexStyle = {
      fillcolor: 'blue',
      fillOpacity: 0.5,
      opacity: 0
    };

    //Create the index layer
    var indexGeoJson = L.Proj.geoJson(indexData, {
      style: indexStyle
    });

    indexGeoJson.addTo(map);

    //Center map view on geojson polygon
    var bounds = indexGeoJson.getBounds();

    map.fitBounds(bounds, {maxZoom: 18, animate: false});
  } else {
    document.getElementById('map').innerText = 'No map information available';
  }
};
<|endoftext|>


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

module.exports = function(RED) {
  const SERVICE_IDENTIFIER = 'visual-recognition';
  const VisualRecognitionV3 = require('watson-developer-cloud/visual-recognition/v3');
  var pkg = require('../../package.json'),
    serviceutils = require('../../utilities/service-utils'),
    payloadutils = require('../../utilities/payload-utils'),
    //watson = require('watson-developer-cloud'),
    imageType = require('image-type'),
    url = require('url'),
    temp = require('temp'),
    fileType = require('file-type'),
    fs = require('fs'),
    async = require('async'),
    toArray = require('stream-to-array')
  sAPIKey = null,
    service = null;

  // temp is being used for file streaming to allow the file to arrive so it can be processed.
  temp.track();

  service = serviceutils.getServiceCreds(SERVICE_IDENTIFIER);

  if (service) {
    sAPIKey = service.api_key;
  }

  RED.httpAdmin.get('/watson-visual-recognition/vcap', function(req, res) {
    res.json(service ? {
      bound_service: true
    } : null);
  });

  function imageCheck(data) {
    return data instanceof Buffer && imageType(data) !== null;
  }

  function urlCheck(str) {
    var parsed = url.parse(str);
    return (!!parsed.hostname && !!parsed.protocol && str.indexOf(' ') < 0);
  }

  function stream_buffer(file, contents, cb) {
    fs.writeFile(file, contents, function(err) {
      if (err) {
        throw err;
      }
      cb();
    });
  }

  function verifyPayload(msg) {
    if (!msg.payload) {
      return Promise.reject('Missing property: msg.payload');
    } else {
      return Promise.resolve();
    }
  }

  function verifyInputs(feature, msg) {
    switch (feature) {
      case 'classifyImage':
      case 'detectFaces':
      case 'recognizeText':
        if (typeof msg.payload === 'boolean' ||
          typeof msg.payload === 'number') {
          return Promise.reject('Bad format : msg.payload must be a URL string or a Node.js Buffer');
        }
        break;
    }
    return Promise.resolve();
  }

  // Even though Visual Reconition SDK can accept a filestream as input
  // it can't handle the one on msg.payload, so read it into a buffer
  function checkForStream(msg) {
    var p = new Promise(function resolver(resolve, reject) {
      if (payloadutils.isReadableStream(msg.payload)) {
        msg.payload.resume();
        toArray(msg.payload)
          .then(function(parts) {
            var buffers = [];

            for (var i = 0; i < parts.length; ++i) {
              var part = parts[i];
              buffers.push((part instanceof Buffer) ? part : new Buffer(part));
            }
            msg.payload = Buffer.concat(buffers);
            resolve();

          });
      } else {
        resolve();
      }
    });
    return p;

  }

  function verifyServiceCredentials(node, msg) {
    // If it is present the newly provided user entered key
    // takes precedence over the existing one.
    node.apikey = sAPIKey || node.credentials.apikey;
    if (!node.apikey) {
      return Promise.reject('Missing Watson Visual Recognition API service credentials');
    }

    node.service = new VisualRecognitionV3({
      api_key: node.apikey,
      version_date: VisualRecognitionV3.VERSION_DATE_2016_05_20,
      headers: {
        'User-Agent': pkg.name + '-' + pkg.version
      }
    });

    return Promise.resolve();
  }

  function processTheResponse(body, feature, node, msg) {
    if (body == null) {
      return Promise.reject('call to watson visual recognition v3 service failed');
    } else if (body.images != null && body.images[0].error) {
      return Promise.reject(body.images[0].error);
    } else {
      if (feature === 'deleteClassifier') {
        msg.result = 'Successfully deleted classifier_id: ' + msg.params.classifier_id;
      } else {
        msg.result = body;
      }
      return Promise.resolve();
    }
  }

  function setCommonParams(node, msg, params) {
    if (msg.params != null && msg.params.classifier_ids != null) {
      params['classifier_ids'] = msg.params['classifier_ids'];
    }
    if (msg.params != null && msg.params.owners != null) {
      params['owners'] = msg.params['owners'];
    }
    if (msg.params != null && msg.params.threshold != null) {
      params['threshold'] = msg.params['threshold'];
    }
    if (node.config != null && node.config.lang != null) {
      params['Accept-Language'] = node.config.lang;
    }
    if (msg.params != null && msg.params.accept_language != null) {
      params['Accept-Language'] = msg.params['accept_language'];
    }
  }

  function prepareCommonParams(params, node, msg) {
    var p = new Promise(function resolver(resolve, reject) {
      if (imageCheck(msg.payload)) {
        var ft = fileType(msg.payload);
        var ext = ft ? ft.ext : 'tmp';
        temp.open({
          suffix: '.' + ext
        }, function(err, info) {
          if (err) {
            reject('Node has been unable to open the image stream');
          }
          stream_buffer(info.path, msg.payload, function() {
            params['images_file'] = fs.createReadStream(info.path);
            setCommonParams(node, msg, params);
            resolve();
          });
        });
      } else if (urlCheck(msg.payload)) {
        params['url'] = msg.payload;
        setCommonParams(node, msg, params);
        resolve();
      } else {
        reject('Payload must be either an image buffer or a string representing a url');
      }
    });
    return p;
  }

  // This is where the two read stream tasks are built, individually
  // asynTasks will be the two functions that will be invoked added
  // one at a time, each time this function is invoked
  // msg holds the parameters
  // k is the key value for the files in msg.
  // listParams is where the files will go so that they
  // can be sent to the service.
  // callback is the callback that async forces so that it can
  // control the async functions that it is invoking.
  function addTask(asyncTasks, msg, k, listParams, node) {
    asyncTasks.push(function(callback) {
      var buffer = msg.params[k];
      temp.open({
        suffix: '.' + fileType(buffer).ext
      }, function(err, info) {
        if (err) {
          node.status({
            fill: 'red',
            shape: 'ring',
            text: 'unable to open image stream'
          });
          node.error('Node has been unable to open the image stream', msg);
          return callback('open error on ' + k);
        }
        stream_buffer(info.path, msg.params[k], function() {
          listParams[k] = fs.createReadStream(info.path);
          callback(null, k);
        });
      });
    });
  }

  // This function is expecting
  // msg.params["name"] : a string name that will be used as prefix
  // for the returned classifier_id (Required)
  // msg.params["{classname}_positive_examples"] : a Node.js binary
  // Buffer of the ZIP that contains a minimum of 10 images. (Required)
  // msg.params["negative_examples"] : a Node.js binary Buffer of the ZIP
  // that contains a minimum of 10 images.(Optional)

  function prepareParamsCreateClassifier(params, node, msg) {
    var p = new Promise(function resolver(resolve, reject) {
      var listParams = {},
        asyncTasks = [],
        k = null;
      for (k in msg.params) {
        if (k.indexOf('_examples') >= 0) {
          addTask(asyncTasks, msg, k, listParams, node);
        } else if (k === 'name') {
          listParams[k] = msg.params[k];
        }
      }

      // function is invoked when both asyncTasks have completed.
      async.parallel(asyncTasks, function(error) {
        if (error) {
          reject(error);
          //throw error;
        }
        for (var p in listParams) {
          if (p != null) {
            params[p] = listParams[p];
          }
        }
        resolve();
      });
    });
    return p;
  }

  function performDeleteAllClassifiers(params, node, msg) {
    var p = new Promise(function resolver(resolve, reject) {
      node.service.listClassifiers(params, function(err, body) {
        node.status({});
        if (err) {
          reject(err);
        } else {
          // Array to hold async tasks
          var asyncTasks = [],
            nbTodelete = 0,
            nbdeleted = 0;
          nbTodelete = body.classifiers.length;
          body.classifiers.forEach(function(aClassifier) {
            asyncTasks.push(function(cb) {
              var parms = {};

              parms.classifier_id = aClassifier.classifier_id;
              node.service.deleteClassifier(parms, function(err) {
                if (err) {
                  node.error(err, msg);
                  return cb('error');
                }
                nbdeleted++;
                cb(null, parms.classifier_id);
              });
            });
          });
          async.parallel(asyncTasks, function(error, deletedList) {
            if (deletedList.length === nbTodelete) {
              msg.result = 'All custom classifiers have been deleted.';
            } else {
              msg.result = 'Some Classifiers could have not been deleted;' +
                'See log for errors.';
            }
            resolve();
          });
        }
      });
    });
    return p;
  }

  function invokeService(feature, params, node, msg) {
    var p = new Promise(function resolver(resolve, reject) {
      switch (feature) {
        case 'classifyImage':
          node.service.classify(params, function(err, body) {
            if (err) {
              reject(err);
            } else {
              resolve(body);
            }
          });
          break;
        case 'detectFaces':
          node.service.detectFaces(params, function(err, body) {
            if (err) {
              reject(err);
            } else {
              resolve(body);
            }
          });
          break;
        case 'recognizeText':
          node.service.recognizeText(params, function(err, body) {
            if (err) {
              reject(err);
            } else {
              resolve(body);
            }
          });
          break;
      }
    });
    return p;
  }

  function executeService(feature, params, node, msg) {
    var p = prepareCommonParams(params, node, msg)
      .then(function() {
        return invokeService(feature, params, node, msg);
      })
      .then(function(body) {
        return processTheResponse(body, feature, node, msg);
      });
    return p;
  }

  function invokeCreateClassifier(node, params) {
    var p = new Promise(function resolver(resolve, reject) {
      node.service.createClassifier(params, function(err, body) {
        if (err) {
          reject(err);
        } else {
          resolve(body);
        }
      });

    });
    return p;
  }

  function invokeListClassifiers(node, params) {
    var p = new Promise(function resolver(resolve, reject) {
      node.service.listClassifiers(params, function(err, body) {
        if (err) {
          reject(err);
        } else {
          resolve(body);
        }
      });
    });
    return p;
  }

  function invokeGetClassifier(node, params, msg) {
    var p = new Promise(function resolver(resolve, reject) {
      params['classifier_id'] = msg.params['classifier_id'];
      node.service.getClassifier(params, function(err, body) {
        if (err) {
          reject(err);
        } else {
          resolve(body);
        }
      });
    });
    return p;
  }

  function invokeDeleteClassifier(node, params, msg) {
    var p = new Promise(function resolver(resolve, reject) {
      params['classifier_id'] = msg.params['classifier_id'];
      node.service.deleteClassifier(params, function(err, body) {
        if (err) {
          reject(err);
        } else {
          resolve(body);
        }
      });
    });
    return p;
  }

  function executeCreateClassifier(params, node, msg) {
    var p = prepareParamsCreateClassifier(params, node, msg)
      .then(function() {
        return invokeCreateClassifier(node, params);
      });

    return p;
  }

  function executeUtilService(feature, params, node, msg) {
    var p = null;
    switch (feature) {
      case 'createClassifier':
        p = executeCreateClassifier(params, node, msg)
          .then(function(body) {
            return processTheResponse(body, feature, node, msg);
          });
        break;

      case 'retrieveClassifiersList':
        p = invokeListClassifiers(node, params)
          .then(function(body) {
            return processTheResponse(body, feature, node, msg);
          });
        break;

      case 'retrieveClassifierDetails':
        p = invokeGetClassifier(node, params, msg)
          .then(function(body) {
            return processTheResponse(body, feature, node, msg);
          });
        break;

      case 'deleteClassifier':
        p = invokeDeleteClassifier(node, params, msg)
          .then(function(body) {
            return processTheResponse(body, feature, node, msg);
          });
        break;

      case 'deleteAllClassifiers':
        p = performDeleteAllClassifiers(params, node, msg);
        break;

      default:
        p = Promise.reject('Mode ' + feature + ' not understood');
    }
    return p;
  }

  function execute(feature, params, node, msg) {
    node.status({
      fill: 'blue',
      shape: 'dot',
      text: 'Calling ' + feature + ' ...'
    });
    if (feature === 'classifyImage' || feature === 'detectFaces' || feature === 'recognizeText') {
      return executeService(feature, params, node, msg);
      //return Promise.resolve();
    } else {
      return executeUtilService(feature, params, node, msg);
      // return Promise.resolve();
    }
  }

  // This is the Watson Visual Recognition V3 Node
  function WatsonVisualRecognitionV3Node(config) {
    var node = this,
      b = false,
      feature = config['image-feature'];
    RED.nodes.createNode(this, config);
    node.config = config;

    node.on('input', function(msg) {
      var params = {};

      node.status({});

      verifyPayload(msg)
        .then(function() {
          return checkForStream(msg);
        })
        .then(function() {
          return verifyInputs(feature, msg);
        })
        .then(function() {
          return verifyServiceCredentials(node, msg);
        })
        .then(function() {
          return execute(feature, params, node, msg);
        })
        .then(function() {
          temp.cleanup();
          node.status({});
          node.send(msg);
        })
        .catch(function(err) {
          payloadutils.reportError(node, msg, err);

          msg.result = {};
          msg.result['error'] = err;
          // Note: This node.send forwards the error to the next node,
          // if this isn't desired then this line needs to be removed.
          // Should be ok as the node.error would already have recorded
          // the error in the debug console.
          temp.cleanup();
          node.send(msg);
        });
    });
  }

  RED.nodes.registerType('visual-recognition-v3', WatsonVisualRecognitionV3Node, {
    credentials: {
      apikey: {
        type: 'password'
      }
    }
  });

  RED.nodes.registerType('visual-recognition-util-v3', WatsonVisualRecognitionV3Node, {
    credentials: {
      apikey: {
        type: 'password'
      }
    }
  });

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

module.exports = function(RED) {
  const SERVICE_IDENTIFIER = 'visual-recognition';
  const VisualRecognitionV3 = require('watson-developer-cloud/visual-recognition/v3');
  var pkg = require('../../package.json'),
    serviceutils = require('../../utilities/service-utils'),
    payloadutils = require('../../utilities/payload-utils'),
    //watson = require('watson-developer-cloud'),
    imageType = require('image-type'),
    url = require('url'),
    temp = require('temp'),
    fileType = require('file-type'),
    fs = require('fs'),
    async = require('async'),
    toArray = require('stream-to-array'),
    sAPIKey = null,
    service = null;

  // temp is being used for file streaming to allow the file to arrive so it can be processed.
  temp.track();

  service = serviceutils.getServiceCreds(SERVICE_IDENTIFIER);

  if (service) {
    sAPIKey = service.api_key;
  }

  RED.httpAdmin.get('/watson-visual-recognition/vcap', function(req, res) {
    res.json(service ? {
      bound_service: true
    } : null);
  });

  function imageCheck(data) {
    return data instanceof Buffer && imageType(data) !== null;
  }

  function urlCheck(str) {
    var parsed = url.parse(str);
    return (!!parsed.hostname && !!parsed.protocol && str.indexOf(' ') < 0);
  }

  function stream_buffer(file, contents, cb) {
    fs.writeFile(file, contents, function(err) {
      if (err) {
        throw err;
      }
      cb();
    });
  }

  function verifyPayload(msg) {
    if (!msg.payload) {
      return Promise.reject('Missing property: msg.payload');
    } else {
      return Promise.resolve();
    }
  }

  function verifyInputs(feature, msg) {
    switch (feature) {
      case 'classifyImage':
      case 'detectFaces':
      case 'recognizeText':
        if (typeof msg.payload === 'boolean' ||
          typeof msg.payload === 'number') {
          return Promise.reject('Bad format : msg.payload must be a URL string or a Node.js Buffer');
        }
        break;
    }
    return Promise.resolve();
  }

  // Even though Visual Reconition SDK can accept a filestream as input
  // it can't handle the one on msg.payload, so read it into a buffer
  function checkForStream(msg) {
    var p = new Promise(function resolver(resolve, reject) {
      if (payloadutils.isReadableStream(msg.payload)) {
        msg.payload.resume();
        toArray(msg.payload)
          .then(function(parts) {
            var buffers = [];

            for (var i = 0; i < parts.length; ++i) {
              var part = parts[i];
              buffers.push((part instanceof Buffer) ? part : new Buffer(part));
            }
            msg.payload = Buffer.concat(buffers);
            resolve();

          });
      } else {
        resolve();
      }
    });
    return p;

  }

  function verifyServiceCredentials(node, msg) {
    // If it is present the newly provided user entered key
    // takes precedence over the existing one.
    node.apikey = sAPIKey || node.credentials.apikey;
    if (!node.apikey) {
      return Promise.reject('Missing Watson Visual Recognition API service credentials');
    }

    node.service = new VisualRecognitionV3({
      api_key: node.apikey,
      version_date: VisualRecognitionV3.VERSION_DATE_2016_05_20,
      headers: {
        'User-Agent': pkg.name + '-' + pkg.version
      }
    });

    return Promise.resolve();
  }

  function processTheResponse(body, feature, node, msg) {
    if (body == null) {
      return Promise.reject('call to watson visual recognition v3 service failed');
    } else if (body.images != null && body.images[0].error) {
      return Promise.reject(body.images[0].error);
    } else {
      if (feature === 'deleteClassifier') {
        msg.result = 'Successfully deleted classifier_id: ' + msg.params.classifier_id;
      } else {
        msg.result = body;
      }
      return Promise.resolve();
    }
  }

  function setCommonParams(node, msg, params) {
    if (msg.params != null && msg.params.classifier_ids != null) {
      params['classifier_ids'] = msg.params['classifier_ids'];
    }
    if (msg.params != null && msg.params.owners != null) {
      params['owners'] = msg.params['owners'];
    }
    if (msg.params != null && msg.params.threshold != null) {
      params['threshold'] = msg.params['threshold'];
    }
    if (node.config != null && node.config.lang != null) {
      params['Accept-Language'] = node.config.lang;
    }
    if (msg.params != null && msg.params.accept_language != null) {
      params['Accept-Language'] = msg.params['accept_language'];
    }
  }

  function prepareCommonParams(params, node, msg) {
    var p = new Promise(function resolver(resolve, reject) {
      if (imageCheck(msg.payload)) {
        var ft = fileType(msg.payload);
        var ext = ft ? ft.ext : 'tmp';
        temp.open({
          suffix: '.' + ext
        }, function(err, info) {
          if (err) {
            reject('Node has been unable to open the image stream');
          }
          stream_buffer(info.path, msg.payload, function() {
            params['images_file'] = fs.createReadStream(info.path);
            setCommonParams(node, msg, params);
            resolve();
          });
        });
      } else if (urlCheck(msg.payload)) {
        params['url'] = msg.payload;
        setCommonParams(node, msg, params);
        resolve();
      } else {
        reject('Payload must be either an image buffer or a string representing a url');
      }
    });
    return p;
  }

  // This is where the two read stream tasks are built, individually
  // asynTasks will be the two functions that will be invoked added
  // one at a time, each time this function is invoked
  // msg holds the parameters
  // k is the key value for the files in msg.
  // listParams is where the files will go so that they
  // can be sent to the service.
  // callback is the callback that async forces so that it can
  // control the async functions that it is invoking.
  function addTask(asyncTasks, msg, k, listParams, node) {
    asyncTasks.push(function(callback) {
      var buffer = msg.params[k];
      temp.open({
        suffix: '.' + fileType(buffer).ext
      }, function(err, info) {
        if (err) {
          node.status({
            fill: 'red',
            shape: 'ring',
            text: 'unable to open image stream'
          });
          node.error('Node has been unable to open the image stream', msg);
          return callback('open error on ' + k);
        }
        stream_buffer(info.path, msg.params[k], function() {
          listParams[k] = fs.createReadStream(info.path);
          callback(null, k);
        });
      });
    });
  }

  // This function is expecting
  // msg.params["name"] : a string name that will be used as prefix
  // for the returned classifier_id (Required)
  // msg.params["{classname}_positive_examples"] : a Node.js binary
  // Buffer of the ZIP that contains a minimum of 10 images. (Required)
  // msg.params["negative_examples"] : a Node.js binary Buffer of the ZIP
  // that contains a minimum of 10 images.(Optional)

  function prepareParamsCreateClassifier(params, node, msg) {
    var p = new Promise(function resolver(resolve, reject) {
      var listParams = {},
        asyncTasks = [],
        k = null;
      for (k in msg.params) {
        if (k.indexOf('_examples') >= 0) {
          addTask(asyncTasks, msg, k, listParams, node);
        } else if (k === 'name') {
          listParams[k] = msg.params[k];
        }
      }

      // function is invoked when both asyncTasks have completed.
      async.parallel(asyncTasks, function(error) {
        if (error) {
          reject(error);
          //throw error;
        }
        for (var p in listParams) {
          if (p != null) {
            params[p] = listParams[p];
          }
        }
        resolve();
      });
    });
    return p;
  }

  function performDeleteAllClassifiers(params, node, msg) {
    var p = new Promise(function resolver(resolve, reject) {
      node.service.listClassifiers(params, function(err, body) {
        node.status({});
        if (err) {
          reject(err);
        } else {
          // Array to hold async tasks
          var asyncTasks = [],
            nbTodelete = 0,
            nbdeleted = 0;
          nbTodelete = body.classifiers.length;
          body.classifiers.forEach(function(aClassifier) {
            asyncTasks.push(function(cb) {
              var parms = {};

              parms.classifier_id = aClassifier.classifier_id;
              node.service.deleteClassifier(parms, function(err) {
                if (err) {
                  node.error(err, msg);
                  return cb('error');
                }
                nbdeleted++;
                cb(null, parms.classifier_id);
              });
            });
          });
          async.parallel(asyncTasks, function(error, deletedList) {
            if (deletedList.length === nbTodelete) {
              msg.result = 'All custom classifiers have been deleted.';
            } else {
              msg.result = 'Some Classifiers could have not been deleted;' +
                'See log for errors.';
            }
            resolve();
          });
        }
      });
    });
    return p;
  }

  function invokeService(feature, params, node, msg) {
    var p = new Promise(function resolver(resolve, reject) {
      switch (feature) {
        case 'classifyImage':
          node.service.classify(params, function(err, body) {
            if (err) {
              reject(err);
            } else {
              resolve(body);
            }
          });
          break;
        case 'detectFaces':
          node.service.detectFaces(params, function(err, body) {
            if (err) {
              reject(err);
            } else {
              resolve(body);
            }
          });
          break;
        case 'recognizeText':
          node.service.recognizeText(params, function(err, body) {
            if (err) {
              reject(err);
            } else {
              resolve(body);
            }
          });
          break;
      }
    });
    return p;
  }

  function executeService(feature, params, node, msg) {
    var p = prepareCommonParams(params, node, msg)
      .then(function() {
        return invokeService(feature, params, node, msg);
      })
      .then(function(body) {
        return processTheResponse(body, feature, node, msg);
      });
    return p;
  }

  function invokeCreateClassifier(node, params) {
    var p = new Promise(function resolver(resolve, reject) {
      node.service.createClassifier(params, function(err, body) {
        if (err) {
          reject(err);
        } else {
          resolve(body);
        }
      });

    });
    return p;
  }

  function invokeListClassifiers(node, params) {
    var p = new Promise(function resolver(resolve, reject) {
      node.service.listClassifiers(params, function(err, body) {
        if (err) {
          reject(err);
        } else {
          resolve(body);
        }
      });
    });
    return p;
  }

  function invokeGetClassifier(node, params, msg) {
    var p = new Promise(function resolver(resolve, reject) {
      params['classifier_id'] = msg.params['classifier_id'];
      node.service.getClassifier(params, function(err, body) {
        if (err) {
          reject(err);
        } else {
          resolve(body);
        }
      });
    });
    return p;
  }

  function invokeDeleteClassifier(node, params, msg) {
    var p = new Promise(function resolver(resolve, reject) {
      params['classifier_id'] = msg.params['classifier_id'];
      node.service.deleteClassifier(params, function(err, body) {
        if (err) {
          reject(err);
        } else {
          resolve(body);
        }
      });
    });
    return p;
  }

  function executeCreateClassifier(params, node, msg) {
    var p = prepareParamsCreateClassifier(params, node, msg)
      .then(function() {
        return invokeCreateClassifier(node, params);
      });

    return p;
  }

  function executeUtilService(feature, params, node, msg) {
    var p = null;
    switch (feature) {
      case 'createClassifier':
        p = executeCreateClassifier(params, node, msg)
          .then(function(body) {
            return processTheResponse(body, feature, node, msg);
          });
        break;

      case 'retrieveClassifiersList':
        p = invokeListClassifiers(node, params)
          .then(function(body) {
            return processTheResponse(body, feature, node, msg);
          });
        break;

      case 'retrieveClassifierDetails':
        p = invokeGetClassifier(node, params, msg)
          .then(function(body) {
            return processTheResponse(body, feature, node, msg);
          });
        break;

      case 'deleteClassifier':
        p = invokeDeleteClassifier(node, params, msg)
          .then(function(body) {
            return processTheResponse(body, feature, node, msg);
          });
        break;

      case 'deleteAllClassifiers':
        p = performDeleteAllClassifiers(params, node, msg);
        break;

      default:
        p = Promise.reject('Mode ' + feature + ' not understood');
    }
    return p;
  }

  function execute(feature, params, node, msg) {
    node.status({
      fill: 'blue',
      shape: 'dot',
      text: 'Calling ' + feature + ' ...'
    });
    if (feature === 'classifyImage' || feature === 'detectFaces' || feature === 'recognizeText') {
      return executeService(feature, params, node, msg);
      //return Promise.resolve();
    } else {
      return executeUtilService(feature, params, node, msg);
      // return Promise.resolve();
    }
  }

  // This is the Watson Visual Recognition V3 Node
  function WatsonVisualRecognitionV3Node(config) {
    var node = this,
      b = false,
      feature = config['image-feature'];
    RED.nodes.createNode(this, config);
    node.config = config;

    node.on('input', function(msg) {
      var params = {};

      node.status({});

      verifyPayload(msg)
        .then(function() {
          return checkForStream(msg);
        })
        .then(function() {
          return verifyInputs(feature, msg);
        })
        .then(function() {
          return verifyServiceCredentials(node, msg);
        })
        .then(function() {
          return execute(feature, params, node, msg);
        })
        .then(function() {
          temp.cleanup();
          node.status({});
          node.send(msg);
        })
        .catch(function(err) {
          payloadutils.reportError(node, msg, err);

          msg.result = {};
          msg.result['error'] = err;
          // Note: This node.send forwards the error to the next node,
          // if this isn't desired then this line needs to be removed.
          // Should be ok as the node.error would already have recorded
          // the error in the debug console.
          temp.cleanup();
          node.send(msg);
        });
    });
  }

  RED.nodes.registerType('visual-recognition-v3', WatsonVisualRecognitionV3Node, {
    credentials: {
      apikey: {
        type: 'password'
      }
    }
  });

  RED.nodes.registerType('visual-recognition-util-v3', WatsonVisualRecognitionV3Node, {
    credentials: {
      apikey: {
        type: 'password'
      }
    }
  });

};
<|endoftext|>


<commit_before>$(document).ready(function () {

    // $('.js_nav_month a:first').on('click', function (e) {
    //     e.preventDefault();
    //     var $ul = $(this).next("ul");
    //     if (!$ul.find('li').length) {
    //         // TODO: Why POST? (to pass the domain) A GET would be more appropriate...
    //         // This should be done server side anyway...
    //         $.post('/blog/nav', {'domain': $(this).data("domain")}, function (result) {
    //             var blog_id = +window.location.pathname.split("/").pop();
    //             $(JSON.parse(result)).each(function () {
    //                 var $li = $($.parseHTML(this.fragment));
    //                 if (blog_id == this.id) $li.addClass("active");
    //                 if (!this.website_published) $li.find('a').css("color", "red");
    //                 $ul.append($li);
    //             });

    //         });
    //     } else {
    //         $ul.toggle();
    //     }
    // });

    // When choosing an delivery carrier, update the quotation and the acquirers
    var $carrier = $("#delivery_carrier");
    $carrier.find("input[name='delivery_type']").click(function (ev) {
        var carrier_id = $(ev.currentTarget).val();
        console.log('choosing carrier', carrier_id);
        var link = $carrier.find('a');
        link.attr('href', '/shop/payment?carrier_id=' + carrier_id)
    });

});
<commit_msg>[CLEAN] website_sale_delivery: removed dead JS code<commit_after>$(document).ready(function () {

    // When choosing an delivery carrier, update the quotation and the acquirers
    var $carrier = $("#delivery_carrier");
    $carrier.find("input[name='delivery_type']").click(function (ev) {
        var carrier_id = $(ev.currentTarget).val();
        console.log('choosing carrier', carrier_id);
        var link = $carrier.find('a');
        link.attr('href', '/shop/payment?carrier_id=' + carrier_id)
    });

});
<|endoftext|>