<commit_before>import { combineReducers } from 'redux';
import { routerReducer as routing } from 'react-router-redux';
import { verticalTreeData } from './verticalTreeReducers';
import userCode from './userCode';
import async from './asyncReducers';

const rootReducer = combineReducers({
  routing,
  verticalTreeData,
  userCode,
  async
});

export default rootReducer;
<commit_msg>Include tutorial reducers in root reducer<commit_after>import { combineReducers } from 'redux';
import { routerReducer as routing } from 'react-router-redux';
import { verticalTreeData } from './verticalTreeReducers';
import userCode from './userCode';
import async from './asyncReducers';
import tutorial from './tutorialReducers';

const rootReducer = combineReducers({
  routing,
  verticalTreeData,
  userCode,
  async,
  tutorial
});

export default rootReducer;
<|endoftext|>


<commit_before>var finalHandler = require('finalhandler'),
    serveStatic = require('serve-static'),
    serve = serveStatic(__dirname + '/../static', {
        'index': ['landing_page.html', 'index.html']
    }),
    fs = require('fs'),
    path = require('path');

var RequestRouter = function () {
    var handleGet = function (req, res) {
        var done = finalHandler(res, res);
        serve(req, res, done);
    };

    var handlePost = function (req, res) {
        var formidable = require('formidable'),
            uploadDir = __dirname + '/../run/',
            util = require('util'),
            form = new formidable.IncomingForm();

        form.uploadDir = uploadDir;

        form.parse(req, function (err, fields, files) {
            var tempFile = files.file,
                newFileName = 'upload_' + new Date().getTime().toString() + path.extname(tempFile.name);

            fs.rename(tempFile.path, path.join(uploadDir, newFileName));

            sendMessages(path.join(uploadDir, newFileName)).then(function(resopnses){
                console.log("-----starting upload-----");
                console.log(resopnses);
                res.writeHead(302, {
                    'content-type': 'text/html',
                    'Location': '/' + req.headers.referer.match(/^http(s)?:\/\/(.+)$/)[2].split('/')[1],
                    'Set-Cookie': 'RapidProResponse=' + JSON.stringify(resopnses)
                });

                res.end();
            });
        });
    };

    var handleProhibitted = function (req, res) {
        res.writeHead(403, {
            'Content-type': 'text/plain'
        });
        res.write('Request not permitted');
        res.end();
    };

    var sendMessages = function(filePath){
        Case = require('./case');

        return Case.load(filePath).then(function (allCases) {
            var rapidProMessages = allCases.map(function (aCase) {
                return aCase.toRapidProMessage();
            });
            var sendPromises = rapidProMessages.map(function(message) {
                return message.send();
            });

            var Q = require('q');
            return Q.all(sendPromises);
        });
    };

    this.handle = function (req, res) {
        if (req.method == 'GET') {
            handleGet(req, res);
        } else if (req.method == 'POST') {
            handlePost(req, res);
        } else {
            handleProhibitted(req, res);
        }
    };

};

module.exports = RequestRouter;
<commit_msg>redirect to current path after post<commit_after>var finalHandler = require('finalhandler'),
    serveStatic = require('serve-static'),
    serve = serveStatic(__dirname + '/../static', {
        'index': ['landing_page.html', 'index.html']
    }),
    fs = require('fs'),
    path = require('path');

var RequestRouter = function () {
    var handleGet = function (req, res) {
        var done = finalHandler(res, res);
        serve(req, res, done);
    };

    var handlePost = function (req, res) {
        var formidable = require('formidable'),
            uploadDir = __dirname + '/../run/',
            util = require('util'),
            form = new formidable.IncomingForm();

        form.uploadDir = uploadDir;

        form.parse(req, function (err, fields, files) {
            var tempFile = files.file,
                newFileName = 'upload_' + new Date().getTime().toString() + path.extname(tempFile.name);

            fs.rename(tempFile.path, path.join(uploadDir, newFileName));

            sendMessages(path.join(uploadDir, newFileName)).then(function(resopnses){
                console.log("-----starting upload-----");
                console.log(resopnses);
                res.writeHead(302, {
                    'content-type': 'text/html',
                    'Location': '/' + req.headers.referer.match(/^http(s)?:\/\/(.+)$/)[2].split('/').slice(1).join('/'),
                    'Set-Cookie': 'RapidProResponse=' + JSON.stringify(resopnses)
                });

                res.end();
            });
        });
    };

    var handleProhibitted = function (req, res) {
        res.writeHead(403, {
            'Content-type': 'text/plain'
        });
        res.write('Request not permitted');
        res.end();
    };

    var sendMessages = function(filePath){
        Case = require('./case');

        return Case.load(filePath).then(function (allCases) {
            var rapidProMessages = allCases.map(function (aCase) {
                return aCase.toRapidProMessage();
            });
            var sendPromises = rapidProMessages.map(function(message) {
                return message.send();
            });

            var Q = require('q');
            return Q.all(sendPromises);
        });
    };

    this.handle = function (req, res) {
        if (req.method == 'GET') {
            handleGet(req, res);
        } else if (req.method == 'POST') {
            handlePost(req, res);
        } else {
            handleProhibitted(req, res);
        }
    };

};

module.exports = RequestRouter;
<|endoftext|>


<commit_before>import Ember from 'ember';
import JSONSerializer from 'ember-data/serializers/json';

var traverse = function (object, callback) {
  if (!object) {
    return;
  }

  if (typeof(object) === 'object' && !Ember.isArray(object)) {
    callback(object);
  }

  if (Ember.isArray(object)) {
    for (let item of object) {
      traverse(item, callback);
    }
  } else if (typeof object === 'object') {
    for (let key in object) {
      if (object.hasOwnProperty(key)) {
        let item = object[key];
        traverse(item, callback);
      }
    }
  }
};

export default JSONSerializer.extend({
  isNewSerializerAPI: true,

  extractRelationship(type, hash) {
    if (hash && !hash.id && hash['@href']) {
      hash.id = hash['@href'];
    }

    let relationshipHash = this._super(...arguments);
    if (relationshipHash && relationshipHash['@type']) {
      relationshipHash.type = relationshipHash['@type'];
    } else if (relationshipHash && !relationshipHash.type) {
      relationshipHash.type = type;
    }
    return relationshipHash;
  },

  extractRelationships() {
    let relationships = this._super(...arguments);
    return relationships;
  },

  keyForRelationship(key/* , typeClass, method*/) {
    if (key && key.underscore) {
      return key.underscore();
    } else {
      return key;
    }
  },

  extractAttributes(/* modelClass,resourceHash*/) {
    let attributes = this._super(...arguments);
    for (let key in attributes) {
      if (key.startsWith('@')) {
        delete attributes.key;
      }
    }

    return attributes;
  },

  normalizeResponse(store, primaryModelClass, payload/* , id, requestType*/) {
    this._fixReferences(payload);
    return this._super(...arguments);
  },

  normalizeArrayResponse(store, primaryModelClass, payload/* , id, requestType*/) {
    let documentHash = {
      data: null,
      included: []
    };

    let meta = this.extractMeta(store, primaryModelClass, payload);
    if (meta) {
      let metaType = Ember.typeOf(meta);
      let metaIsObject = metaType == 'object';
      let errorMessage =
        `The 'meta' returned from 'extractMeta' has to be an object, not ${metaType}.`;
      Ember.assert(errorMessage, metaIsObject);
      documentHash.meta = meta;
    }

    let items;
    let type = payload['@type'];
    if (type) {
      items = payload[type];
    } else {
      items = payload[primaryModelClass.modelName.underscore() + 's'];
    }

    documentHash.data = items.map((item) => {
      let { data, included } = this.normalize(primaryModelClass, item);
      if (included) {
        documentHash.included.push(...included);
      }
      return data;
    });

    return documentHash;
  },

  normalize(/* modelClass, resourceHash*/) {
    let { data, included } = this._super(...arguments);
    if (!included) {
      included = [];
    }
    let store = this.store;

    if (data.relationships) {
      Object.keys(data.relationships).forEach(function (key) {
        let relationship = data.relationships[key];
        let process = function (data) {
          if (data['@representation'] !== 'standard') {
            return;
          }
          let type = data['@type'];
          let serializer = store.serializerFor(type);
          let modelClass = store.modelFor(type);
          let normalized = serializer.normalize(modelClass, data);
          included.push(normalized.data);
          if (normalized.included) {
            normalized.included.forEach(function (item) {
              included.push(item);
            });
          }
        };

        if (Array.isArray(relationship.data)) {
          relationship.data.forEach(process);
        } else if (relationship && relationship.data) {
          process(relationship.data);
        }
      });
    }

    return { data, included };
  },

  keyForAttribute(key) {
    return Ember.String.underscore(key);
  },

  _fixReferences(payload) {
    let byHref = {}, records;
    if (payload['@type']) {
      // API V3 doesn't return all of the objects in a full representation
      // If an object is present in one place in the response, all of the
      // other occurences will be just references of a kind - they will just
      // include @href property.
      //
      // I don't want to identify records by href in ember-data, so here I'll
      // set an id and a @type field on all of the references.
      //
      // First we need to group all of the items in the response by href:
      traverse(payload, (item) => {
        let href = item['@href'];
        if (href) {
          let records = byHref[href];
          if (records) {
            records.push(item);
          } else {
            byHref[href] = [item];
          }
        }
      });

      // Then we can choose a record with an id for each href and put the id
      // in all of the other occurences.
      for (let href in byHref) {
        records = byHref[href];
        let recordWithAnId = records.find((record) => record.id);
        if (recordWithAnId) {
          for (let record of records) {
            record.id = recordWithAnId.id;
            // record['@type'] = recordWithAnId['@type'];
          }
        }
      }
    }

    return payload;
  }
});
<commit_msg>Remove unneeded override<commit_after>import Ember from 'ember';
import JSONSerializer from 'ember-data/serializers/json';

var traverse = function (object, callback) {
  if (!object) {
    return;
  }

  if (typeof(object) === 'object' && !Ember.isArray(object)) {
    callback(object);
  }

  if (Ember.isArray(object)) {
    for (let item of object) {
      traverse(item, callback);
    }
  } else if (typeof object === 'object') {
    for (let key in object) {
      if (object.hasOwnProperty(key)) {
        let item = object[key];
        traverse(item, callback);
      }
    }
  }
};

export default JSONSerializer.extend({
  isNewSerializerAPI: true,

  extractRelationship(type, hash) {
    if (hash && !hash.id && hash['@href']) {
      hash.id = hash['@href'];
    }

    let relationshipHash = this._super(...arguments);
    if (relationshipHash && relationshipHash['@type']) {
      relationshipHash.type = relationshipHash['@type'];
    } else if (relationshipHash && !relationshipHash.type) {
      relationshipHash.type = type;
    }
    return relationshipHash;
  },

  keyForRelationship(key/* , typeClass, method*/) {
    if (key && key.underscore) {
      return key.underscore();
    } else {
      return key;
    }
  },

  extractAttributes(/* modelClass,resourceHash*/) {
    let attributes = this._super(...arguments);
    for (let key in attributes) {
      if (key.startsWith('@')) {
        delete attributes.key;
      }
    }

    return attributes;
  },

  normalizeResponse(store, primaryModelClass, payload/* , id, requestType*/) {
    this._fixReferences(payload);
    return this._super(...arguments);
  },

  normalizeArrayResponse(store, primaryModelClass, payload/* , id, requestType*/) {
    let documentHash = {
      data: null,
      included: []
    };

    let meta = this.extractMeta(store, primaryModelClass, payload);
    if (meta) {
      let metaType = Ember.typeOf(meta);
      let metaIsObject = metaType == 'object';
      let errorMessage =
        `The 'meta' returned from 'extractMeta' has to be an object, not ${metaType}.`;
      Ember.assert(errorMessage, metaIsObject);
      documentHash.meta = meta;
    }

    let items;
    let type = payload['@type'];
    if (type) {
      items = payload[type];
    } else {
      items = payload[primaryModelClass.modelName.underscore() + 's'];
    }

    documentHash.data = items.map((item) => {
      let { data, included } = this.normalize(primaryModelClass, item);
      if (included) {
        documentHash.included.push(...included);
      }
      return data;
    });

    return documentHash;
  },

  normalize(/* modelClass, resourceHash*/) {
    let { data, included } = this._super(...arguments);
    if (!included) {
      included = [];
    }
    let store = this.store;

    if (data.relationships) {
      Object.keys(data.relationships).forEach(function (key) {
        let relationship = data.relationships[key];
        let process = function (data) {
          if (data['@representation'] !== 'standard') {
            return;
          }
          let type = data['@type'];
          let serializer = store.serializerFor(type);
          let modelClass = store.modelFor(type);
          let normalized = serializer.normalize(modelClass, data);
          included.push(normalized.data);
          if (normalized.included) {
            normalized.included.forEach(function (item) {
              included.push(item);
            });
          }
        };

        if (Array.isArray(relationship.data)) {
          relationship.data.forEach(process);
        } else if (relationship && relationship.data) {
          process(relationship.data);
        }
      });
    }

    return { data, included };
  },

  keyForAttribute(key) {
    return Ember.String.underscore(key);
  },

  _fixReferences(payload) {
    let byHref = {}, records;
    if (payload['@type']) {
      // API V3 doesn't return all of the objects in a full representation
      // If an object is present in one place in the response, all of the
      // other occurences will be just references of a kind - they will just
      // include @href property.
      //
      // I don't want to identify records by href in ember-data, so here I'll
      // set an id and a @type field on all of the references.
      //
      // First we need to group all of the items in the response by href:
      traverse(payload, (item) => {
        let href = item['@href'];
        if (href) {
          let records = byHref[href];
          if (records) {
            records.push(item);
          } else {
            byHref[href] = [item];
          }
        }
      });

      // Then we can choose a record with an id for each href and put the id
      // in all of the other occurences.
      for (let href in byHref) {
        records = byHref[href];
        let recordWithAnId = records.find((record) => record.id);
        if (recordWithAnId) {
          for (let record of records) {
            record.id = recordWithAnId.id;
            // record['@type'] = recordWithAnId['@type'];
          }
        }
      }
    }

    return payload;
  }
});
<|endoftext|>


<commit_before>/*****************************************************************************/
/* Server Only Methods */
/*****************************************************************************/
var Future = Npm.require('fibers/future');

Meteor.methods({
  'user/toggleAutoupdate': function() {

    this.unblock();
    if (!this.userId) return false;

    var user = Meteor.users.findOne(this.userId);
    return Meteor.users.update(this.userId, {
      $set: {
        autoupdateApps: !(user && user.autoupdateApps)
      }
    });

  },

  'user/uninstallApp': function(appId) {

    this.unblock();
    if (!this.userId) return false;

    var unset = {};

    unset['installedApps.' + appId] = true;
    return Meteor.users.update(this.userId, {
      $unset: unset
    });

  },

  'user/installApp': function(appId) {

    // TODO: actually install the app!
    this.unblock();
    if (!this.userId) return false;
    var app = Apps.findOne(appId);
    if (!app) return false;

    var set = {};

    set['installedApps.' + appId] = {
      version: _.last(app.versions),
      dateTime: new Date()
    };
    return Meteor.users.update(this.userId, {
      $set: set
    });

  },

  'user/updateApp': function(appId) {

    // TODO: actually update the app!
    this.unblock();
    if (!this.userId) return false;
    var app = Apps.findOne(appId);
    if (!app) return false;

    var set = {},
        userId = this.userId;

    set['installedApps.' + appId] = {
      version: app.latestVersion(),
      dateTime: new Date()
    };
    // The update is recorded only after an interval as livedata will automatically
    // inform the client, which will result in the UI updating.  We need to give
    // the "app updated" animation time to play before this happens.
    Meteor.setTimeout(function() {
      Meteor.users.update(userId, {
        $set: set
      });
    }, 3000);
    return true;

  },

  'user/updateAllApps': function() {

    // TODO: actually update the apps!
    this.unblock();
    if (!this.userId) return false;

    Genres.findIn('Updates Available', {}, {}, this).forEach(function(app) {

      Meteor.call('user/updateApp', app._id);

    });

    return true;

  },

  'user/save-app': function(app) {

    this.unblock();
    if (!this.userId) return false;

    // check(app, Schemas.AppsBase);  TODO: should we be validating here? User should be able to save in place.
    var set = {},
      setString = 'savedApp.' + (app.replacesApp || 'new');
    set[setString] = app;
    return Meteor.users.update(this.userId, {$set: set});

  },

  'user/delete-saved-app': function(appId) {

    this.unblock();
    if (!this.userId) return false;

    var unset = {},
      unsetString = 'savedApp.' + (appId || 'new');
    unset[unsetString] = true;
    return Meteor.users.update(this.userId, {$unset: unset});

  },

  'user/submit-app': function(app) {

    var fut = new Future();

    this.unblock();
    if (!this.userId) return false;
    if (this.userId !== app.author) throw new Meteor.Error('wrong author', 'Can only submit app by logged-in user');

    Apps.insert(app, function(err, res) {
      console.log(err, res);
      if (err) throw new Meteor.Error(err.message);
      else fut.return(res);
    });

    return fut.wait();

  },

  'user/submit-update': function(app) {

    var fut = new Future();

    this.unblock();
    if (!this.userId) return false;
    if (this.userId !== app.author) throw new Meteor.Error('wrong author', 'Can only submit app by logged-in user');

    check(app.versions.length, Match.Where(function(l) {return l > 0;}));

    Apps.insert(app, function(err, res) {
      console.log(err, res);
      if (err) throw new Meteor.Error(err.message);
      else fut.return(res);
    });

    return fut.wait();

  },

  'user/delete-app': function(appId) {

    var fut = new Future(),
        app = Apps.findOne(appId);

    this.unblock();
    if (!this.userId) return false;
    if (!app || this.userId !== app.author) throw new Meteor.Error('wrong author', 'Can only delete app by logged-in user');

    return Apps.remove(appId);
    // TODO: should we inform installed users that this app is no longer available?

  },

  'user/chip-in': function(appId, amount) {

    this.unblock();

    check(amount, Number);
    check(amount, Match.Where(function(amount) {return (0 < amount) && (40 >= amount);}));

    var user = Meteor.users.findOne(this.userId),
        app = Apps.findOne(appId);

    if (!user) throw new Meteor.Error('no authenticated user', 'Cannot chip in if user is not authenticated');
    if (!Apps.findOne(appId)) throw new Meteor.Error('no matching app', 'Cannot chip in for an app which is not in the database');

    // TODO: Actually make a payment
    console.log('User ' + user.username + ' wants to chip in ' + amount + ' for the app ' + app.name);

    return true;

  },

  'user/flag-app': function(appId, flag) {

    this.unblock();
    if (!this.userId) return false;

    if (!Apps.findOne(appId)) throw new Meteor.Error('no matching app', 'Cannot submit a review for an app which is not in the database');

    var userFlag = {},
        appFlag = {};

    appFlag['flags.' + this.userId] = flag;
    userFlag['flags.' + appId] = flag;

    Apps.update(appId, {$set: appFlag});
    Meteor.users.update(this.userId, {$set: userFlag});

    return true;

  },

  'user/review-app': function(appId, review) {

    this.unblock();
    if (!this.userId) return false;

    if (review.stars) {
      check(review.stars, Match.Integer);
      check(review.stars, Match.Where(function(stars) {return (0 <= stars) && (5 >= stars);}));
    }
    check(review.text, String);
    check(review.text, Match.Where(function(text) {return text.length > 0;}));

    if (!Apps.findOne(appId)) throw new Meteor.Error('no matching app', 'Cannot submit a review for an app which is not in the database');

    var userReviewObj = {};

    review.createdAt = new Date();
    userReviewObj['appReviews.' + appId] = review;

    Meteor.users.update(this.userId, {$set: userReviewObj});

    return true;

  },

  'apps/togglePrivate': function(appId) {

    this.unblock();
    var app = Apps.findOne(appId);
    if (!app || app.author !== this.userId) return false;

    Apps.update(appId, {$set: {public: !app.public}});

    return true;

  }

});
<commit_msg>newly suggested categories registered as such on insert<commit_after>/*****************************************************************************/
/* Server Only Methods */
/*****************************************************************************/
var Future = Npm.require('fibers/future');

Meteor.methods({
  'user/toggleAutoupdate': function() {

    this.unblock();
    if (!this.userId) return false;

    var user = Meteor.users.findOne(this.userId);
    return Meteor.users.update(this.userId, {
      $set: {
        autoupdateApps: !(user && user.autoupdateApps)
      }
    });

  },

  'user/uninstallApp': function(appId) {

    this.unblock();
    if (!this.userId) return false;

    var unset = {};

    unset['installedApps.' + appId] = true;
    return Meteor.users.update(this.userId, {
      $unset: unset
    });

  },

  'user/installApp': function(appId) {

    // TODO: actually install the app!
    this.unblock();
    if (!this.userId) return false;
    var app = Apps.findOne(appId);
    if (!app) return false;

    var set = {};

    set['installedApps.' + appId] = {
      version: _.last(app.versions),
      dateTime: new Date()
    };
    return Meteor.users.update(this.userId, {
      $set: set
    });

  },

  'user/updateApp': function(appId) {

    // TODO: actually update the app!
    this.unblock();
    if (!this.userId) return false;
    var app = Apps.findOne(appId);
    if (!app) return false;

    var set = {},
        userId = this.userId;

    set['installedApps.' + appId] = {
      version: app.latestVersion(),
      dateTime: new Date()
    };
    // The update is recorded only after an interval as livedata will automatically
    // inform the client, which will result in the UI updating.  We need to give
    // the "app updated" animation time to play before this happens.
    Meteor.setTimeout(function() {
      Meteor.users.update(userId, {
        $set: set
      });
    }, 3000);
    return true;

  },

  'user/updateAllApps': function() {

    // TODO: actually update the apps!
    this.unblock();
    if (!this.userId) return false;

    Genres.findIn('Updates Available', {}, {}, this).forEach(function(app) {

      Meteor.call('user/updateApp', app._id);

    });

    return true;

  },

  'user/save-app': function(app) {

    this.unblock();
    if (!this.userId) return false;

    // check(app, Schemas.AppsBase);  TODO: should we be validating here? User should be able to save in place.
    var set = {},
      setString = 'savedApp.' + (app.replacesApp || 'new');
    set[setString] = app;
    return Meteor.users.update(this.userId, {$set: set});

  },

  'user/delete-saved-app': function(appId) {

    this.unblock();
    if (!this.userId) return false;

    var unset = {},
      unsetString = 'savedApp.' + (appId || 'new');
    unset[unsetString] = true;
    return Meteor.users.update(this.userId, {$unset: unset});

  },

  'user/submit-app': function(app) {

    var fut = new Future();

    this.unblock();
    if (!this.userId) return false;
    if (this.userId !== app.author) throw new Meteor.Error('wrong author', 'Can only submit app by logged-in user');

    _.each(app.categories, function(cat) {
      if (!Categories.findOne({name: cat})) Categories.insert({
        name: cat,
        suggested: true
      });
    });

    Apps.insert(app, function(err, res) {
      console.log(err, res);
      if (err) throw new Meteor.Error(err.message);
      else fut.return(res);
    });

    return fut.wait();

  },

  'user/submit-update': function(app) {

    var fut = new Future();

    this.unblock();
    if (!this.userId) return false;
    if (this.userId !== app.author) throw new Meteor.Error('wrong author', 'Can only submit app by logged-in user');

    check(app.versions.length, Match.Where(function(l) {return l > 0;}));

    _.each(app.categories, function(cat) {
      if (!Categories.findOne({name: cat})) Categories.insert({
        name: cat,
        suggested: true
      });
    });

    Apps.insert(app, function(err, res) {
      console.log(err, res);
      if (err) throw new Meteor.Error(err.message);
      else fut.return(res);
    });

    return fut.wait();

  },

  'user/delete-app': function(appId) {

    var fut = new Future(),
        app = Apps.findOne(appId);

    this.unblock();
    if (!this.userId) return false;
    if (!app || this.userId !== app.author) throw new Meteor.Error('wrong author', 'Can only delete app by logged-in user');

    return Apps.remove(appId);
    // TODO: should we inform installed users that this app is no longer available?

  },

  'user/chip-in': function(appId, amount) {

    this.unblock();

    check(amount, Number);
    check(amount, Match.Where(function(amount) {return (0 < amount) && (40 >= amount);}));

    var user = Meteor.users.findOne(this.userId),
        app = Apps.findOne(appId);

    if (!user) throw new Meteor.Error('no authenticated user', 'Cannot chip in if user is not authenticated');
    if (!Apps.findOne(appId)) throw new Meteor.Error('no matching app', 'Cannot chip in for an app which is not in the database');

    // TODO: Actually make a payment
    console.log('User ' + user.username + ' wants to chip in ' + amount + ' for the app ' + app.name);

    return true;

  },

  'user/flag-app': function(appId, flag) {

    this.unblock();
    if (!this.userId) return false;

    if (!Apps.findOne(appId)) throw new Meteor.Error('no matching app', 'Cannot submit a review for an app which is not in the database');

    var userFlag = {},
        appFlag = {};

    appFlag['flags.' + this.userId] = flag;
    userFlag['flags.' + appId] = flag;

    Apps.update(appId, {$set: appFlag});
    Meteor.users.update(this.userId, {$set: userFlag});

    return true;

  },

  'user/review-app': function(appId, review) {

    this.unblock();
    if (!this.userId) return false;

    if (review.stars) {
      check(review.stars, Match.Integer);
      check(review.stars, Match.Where(function(stars) {return (0 <= stars) && (5 >= stars);}));
    }
    check(review.text, String);
    check(review.text, Match.Where(function(text) {return text.length > 0;}));

    if (!Apps.findOne(appId)) throw new Meteor.Error('no matching app', 'Cannot submit a review for an app which is not in the database');

    var userReviewObj = {};

    review.createdAt = new Date();
    userReviewObj['appReviews.' + appId] = review;

    Meteor.users.update(this.userId, {$set: userReviewObj});

    return true;

  },

  'apps/togglePrivate': function(appId) {

    this.unblock();
    var app = Apps.findOne(appId);
    if (!app || app.author !== this.userId) return false;

    Apps.update(appId, {$set: {public: !app.public}});

    return true;

  }

});
<|endoftext|>


<commit_before>import Ember from 'ember';
import moment from 'moment';
import Node from '../models/node';
import ENV from 'plenario-explorer/config/environment';
// import MockNetwork from '../mirage/mock-network'
import {sensorData, mockNetwork} from '../mirage/sensor-data';
/* global URI */
// const network = new MockNetwork(sensorData.curation, sensorData.nodes.data);
/**
 * Grabs and caches all dataset metadata.
 */
export default Ember.Service.extend({
  ajax: Ember.inject.service(),
  io: Ember.inject.service('socket-io'),

  queryRoot: Ember.computed('ajax', function() {
    return this.get('ajax').host;
  }),

  /**
   * For when we want to redirect the user
   * to download the data directly
   * rather than use it in the application
   */
  openInNewTab(endpoint, params) {
    const qString = URI('').addQuery(params).toString();
    window.open(`${this.queryRoot}/v1/api${endpoint}${qString}`);
  },

  camelizeHash: function (hash) {
    let normalized = {};
    for (let key in hash) {
      if (hash.hasOwnProperty(key)) {
        const normalizedKey = Ember.String.underscore(key).camelize();
        normalized[normalizedKey] = hash[key];
      }
    }
    return normalized;
  },

  // Kludge for enabling right-click
  injectExplorerData: function (route, params, obj) {
    Ember.assign(obj, {'explorerData': {'route': route, 'queryParams': params}});
    return obj;
  },

  init() {
    this._super(...arguments);
    this.set('events', this.get('ajax').request('/datasets'));
    this.set('shapes', this.get('ajax').request('/shapes'));
    //this.set('nodes', this.get('ajax').request('/sensor-networks/ArrayOfThings/nodes'));
  },

  _getMetadata(type) {
    const camelizeHash = this.camelizeHash;
    const injectExplorerData = this.injectExplorerData;
    let route = "event";
    if (type === "events") {
      route = "event";
    }
    else if (type === "shapes") {
      route = "shape";
    }
    return this.get(type).then(function (doc) {
      return doc.objects.map(v => injectExplorerData(route, undefined, camelizeHash(v)));
    }, function (reason) {
      console.log(`Failed to load ${type}. Reason: ${reason}.`);
    });
  },

  /**
   * Return all event metadata objects
   * in the cache.
   * @returns {*}
   */
  allEventMetadata() {
    return this._getMetadata('events');
  },

  /**
   * Return all shape metadata objects
   * in the cache.
   * @returns {*}
   */
  allShapeMetadata() {
    return this._getMetadata('shapes');
  },

  /**
   * Look through metadata cache
   * and return metadata of event dataset with given name
   * if available. Else return null.
   *
   * @param name
   * @returns {*}
   */
  eventMetadata(name) {
    const allEventDatasets = this._getMetadata('events');
    return this._findDataset(name, allEventDatasets);
  },

  /**
   * Look through metadata cache
   * and return metadata of shape dataset with given name
   * if available. Else return null.
   *
   * @param name
   * @returns {*}
   */
  shapeMetadata(name) {
    const allEventDatasets = this._getMetadata('shapes');
    return this._findDataset(name, allEventDatasets);
  },

  allNodeMetadata() {
    const qString = `/sensor-networks/${ENV.networkId}/nodes`;
    return this.get('ajax').request(qString)
    .then(nodeMeta =>
      {
        return nodeMeta.data.map(
          nodeRecord => Node.create({nodeGeoJSON: nodeRecord})
        );
      }
    );
  },

  getSocketForNode(networkId, nodeId, sensorList) {
    if (ENV['ember-cli-mirage'].enabled) {
      return mockNetwork.getMockSocket(nodeId);
    }
    const io = this.get('io');
    const host = 'ws://streaming.plenar.io';
    const connString = URI(host).addQuery({
      network: networkId,
      nodes: nodeId,
      sensors: sensorList.join(',')
    }).toString();
    return io.socketFor(connString);
  },

  getHistoryFor(nodeId, sensorName, type) {
    const ajax = this.get('ajax');
    const aWeekAgo = moment().subtract(7, 'days').utc().format();
    // Could already be a string.
    // If it's not, we want it to be a comma delimited list
    type = type.toString();

    const params = {
      data: {
        sensors: sensorName,
        node: nodeId,
        feature: type,
        function: 'avg',
        start_datetime: aWeekAgo
      }
    };
    const path = `/sensor-networks/${ENV.networkId}/aggregate`;
    return ajax.request(path, params).then(response => Ember.RSVP.resolve(response.data));
  },

  getSensorObservations(nodeId, networkId, feature, sensor) {
    const params = {
      data: {
        feature: feature,
        nodes: nodeId,
        start_datetime: moment().utc().subtract(1, 'hours').format(),
        end_datetime: moment().utc().format()
      }
    };
    if (sensor) {params.data.sensors = sensor;}
    const path = `/sensor-networks/${networkId}/query`;
    return this.get('ajax').request(path, params).then(response => {
      return response.data;
    });
  },

  getCurationFor(networkId) {
    if (ENV['ember-cli-mirage'].enabled) {
      return sensorData.curation;
    }
    else {
      const url = `http://sensor-curation.s3-website-us-east-1.amazonaws.com/${networkId}.json`;
      return this.get('ajax').request(url).then(response => response);
    }
  },

  _findDataset(name, datasets) {
    return datasets.then(function (dsets) {
      for (const key in dsets) {
        if (dsets.hasOwnProperty(key)) {
          const dset = dsets[key];
          if (dset.datasetName === name) {
            return dset;
          }
        }
      }
    });
  },

  /**
   * Return timeseries array
   * embedded within event metadata object.
   *
   * @param name
   * @param params
   * @param newTab
   */
  timeseries(params, newTab = false) {
    params = Ember.copy(params);
    params = this._translateFilters(params);
    const endpoint = '/detail-aggregate';

    if (newTab) {
      this.openInNewTab(endpoint, params);
    } else {
      const ts = this.get('ajax').request(endpoint, {data: params});
      return ts.then(payload => {
        return {
          series: this.prepTimeseries(payload.objects),
          count: payload.count
        };
      }, function (reason) {
        console.log(reason);
        return {error: reason};
      });
    }
  },

  /**
   Takes array of objects of the form [{count: 3, datetime: '2016-12-06'}...]
   Returns array of arrays of the form [[momentJSObject, integer]]
   */
  prepTimeseries(ts) {
    const formattedSeries = ts.map(function (timeSlice) {
      // Why exactly does `moment(timeSlice.datetime + "+0000").valueOf()` work
      // to let Highcharts accept datetimes on the x axis?
      // I don't know. Don't question it.
      return [moment(timeSlice.datetime + "+0000").valueOf(), timeSlice.count];
    });
    // The chart expects a list of series objects,
    // each with a data attribute that actually holds the timeseries.
    // So construct a list of one such object.
    // NB: name: 'Count' determines chart tooltip.
    // Easy to override if desired.
    return [{data: formattedSeries, name: 'Count'}];
  },

  /**
   * Return grid response geoJSON.
   *
   * @param name
   * @param params
   * @param newTab
   */
  grid(params, newTab = false) {
    params = Ember.copy(params);
    params = this._translateFilters(params);
    const endpoint = '/grid';
    if (newTab) {
      this.openInNewTab(endpoint, params);
    }
    else {
      const grid = this.get('ajax').request(endpoint, {data: params});
      return grid.then(function (payload) {
        return payload;
      }, function (reason) {
        console.log(reason);
      });
    }
  },

  _translateFilters(params) {
    params = Ember.copy(params);
    if (!params.filters) {
      // No filters to transform.
      return params;
    }
    let filterAPIFormatted = {};
    const filterHashes = JSON.parse(params.filters);
    for (const filter of filterHashes) {
      if (filter.op === '=') {
        filterAPIFormatted[filter.field] = filter.val;
      }
      else {
        const APIOperator = this.get('operatorMap')[filter.op];
        filterAPIFormatted[`${filter.field}__${APIOperator}`] = filter.val;
      }
    }
    delete params['filters'];
    Ember.assign(params, filterAPIFormatted);
    return params;
  },

  operatorMap: {
    '=': 'eq',
    '>': 'gt',
    '>=': 'ge',
    '<': 'lt',
    '<=': 'le',
    '!=': 'ne',
    'LIKE': 'ilike',
    'IN': 'in'
  },

  /**
   * Return list of event metadata objects
   * that are within the given time and space bounding box.
   * @param params
   */
  eventCandidates(params) {
    const candidates = this.get('ajax').request('/datasets', {data: params});
    return candidates.then(doc => {
      return doc.objects.map(v => {
        return this.injectExplorerData("event", params, this.camelizeHash(v));
      });
    }, function (reason) {
      console.log(`Event candidate query failed: ${reason}`);
      return {error: reason};
    });
  },

  /**
   * Return list of shape metadata objects
   * that are within the given space bounding box.
   * @param params
   */
  shapeSubsets(params) {
    const subsets = this.get('ajax').request('/shapes', {data: params});
    return subsets.then(doc => {
      return doc.objects.map(v => {
        return this.injectExplorerData("shape", params, this.camelizeHash(v));
      });
    }, function (reason) {
      console.log(`Shape subset query failed: ${reason}`);
      return {error: reason};
    });
  },

  /**
   * return geoJSON of shape dataset
   * @param name
   * @param params
   * @param newTab
   */
  rawShape(name, params, newTab = false) {
    const endpoint = `/shapes/${name}`;
    if (newTab) {
      this.openInNewTab(endpoint, params);
    }
    else {
      const shape = this.get('ajax').request(endpoint, {data: params});
      return shape.then(payload => {
        return payload;
      }, reason => {
        console.log(reason);
      });
    }
  },

  /**
   * Return CSV or GeoJSON of events
   *
   * @param name
   * @param params
   * @param newTab
   */
  rawEvents(params, newTab = false) {
    params = Ember.copy(params);
    params = this._translateFilters(params);
    const endpoint = '/detail';

    if (newTab) {
      this.openInNewTab(endpoint, params);
    } else {
      const events = this.get('ajax').request(endpoint, {data: params});
      return events.then(payload => {
        // Don't currently call this from any route.
        // Would be useful for putting markers on a map.
        return payload;
      }, function (reason) {
        console.log(reason);
      });
    }
  },

  /**
   * Create a job for a large CSV or GeoJSON download of events
   *
   * @param name
   * @param params
   * @param newTab
   */
  dataDump(params, newTab = false) {
    params = Ember.copy(params);
    params = this._translateFilters(params);
    const endpoint = '/datadump';

    if (newTab) {
      this.openInNewTab(endpoint, params);
    } else {
      const events = this.get('ajax').request(endpoint, {data: params});
      return events;
    }
  },

  /**
   * Analogue of dataDump for sensor data.
   *
   */
  sensorDownload(params) {
    // networkId, nodeId, features, startDatetime, endDatetime
    const endpoint = `${ENV.host}/v1/api/sensor-networks/${params.networkId}/download`;
    const queryParams = {
      nodes: params.nodeId,
      features: params.features.join(','),
      start_datetime: params.startDatetime,
      end_datetime: params.endDatetime
    };
    // return this.get('ajax').request(endpoint, {data: queryParams});
    var query = endpoint + '?' + URI('').addQuery(queryParams).toString();
    console.log('[services.query] sensorDownload.query: ' + query);
    return window.open(query);
  },

  /**
   * Fetch the large CSV or GeoJSON download of events
   *
   * @param name
   * @param params
   * @param newTab
   */
  getDataDump(ticket) {
    const endpoint = `http://plenar.io/v1/api/datadump/${ticket}`;
    window.open(endpoint);
  },

  /**
   * Return job information
   *
   * @param ticket
   */
  job(ticket) {
    const endpoint = 'http://plenar.io/v1/api/jobs/'+ticket;
    const job = this.get('ajax').request(endpoint);
    return job;
  },

});
<commit_msg>Fix improper use of computer property<commit_after>import Ember from 'ember';
import moment from 'moment';
import Node from '../models/node';
import ENV from 'plenario-explorer/config/environment';
// import MockNetwork from '../mirage/mock-network'
import {sensorData, mockNetwork} from '../mirage/sensor-data';
/* global URI */
// const network = new MockNetwork(sensorData.curation, sensorData.nodes.data);
/**
 * Grabs and caches all dataset metadata.
 */
export default Ember.Service.extend({
  ajax: Ember.inject.service(),
  io: Ember.inject.service('socket-io'),

  queryRoot: Ember.computed('ajax', function() {
    return this.get('ajax').host;
  }),

  /**
   * For when we want to redirect the user
   * to download the data directly
   * rather than use it in the application
   */
  openInNewTab(endpoint, params) {
    const qString = URI('').addQuery(params).toString();
    window.open(`${this.get('queryRoot')}/v1/api${endpoint}${qString}`);
  },

  camelizeHash: function (hash) {
    let normalized = {};
    for (let key in hash) {
      if (hash.hasOwnProperty(key)) {
        const normalizedKey = Ember.String.underscore(key).camelize();
        normalized[normalizedKey] = hash[key];
      }
    }
    return normalized;
  },

  // Kludge for enabling right-click
  injectExplorerData: function (route, params, obj) {
    Ember.assign(obj, {'explorerData': {'route': route, 'queryParams': params}});
    return obj;
  },

  init() {
    this._super(...arguments);
    this.set('events', this.get('ajax').request('/datasets'));
    this.set('shapes', this.get('ajax').request('/shapes'));
    //this.set('nodes', this.get('ajax').request('/sensor-networks/ArrayOfThings/nodes'));
  },

  _getMetadata(type) {
    const camelizeHash = this.camelizeHash;
    const injectExplorerData = this.injectExplorerData;
    let route = "event";
    if (type === "events") {
      route = "event";
    }
    else if (type === "shapes") {
      route = "shape";
    }
    return this.get(type).then(function (doc) {
      return doc.objects.map(v => injectExplorerData(route, undefined, camelizeHash(v)));
    }, function (reason) {
      console.log(`Failed to load ${type}. Reason: ${reason}.`);
    });
  },

  /**
   * Return all event metadata objects
   * in the cache.
   * @returns {*}
   */
  allEventMetadata() {
    return this._getMetadata('events');
  },

  /**
   * Return all shape metadata objects
   * in the cache.
   * @returns {*}
   */
  allShapeMetadata() {
    return this._getMetadata('shapes');
  },

  /**
   * Look through metadata cache
   * and return metadata of event dataset with given name
   * if available. Else return null.
   *
   * @param name
   * @returns {*}
   */
  eventMetadata(name) {
    const allEventDatasets = this._getMetadata('events');
    return this._findDataset(name, allEventDatasets);
  },

  /**
   * Look through metadata cache
   * and return metadata of shape dataset with given name
   * if available. Else return null.
   *
   * @param name
   * @returns {*}
   */
  shapeMetadata(name) {
    const allEventDatasets = this._getMetadata('shapes');
    return this._findDataset(name, allEventDatasets);
  },

  allNodeMetadata() {
    const qString = `/sensor-networks/${ENV.networkId}/nodes`;
    return this.get('ajax').request(qString)
    .then(nodeMeta =>
      {
        return nodeMeta.data.map(
          nodeRecord => Node.create({nodeGeoJSON: nodeRecord})
        );
      }
    );
  },

  getSocketForNode(networkId, nodeId, sensorList) {
    if (ENV['ember-cli-mirage'].enabled) {
      return mockNetwork.getMockSocket(nodeId);
    }
    const io = this.get('io');
    const host = 'ws://streaming.plenar.io';
    const connString = URI(host).addQuery({
      network: networkId,
      nodes: nodeId,
      sensors: sensorList.join(',')
    }).toString();
    return io.socketFor(connString);
  },

  getHistoryFor(nodeId, sensorName, type) {
    const ajax = this.get('ajax');
    const aWeekAgo = moment().subtract(7, 'days').utc().format();
    // Could already be a string.
    // If it's not, we want it to be a comma delimited list
    type = type.toString();

    const params = {
      data: {
        sensors: sensorName,
        node: nodeId,
        feature: type,
        function: 'avg',
        start_datetime: aWeekAgo
      }
    };
    const path = `/sensor-networks/${ENV.networkId}/aggregate`;
    return ajax.request(path, params).then(response => Ember.RSVP.resolve(response.data));
  },

  getSensorObservations(nodeId, networkId, feature, sensor) {
    const params = {
      data: {
        feature: feature,
        nodes: nodeId,
        start_datetime: moment().utc().subtract(1, 'hours').format(),
        end_datetime: moment().utc().format()
      }
    };
    if (sensor) {params.data.sensors = sensor;}
    const path = `/sensor-networks/${networkId}/query`;
    return this.get('ajax').request(path, params).then(response => {
      return response.data;
    });
  },

  getCurationFor(networkId) {
    if (ENV['ember-cli-mirage'].enabled) {
      return sensorData.curation;
    }
    else {
      const url = `http://sensor-curation.s3-website-us-east-1.amazonaws.com/${networkId}.json`;
      return this.get('ajax').request(url).then(response => response);
    }
  },

  _findDataset(name, datasets) {
    return datasets.then(function (dsets) {
      for (const key in dsets) {
        if (dsets.hasOwnProperty(key)) {
          const dset = dsets[key];
          if (dset.datasetName === name) {
            return dset;
          }
        }
      }
    });
  },

  /**
   * Return timeseries array
   * embedded within event metadata object.
   *
   * @param name
   * @param params
   * @param newTab
   */
  timeseries(params, newTab = false) {
    params = Ember.copy(params);
    params = this._translateFilters(params);
    const endpoint = '/detail-aggregate';

    if (newTab) {
      this.openInNewTab(endpoint, params);
    } else {
      const ts = this.get('ajax').request(endpoint, {data: params});
      return ts.then(payload => {
        return {
          series: this.prepTimeseries(payload.objects),
          count: payload.count
        };
      }, function (reason) {
        console.log(reason);
        return {error: reason};
      });
    }
  },

  /**
   Takes array of objects of the form [{count: 3, datetime: '2016-12-06'}...]
   Returns array of arrays of the form [[momentJSObject, integer]]
   */
  prepTimeseries(ts) {
    const formattedSeries = ts.map(function (timeSlice) {
      // Why exactly does `moment(timeSlice.datetime + "+0000").valueOf()` work
      // to let Highcharts accept datetimes on the x axis?
      // I don't know. Don't question it.
      return [moment(timeSlice.datetime + "+0000").valueOf(), timeSlice.count];
    });
    // The chart expects a list of series objects,
    // each with a data attribute that actually holds the timeseries.
    // So construct a list of one such object.
    // NB: name: 'Count' determines chart tooltip.
    // Easy to override if desired.
    return [{data: formattedSeries, name: 'Count'}];
  },

  /**
   * Return grid response geoJSON.
   *
   * @param name
   * @param params
   * @param newTab
   */
  grid(params, newTab = false) {
    params = Ember.copy(params);
    params = this._translateFilters(params);
    const endpoint = '/grid';
    if (newTab) {
      this.openInNewTab(endpoint, params);
    }
    else {
      const grid = this.get('ajax').request(endpoint, {data: params});
      return grid.then(function (payload) {
        return payload;
      }, function (reason) {
        console.log(reason);
      });
    }
  },

  _translateFilters(params) {
    params = Ember.copy(params);
    if (!params.filters) {
      // No filters to transform.
      return params;
    }
    let filterAPIFormatted = {};
    const filterHashes = JSON.parse(params.filters);
    for (const filter of filterHashes) {
      if (filter.op === '=') {
        filterAPIFormatted[filter.field] = filter.val;
      }
      else {
        const APIOperator = this.get('operatorMap')[filter.op];
        filterAPIFormatted[`${filter.field}__${APIOperator}`] = filter.val;
      }
    }
    delete params['filters'];
    Ember.assign(params, filterAPIFormatted);
    return params;
  },

  operatorMap: {
    '=': 'eq',
    '>': 'gt',
    '>=': 'ge',
    '<': 'lt',
    '<=': 'le',
    '!=': 'ne',
    'LIKE': 'ilike',
    'IN': 'in'
  },

  /**
   * Return list of event metadata objects
   * that are within the given time and space bounding box.
   * @param params
   */
  eventCandidates(params) {
    const candidates = this.get('ajax').request('/datasets', {data: params});
    return candidates.then(doc => {
      return doc.objects.map(v => {
        return this.injectExplorerData("event", params, this.camelizeHash(v));
      });
    }, function (reason) {
      console.log(`Event candidate query failed: ${reason}`);
      return {error: reason};
    });
  },

  /**
   * Return list of shape metadata objects
   * that are within the given space bounding box.
   * @param params
   */
  shapeSubsets(params) {
    const subsets = this.get('ajax').request('/shapes', {data: params});
    return subsets.then(doc => {
      return doc.objects.map(v => {
        return this.injectExplorerData("shape", params, this.camelizeHash(v));
      });
    }, function (reason) {
      console.log(`Shape subset query failed: ${reason}`);
      return {error: reason};
    });
  },

  /**
   * return geoJSON of shape dataset
   * @param name
   * @param params
   * @param newTab
   */
  rawShape(name, params, newTab = false) {
    const endpoint = `/shapes/${name}`;
    if (newTab) {
      this.openInNewTab(endpoint, params);
    }
    else {
      const shape = this.get('ajax').request(endpoint, {data: params});
      return shape.then(payload => {
        return payload;
      }, reason => {
        console.log(reason);
      });
    }
  },

  /**
   * Return CSV or GeoJSON of events
   *
   * @param name
   * @param params
   * @param newTab
   */
  rawEvents(params, newTab = false) {
    params = Ember.copy(params);
    params = this._translateFilters(params);
    const endpoint = '/detail';

    if (newTab) {
      this.openInNewTab(endpoint, params);
    } else {
      const events = this.get('ajax').request(endpoint, {data: params});
      return events.then(payload => {
        // Don't currently call this from any route.
        // Would be useful for putting markers on a map.
        return payload;
      }, function (reason) {
        console.log(reason);
      });
    }
  },

  /**
   * Create a job for a large CSV or GeoJSON download of events
   *
   * @param name
   * @param params
   * @param newTab
   */
  dataDump(params, newTab = false) {
    params = Ember.copy(params);
    params = this._translateFilters(params);
    const endpoint = '/datadump';

    if (newTab) {
      this.openInNewTab(endpoint, params);
    } else {
      const events = this.get('ajax').request(endpoint, {data: params});
      return events;
    }
  },

  /**
   * Analogue of dataDump for sensor data.
   *
   */
  sensorDownload(params) {
    // networkId, nodeId, features, startDatetime, endDatetime
    const endpoint = `${ENV.host}/v1/api/sensor-networks/${params.networkId}/download`;
    const queryParams = {
      nodes: params.nodeId,
      features: params.features.join(','),
      start_datetime: params.startDatetime,
      end_datetime: params.endDatetime
    };
    // return this.get('ajax').request(endpoint, {data: queryParams});
    var query = endpoint + '?' + URI('').addQuery(queryParams).toString();
    console.log('[services.query] sensorDownload.query: ' + query);
    return window.open(query);
  },

  /**
   * Fetch the large CSV or GeoJSON download of events
   *
   * @param name
   * @param params
   * @param newTab
   */
  getDataDump(ticket) {
    const endpoint = `http://plenar.io/v1/api/datadump/${ticket}`;
    window.open(endpoint);
  },

  /**
   * Return job information
   *
   * @param ticket
   */
  job(ticket) {
    const endpoint = 'http://plenar.io/v1/api/jobs/'+ticket;
    const job = this.get('ajax').request(endpoint);
    return job;
  },

});
<|endoftext|>


<commit_before>/*jslint node: true */

'use strict';

var log = require('winston');
var redis = require('redis');
var url = require('url');

var client

if (process.env.REDISCLOUD_URL) {
    var urlParse = url.parse(process.env.REDISCLOUD_URL);
    client = redis.createClient(urlParse.port, urlParse.hostname, { no_ready_check: true });
    client.auth(urlParse.auth.split(':')[1]);
} else {
    client = redis.createClient(6379, '127.0.0.1', { no_ready_check: true });
}

client.on('error', function (err) {
    log.error('Redis connection error: ', err.message);
});

client.on('ready', function callback() {
    log.info('Connected to Redis!');
});

exports.getClient = function () {
    return client;
};<commit_msg>Search<commit_after>/*jslint node: true */

'use strict';

var log = require('winston');
var redis = require('redis');
var url = require('url');

var client;

if (process.env.REDISCLOUD_URL) {
    var urlParse = url.parse(process.env.REDISCLOUD_URL);
    client = redis.createClient(urlParse.port, urlParse.hostname, { no_ready_check: true });
    client.auth(urlParse.auth.split(':')[1]);
} else {
    client = redis.createClient(6379, '127.0.0.1', { no_ready_check: true });
}

client.on('error', function (err) {
    log.error('Redis connection error: ', err.message);
});

client.on('ready', function callback() {
    log.info('Connected to Redis!');
});

exports.getClient = function () {
    return client;
};<|endoftext|>


<commit_before>let existy = function existy(obj) {
  return obj != null;
};

let isDefined = function (obj) {
  let flag = true;
  if (obj === null || typeof obj === 'undefined') return false;
  return flag;
};

let isNumber = function isNumber(obj) {
  if (!isDefined(obj)) return false;
  return (obj.constructor === Number);
};

let isString = function (obj) {
  if (!isDefined(obj)) return false;
  return (obj.constructor === String);
};

let isFunction = function (obj) {
  if (!isDefined(obj)) return false;
  return (obj.constructor === Function);
};

let isExistJQueryEle = function ($ele) {
  return !(!$ele || $ele.length <= 0);
};

let not = function not(func) {
  return function (object) {
    return !func(object);
  };
};

let each = function each(dataCanLoop, func, context) {
  if (!(Array.isArray(dataCanLoop) || isString(dataCanLoop))) throw new TypeError('dataCanLoop parameter type of each() must be Array or String.');

  var _context = (existy(context)) ? context : null;

  for (var i = 0, max = dataCanLoop.length; i < max; i++) {
    func.call(_context, dataCanLoop[i]);
  }
};

let best = function best(conditionFunc, array) {
  if (!isFunction(conditionFunc)) throw new TypeError('conditionFunc parameter type of best() must be Function.');
  if (!Array.isArray(array)) throw new TypeError('array parameter type of best() must be Array.');

  return array.reduce(function (previousValue, currentValue) {
    return conditionFunc(previousValue, currentValue) ? previousValue : currentValue;
  });
};

let rest = function rest(array, beginIndex) {
  if (!Array.isArray(array)) throw new TypeError('array parameter type of rest() must be Array.');

  var begin = (!existy(beginIndex)) ? 1 : beginIndex;
  return Array.prototype.slice.call(array, begin);
};

let pipeline = function pipeline(seed /* args */) {
  var restArgs = rest(Array.prototype.slice.call(arguments));

  return restArgs.reduce(function (prev, current) {
    return current(prev);
  }, seed);
};

let notSingleEle = not(function ($ele) {
  return $ele.length === 1;
});

export {
  existy,
  isDefined,
  isNumber,
  isString,
  isFunction,
  isExistJQueryEle,
  not,
  each,
  best,
  rest,
  pipeline,
  notSingleEle
};<commit_msg>Update : add singleEle function in util<commit_after>let existy = function existy(obj) {
  return obj != null;
};

let isDefined = function (obj) {
  let flag = true;
  if (obj === null || typeof obj === 'undefined') return false;
  return flag;
};

let isNumber = function isNumber(obj) {
  if (!isDefined(obj)) return false;
  return (obj.constructor === Number);
};

let isString = function (obj) {
  if (!isDefined(obj)) return false;
  return (obj.constructor === String);
};

let isFunction = function (obj) {
  if (!isDefined(obj)) return false;
  return (obj.constructor === Function);
};

let isExistJQueryEle = function ($ele) {
  return !(!$ele || $ele.length <= 0);
};

let not = function not(func) {
  return function (object) {
    return !func(object);
  };
};

let each = function each(dataCanLoop, func, context) {
  if (!(Array.isArray(dataCanLoop) || isString(dataCanLoop))) throw new TypeError('dataCanLoop parameter type of each() must be Array or String.');

  var _context = (existy(context)) ? context : null;

  for (var i = 0, max = dataCanLoop.length; i < max; i++) {
    func.call(_context, dataCanLoop[i]);
  }
};

let best = function best(conditionFunc, array) {
  if (!isFunction(conditionFunc)) throw new TypeError('conditionFunc parameter type of best() must be Function.');
  if (!Array.isArray(array)) throw new TypeError('array parameter type of best() must be Array.');

  return array.reduce(function (previousValue, currentValue) {
    return conditionFunc(previousValue, currentValue) ? previousValue : currentValue;
  });
};

let rest = function rest(array, beginIndex) {
  if (!Array.isArray(array)) throw new TypeError('array parameter type of rest() must be Array.');

  var begin = (!existy(beginIndex)) ? 1 : beginIndex;
  return Array.prototype.slice.call(array, begin);
};

let pipeline = function pipeline(seed /* args */) {
  var restArgs = rest(Array.prototype.slice.call(arguments));

  return restArgs.reduce(function (prev, current) {
    return current(prev);
  }, seed);
};

let singleEle = function singleEle($ele) {
  return $ele.length === 1;
};

let notSingleEle = not(singleEle);

export {
  existy,
  isDefined,
  isNumber,
  isString,
  isFunction,
  isExistJQueryEle,
  not,
  each,
  best,
  rest,
  pipeline,
  singleEle,
  notSingleEle
};<|endoftext|>


<commit_before>String.prototype.format = function() {
  var args = arguments;
  return this.replace(/{(\d+)}/g, function(match, number) { return typeof args[number] != 'undefined' ? args[number] : match; });
};

var instanceID;
var agentID;
console.log(window.location.href);
var httpURL;
var wsURL;

function createInstance(user, project, version, onloadCB) {
  httpURL = 'https://' + RUNTIME_URL + '/api/v1/' + user + '/' + project + '/' + version;
  wsURL = 'wss://' + RUNTIME_URL + '/api/v1/' + user + '/' + project + '/' + version;
  var oReq = new XMLHttpRequest();
  oReq.open('PUT', httpURL + '?' + 'scope=app', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);
  oReq.onload = function() {
    instanceID = JSON.parse(oReq.responseText).instance.instance_id;
    onloadCB(instanceID);
  };
  oReq.onerror = function() {
    alert('error while creating instance');
  };
  oReq.send();
}

function destroyInstance(onloadCB) {
  console.log('delete');
  //clearInterval( idUpdate );
  var oReq = new XMLHttpRequest();
  oReq.open('DELETE', httpURL + '/' + instanceID, true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);
  oReq.send();
  oReq.onload = onloadCB;
}

function createAgent(behavior, knowledge, onloadCB, onErrorCB) {
  var oReq = new XMLHttpRequest();
  oReq.open('PUT', httpURL + '/' + instanceID + '/agents', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);
  oReq.onload = function() {
    onloadCB();
  };
  oReq.onerror = function() {
    onErrorCB();
  };
  var params = {};
  params.behavior = behavior;
  params.knowledge = knowledge;
  oReq.send(JSON.stringify(params));
}

function update() {
  var oReq = new XMLHttpRequest();
  oReq.open('POST', httpURL + '/' + instanceID + '/update', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);
  oReq.onload = function() {
  };
  oReq.onerror = function() {
    alert('error while updating the instance');
  };
  oReq.send('{"time":0.5,"ts":' + new Date().getTime() + '}');
}

function cancel(requestID, agentID, params) {
  sendCancel(requestID);
}

function getAgentKnowledge(agentID, onloadCB) {
  var oReq = new XMLHttpRequest();
  oReq.open('GET', httpURL + '/' + instanceID + '/agents/' + agentID + '/knowledge', true);
  oReq.setRequestHeader('Content-type', 'application/json');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);
  oReq.onload = function() {
    onloadCB(JSON.parse(oReq.responseText).knowledge);
  };
  oReq.onerror = function(e) {
    alert('error ' + e.target.status + ' while retrieving the knowledge');
  };

  oReq.send();

}

function updateAgentKnowledge(agentID, destination, value, onloadCB) {
  var oReq = new XMLHttpRequest();
  oReq.open('POST', httpURL + '/' + instanceID + '/agents/' + agentID + '/knowledge', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);

  oReq.onload = function() {
    onloadCB();
  };
  var j = {};
  j[destination] = value;
  oReq.send(JSON.stringify(j));

}

function updateInstanceKnowledge(destination, value, onloadCB) {
  var oReq = new XMLHttpRequest();
  oReq.open('POST', httpURL + '/' + instanceID + '/instanceKnowledge', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);

  oReq.onload = function() {
    onloadCB();
  };
  var j = {};
  j[destination] = value;
  oReq.send(JSON.stringify(j));

}

function updateGlobalKnowledge(destination, value, onloadCB) {
  var oReq = new XMLHttpRequest();
  oReq.open('POST', httpURL + '/' + simID + '/globalKnowledge', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);

  oReq.onload = function() {
    onloadCB();
  };
  var j = {};
  j[destination] = value;
  oReq.send(JSON.stringify(j));

}

function sendSuccess(requestID, jsonString) {
  var oReq = new XMLHttpRequest();
  oReq.open('POST', httpURL + '/' + instanceID + '/actions/' + requestID + '/success', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);

  oReq.send(jsonString);
}

function sendFailure(requestID, jsonString) {
  var oReq = new XMLHttpRequest();
  oReq.open('POST', httpURL + '/' + instanceID + '/actions/' + requestID + '/failure', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);
  oReq.send(jsonString);
}

function sendCancel(requestID) {
  var oReq = new XMLHttpRequest();
  oReq.open('POST', httpURL + '/' + instanceID + '/actions/' + requestID + '/cancelation', true);
  oReq.setRequestHeader('content-type', 'application/html; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);
  oReq.send();
}

function registerAction(jsonString, onloadCB) {
  var oReq = new XMLHttpRequest();
  oReq.open('PUT', httpURL + '/' + instanceID + '/actions', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);
  oReq.onload = function() {
    console.log('action', JSON.parse(jsonString).name, 'registred');
    onloadCB();
  };
  oReq.onerror = function() {
    alert('error while registering action ' + JSON.parse(jsonString).name);
  };
  oReq.send(jsonString);
}

function doUpdate() {
  idUpdate = setInterval(update, 500);
}

function doWS() {
  var wsUrlRoute = wsURL + '/' + instanceID + '/websockets';
  console.log('WS Connexion on', wsUrlRoute + ':');
  if (wsUrlRoute) {
    console.log('requesting WS connexion...');
    ws = new WebSocket(wsUrlRoute);
    ws.onmessage = function(evt) {
      if (evt.data != 'ping') {
        var jsonEvt = JSON.parse(evt.data);
        console.log('WS data:', evt.data);
        window[jsonEvt.call](jsonEvt.requestId , jsonEvt.agentID, jsonEvt.input);
      }
      else {
        console.log('ping');
      }
      ws.send('Done');
    };
    ws.onopen = function() {
      ws.send('socket open');
      console.log('WS Connexion open', ws);
    };
    ws.onclose = function() {
      console.log('WS Connexion closed', ws);
    };
    ws.onerror = function() {
      console.log('WS Connexion error', ws);
    };
  }
}
<commit_msg>Updating craft API url<commit_after>String.prototype.format = function() {
  var args = arguments;
  return this.replace(/{(\d+)}/g, function(match, number) { return typeof args[number] != 'undefined' ? args[number] : match; });
};

var instanceID;
var agentID;
console.log(window.location.href);
var httpURL;
var wsURL;

function createInstance(user, project, version, onloadCB) {
  httpURL = 'https://' + RUNTIME_URL + '/v1/' + user + '/' + project + '/' + version;
  wsURL = 'wss://' + RUNTIME_URL + '/v1/' + user + '/' + project + '/' + version;
  var oReq = new XMLHttpRequest();
  oReq.open('PUT', httpURL + '?' + 'scope=app', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);
  oReq.onload = function() {
    instanceID = JSON.parse(oReq.responseText).instance.instance_id;
    onloadCB(instanceID);
  };
  oReq.onerror = function() {
    alert('error while creating instance');
  };
  oReq.send();
}

function destroyInstance(onloadCB) {
  console.log('delete');
  //clearInterval( idUpdate );
  var oReq = new XMLHttpRequest();
  oReq.open('DELETE', httpURL + '/' + instanceID, true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);
  oReq.send();
  oReq.onload = onloadCB;
}

function createAgent(behavior, knowledge, onloadCB, onErrorCB) {
  var oReq = new XMLHttpRequest();
  oReq.open('PUT', httpURL + '/' + instanceID + '/agents', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);
  oReq.onload = function() {
    onloadCB();
  };
  oReq.onerror = function() {
    onErrorCB();
  };
  var params = {};
  params.behavior = behavior;
  params.knowledge = knowledge;
  oReq.send(JSON.stringify(params));
}

function update() {
  var oReq = new XMLHttpRequest();
  oReq.open('POST', httpURL + '/' + instanceID + '/update', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);
  oReq.onload = function() {
  };
  oReq.onerror = function() {
    alert('error while updating the instance');
  };
  oReq.send('{"time":0.5,"ts":' + new Date().getTime() + '}');
}

function cancel(requestID, agentID, params) {
  sendCancel(requestID);
}

function getAgentKnowledge(agentID, onloadCB) {
  var oReq = new XMLHttpRequest();
  oReq.open('GET', httpURL + '/' + instanceID + '/agents/' + agentID + '/knowledge', true);
  oReq.setRequestHeader('Content-type', 'application/json');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);
  oReq.onload = function() {
    onloadCB(JSON.parse(oReq.responseText).knowledge);
  };
  oReq.onerror = function(e) {
    alert('error ' + e.target.status + ' while retrieving the knowledge');
  };

  oReq.send();

}

function updateAgentKnowledge(agentID, destination, value, onloadCB) {
  var oReq = new XMLHttpRequest();
  oReq.open('POST', httpURL + '/' + instanceID + '/agents/' + agentID + '/knowledge', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);

  oReq.onload = function() {
    onloadCB();
  };
  var j = {};
  j[destination] = value;
  oReq.send(JSON.stringify(j));

}

function updateInstanceKnowledge(destination, value, onloadCB) {
  var oReq = new XMLHttpRequest();
  oReq.open('POST', httpURL + '/' + instanceID + '/instanceKnowledge', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);

  oReq.onload = function() {
    onloadCB();
  };
  var j = {};
  j[destination] = value;
  oReq.send(JSON.stringify(j));

}

function updateGlobalKnowledge(destination, value, onloadCB) {
  var oReq = new XMLHttpRequest();
  oReq.open('POST', httpURL + '/' + simID + '/globalKnowledge', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);

  oReq.onload = function() {
    onloadCB();
  };
  var j = {};
  j[destination] = value;
  oReq.send(JSON.stringify(j));

}

function sendSuccess(requestID, jsonString) {
  var oReq = new XMLHttpRequest();
  oReq.open('POST', httpURL + '/' + instanceID + '/actions/' + requestID + '/success', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);

  oReq.send(jsonString);
}

function sendFailure(requestID, jsonString) {
  var oReq = new XMLHttpRequest();
  oReq.open('POST', httpURL + '/' + instanceID + '/actions/' + requestID + '/failure', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);
  oReq.send(jsonString);
}

function sendCancel(requestID) {
  var oReq = new XMLHttpRequest();
  oReq.open('POST', httpURL + '/' + instanceID + '/actions/' + requestID + '/cancelation', true);
  oReq.setRequestHeader('content-type', 'application/html; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);
  oReq.send();
}

function registerAction(jsonString, onloadCB) {
  var oReq = new XMLHttpRequest();
  oReq.open('PUT', httpURL + '/' + instanceID + '/actions', true);
  oReq.setRequestHeader('content-type', 'application/json; charset=utf-8');
  oReq.setRequestHeader('accept', '');
  oReq.setRequestHeader('X-Craft-Ai-App-Id', TUTO_APP_ID);
  oReq.setRequestHeader('X-Craft-Ai-App-Secret', TUTO_APP_SECRET);
  oReq.onload = function() {
    console.log('action', JSON.parse(jsonString).name, 'registred');
    onloadCB();
  };
  oReq.onerror = function() {
    alert('error while registering action ' + JSON.parse(jsonString).name);
  };
  oReq.send(jsonString);
}

function doUpdate() {
  idUpdate = setInterval(update, 500);
}

function doWS() {
  var wsUrlRoute = wsURL + '/' + instanceID + '/websockets';
  console.log('WS Connexion on', wsUrlRoute + ':');
  if (wsUrlRoute) {
    console.log('requesting WS connexion...');
    ws = new WebSocket(wsUrlRoute);
    ws.onmessage = function(evt) {
      if (evt.data != 'ping') {
        var jsonEvt = JSON.parse(evt.data);
        console.log('WS data:', evt.data);
        window[jsonEvt.call](jsonEvt.requestId , jsonEvt.agentID, jsonEvt.input);
      }
      else {
        console.log('ping');
      }
      ws.send('Done');
    };
    ws.onopen = function() {
      ws.send('socket open');
      console.log('WS Connexion open', ws);
    };
    ws.onclose = function() {
      console.log('WS Connexion closed', ws);
    };
    ws.onerror = function() {
      console.log('WS Connexion error', ws);
    };
  }
}
<|endoftext|>


<commit_before>import React from 'react'
import {
  BrowserRouter as Router,
  Route,
  Switch,
//  NavLink,
//  Redirect
} from 'react-router-dom'
import {HashLink as Link} from 'react-router-hash-link'

import * as StepData from './data/StepData'
import QuizPage from './containers/QuizPage'
import Header from './components/Header'

const HomePage = () => (

welcome

    <Link to={`/${StepData.basic[0]}`} className='ui button'>
    start
    </Link>

)

const ReportPage = () => (
report
)

const Root = () => {

  const QuizApp = ({ match: {params} }) => (
    <QuizPage quizID={params.quizID} />
  )

  return(
    <Router basename='/llscanner'>

        <Switch>
          <Route exact path='/' component={HomePage} />
          <Route path='/report' component={ReportPage} />
          <Route path='/:quizID?' component={QuizApp} />
        </Switch>

    </Router>
  )
}

export default Root
<commit_msg>polish welcome, report page<commit_after>import React from 'react'
import {
  BrowserRouter as Router,
  Route,
  Switch,
//  NavLink,
//  Redirect
} from 'react-router-dom'
import {HashLink as Link} from 'react-router-hash-link'

import * as StepData from './data/StepData'
import QuizPage from './containers/QuizPage'
import Header from './components/Header'

const HomePage = () => (

某段歡迎標題

某段歡迎文字

    <Link to={`/${StepData.basic[0]}`} className='ui icon labeled right button'>
      開始

    </Link>

)

const ReportPage = () => (

掃描報告

某段敘述文字

    <Link to={`/${StepData.basic[0]}`} className='ui icon labeled right button'>
      再玩一次

    </Link>

)

const Root = () => {

  const QuizApp = ({ match: {params} }) => (
    <QuizPage quizID={params.quizID} />
  )

  return(
    <Router basename='/llscanner'>

        <Switch>
          <Route exact path='/' component={HomePage} />
          <Route path='/report' component={ReportPage} />
          <Route path='/:quizID?' component={QuizApp} />
        </Switch>

    </Router>
  )
}

export default Root
<|endoftext|>


<commit_before>// @flow

import type { Storage } from './storage';
import Op, { UUID } from 'swarm-ron';

// A wrapper for convenience and to work with pending
// ops in an efficient way.
class PendingOps {
  storage: Storage;
  ops: Array<string>;
  _onIdle: void | (() => void);
  period: number;
  timer: TimeoutID;

  constructor(storage: Storage, ops: string[]): PendingOps {
    this.storage = storage;
    this.ops = ops;
    return this;
  }

  setIdlePeriod(period: number): void {
    this.period = period;
    this.check();
  }

  onIdle(f?: () => void): void {
    this._onIdle = f;
  }

  check = (): void => {
    clearTimeout(this.timer);
    if (this.ops.length && this._onIdle) this._onIdle();
    if (this.period > 0) {
      this.timer = setTimeout(this.check, this.period);
    }
  };

  /*:: @@iterator(): Iterator<string> { return ({}: any); } */

  // $FlowFixMe - computed property
  [Symbol.iterator](): Iterator<string> {
    return this.ops[Symbol.iterator]();
  }

  push(frame: string): Promise<void> {
    this.ops.push(frame);
    return this.flush();
  }

  see(ack: UUID): Promise<void> {
    let i = -1;
    for (const _old of this.ops) {
      i++;
      const old = Op.fromString(_old);
      if (!old) throw new Error(`malformed op: '${_old}'`);

      if (old.event.gt(ack)) {
        this.ops = this.ops.slice(i + 1);
        break;
      }
    }
    if (i === this.ops.length - 1) this.ops = [];

    return this.flush();
  }

  flush(): Promise<void> {
    return this.storage.set(PendingOps.KEY, JSON.stringify(this.ops));
  }

  get length(): number {
    return this.ops.length;
  }

  static async read(storage: Storage): Promise<PendingOps> {
    const pending = await storage.get(PendingOps.KEY);
    return new PendingOps(storage, JSON.parse(pending || '[]'));
  }

  static KEY: string;
}

PendingOps.KEY = '__pending__';

export default PendingOps;
<commit_msg>client: include regenerator runtime<commit_after>// @flow

import regeneratorRuntime from 'regenerator-runtime'; // for async/await work flow

import type { Storage } from './storage';
import Op, { UUID } from 'swarm-ron';

// A wrapper for convenience and to work with pending
// ops in an efficient way.
class PendingOps {
  storage: Storage;
  ops: Array<string>;
  _onIdle: void | (() => void);
  period: number;
  timer: TimeoutID;

  constructor(storage: Storage, ops: string[]): PendingOps {
    this.storage = storage;
    this.ops = ops;
    return this;
  }

  setIdlePeriod(period: number): void {
    this.period = period;
    this.check();
  }

  onIdle(f?: () => void): void {
    this._onIdle = f;
  }

  check = (): void => {
    clearTimeout(this.timer);
    if (this.ops.length && this._onIdle) this._onIdle();
    if (this.period > 0) {
      this.timer = setTimeout(this.check, this.period);
    }
  };

  /*:: @@iterator(): Iterator<string> { return ({}: any); } */

  // $FlowFixMe - computed property
  [Symbol.iterator](): Iterator<string> {
    return this.ops[Symbol.iterator]();
  }

  push(frame: string): Promise<void> {
    this.ops.push(frame);
    return this.flush();
  }

  see(ack: UUID): Promise<void> {
    let i = -1;
    for (const _old of this.ops) {
      i++;
      const old = Op.fromString(_old);
      if (!old) throw new Error(`malformed op: '${_old}'`);

      if (old.event.gt(ack)) {
        this.ops = this.ops.slice(i + 1);
        break;
      }
    }
    if (i === this.ops.length - 1) this.ops = [];

    return this.flush();
  }

  flush(): Promise<void> {
    return this.storage.set(PendingOps.KEY, JSON.stringify(this.ops));
  }

  get length(): number {
    return this.ops.length;
  }

  static async read(storage: Storage): Promise<PendingOps> {
    const pending = await storage.get(PendingOps.KEY);
    return new PendingOps(storage, JSON.parse(pending || '[]'));
  }

  static KEY: string;
}

PendingOps.KEY = '__pending__';

export default PendingOps;
<|endoftext|>


<commit_before>'use strict'

let Transition = require('./errors').Transition

let log = require('../util/log')('tasks/download')
let http = require('../util/http')
let noop = require('../util/noop')

let rimraf = require('../promised/rimraf')

let CaveStore = require('../stores/cave-store')
let CredentialsStore = require('../stores/credentials-store')

function ensure (predicate, reason) {
  if (!predicate) {
    throw new Transition({
      to: 'find-upload',
      reason
    })
  }
}

async function start (opts) {
  let id = opts.id
  let onprogress = opts.onprogress || noop
  let logger = opts.logger
  let emitter = opts.emitter

  let cave = await CaveStore.find(id)

  ensure(cave.upload_id, 'need upload id')
  ensure(cave.uploads, 'need cached uploads')

  let upload = cave.uploads[cave.upload_id]
  ensure(upload, 'need upload in upload cache')

  // Get download URL
  let client = CredentialsStore.get_current_user()
  let url

  try {
    url = (await (cave.key
      ? client.download_upload_with_key(cave.key.id, cave.upload_id)
      : client.download_upload(cave.upload_id)
    )).url
  } catch (e) {
    if (e.errors) {
      if (e.errors[0] === 'invalid upload') {
        throw new Transition({
          to: 'find-upload',
          reason: 'upload-gone'
        })
      }
    }
    throw e
  }

  let parsed = require('url').parse(url)
  log(opts, `downloading from ${parsed.hostname}`)

  let dest = CaveStore.archive_path(upload)

  emitter.on('cancelled', async (e) => {
    let archive_path =
    log(opts, `killed the butler with a wrench in the living room`)
    log(opts, `wiping ${dest}`)
    await rimraf(dest, {
      disableGlob: true
    })
  })

  await http.request({ url, onprogress, logger, dest, emitter })
}

module.exports = { start }
<commit_msg>:shirt:<commit_after>'use strict'

let Transition = require('./errors').Transition

let log = require('../util/log')('tasks/download')
let http = require('../util/http')
let noop = require('../util/noop')

let rimraf = require('../promised/rimraf')

let CaveStore = require('../stores/cave-store')
let CredentialsStore = require('../stores/credentials-store')

function ensure (predicate, reason) {
  if (!predicate) {
    throw new Transition({
      to: 'find-upload',
      reason
    })
  }
}

async function start (opts) {
  let id = opts.id
  let onprogress = opts.onprogress || noop
  let logger = opts.logger
  let emitter = opts.emitter

  let cave = await CaveStore.find(id)

  ensure(cave.upload_id, 'need upload id')
  ensure(cave.uploads, 'need cached uploads')

  let upload = cave.uploads[cave.upload_id]
  ensure(upload, 'need upload in upload cache')

  // Get download URL
  let client = CredentialsStore.get_current_user()
  let url

  try {
    url = (await (cave.key
      ? client.download_upload_with_key(cave.key.id, cave.upload_id)
      : client.download_upload(cave.upload_id)
    )).url
  } catch (e) {
    if (e.errors) {
      if (e.errors[0] === 'invalid upload') {
        throw new Transition({
          to: 'find-upload',
          reason: 'upload-gone'
        })
      }
    }
    throw e
  }

  let parsed = require('url').parse(url)
  log(opts, `downloading from ${parsed.hostname}`)

  let dest = CaveStore.archive_path(upload)

  emitter.on('cancelled', async (e) => {
    log(opts, `killed the butler with a wrench in the living room`)
    log(opts, `wiping ${dest}`)
    await rimraf(dest, {
      disableGlob: true
    })
  })

  await http.request({ url, onprogress, logger, dest, emitter })
}

module.exports = { start }
<|endoftext|>