<commit_before>describe('ClusterCtrl Spec', function() {

  beforeEach(module('app'));

  var ClusterCtrl, scope, $httpBackend, createController,
      ClusterApiService, UserApiService, toaster;

  beforeEach(inject(function($injector, $controller, $rootScope) {
    scope = $rootScope.$new();
    $httpBackend = $injector.get('$httpBackend');
    AuthService = $injector.get('AuthService');
    ClusterApiService = $injector.get('ClusterApiService');
    UserApiService = $injector.get('UserApiService');
    toaster = $injector.get('toaster');

    createController = function(params) {
      return $controller('ClusterCtrl', params);
    };
  }));

  beforeEach(function() {
    $httpBackend.when('GET', ClusterApiService.ENDPOINT +
                           'name?clusterRoute=jack%2Ffoo&token=123&userId=456').respond({
      id: 'ABC',
      owner: { id: '456' },
      subreddits: [],
      admins: []
    });
    $httpBackend.when('GET', ClusterApiService.ENDPOINT +
                           'listing?clusterId=ABC&token=123&userId=456').respond({});
  });

  afterEach(function() {
    $httpBackend.verifyNoOutstandingExpectation();
    $httpBackend.verifyNoOutstandingRequest();
  });

  var ctrl;
  beforeEach(function() {
    ctrl = createController({
      $scope: scope,
      $routeParams: {
        token: '123', user_id: '456', user_name: 'jack', last_active: 'some date',
        username: 'jack', clusterName: 'foo'
      }
    });
  });

  it('calls getCluster', function() {
    $httpBackend.expectGET(ClusterApiService.ENDPOINT +
                           'name?clusterRoute=jack%2Ffoo&token=123&userId=456').respond({
      id: 'ABC',
      owner: { id: '456' },
      subreddits: [],
      admins: []
    });
    $httpBackend.flush();
    expect(scope.cluster.id).toEqual('ABC');
  });

  it('sets canEdit to true if the cluster\'s owner is the logged in user', function() {
    $httpBackend.when('GET', ClusterApiService.ENDPOINT +
                           'name?clusterRoute=jack%2Ffoo&token=123&userId=456').respond({
      id: 'ABC',
      owner: { id: '456' }
    });
    $httpBackend.flush();
    expect(scope.canEdit).toEqual(true);
  });

  it('sets canEdit to true if the owner is an admin', function() {
    ctrl = createController({
      $scope: scope,
      $routeParams: {
        token: '123', user_id: '987', user_name: 'jack', last_active: 'some date',
        username: 'olly', clusterName: 'foo'
      }
    });
    $httpBackend.when('GET', ClusterApiService.ENDPOINT +
                           'name?clusterRoute=olly%2Ffoo&token=123&userId=987').respond({
      id: 'ABC',
      owner: { id: '456' },
      admins: [ { id: '987' } ],
      subreddits: []
    });
    $httpBackend.when('GET', ClusterApiService.ENDPOINT +
                           'listing?clusterId=ABC&token=123&userId=987').respond({ sorted: [1] });
    $httpBackend.flush();
    expect(scope.canEdit).toEqual(true);
  });

  it('gets the cluster listings', function() {
    $httpBackend.expectGET(ClusterApiService.ENDPOINT +
                           'listing?clusterId=ABC&token=123&userId=456').respond({ sorted: [1] });
    $httpBackend.flush();
    expect(scope.listings.sorted).toEqual([1]);
  });

  describe('editing the list of admins', function() {
    it('makes an update request with the right details', function() {
      $httpBackend.flush();
      scope.tagAdmins = [ { text: 'oj206' } ];
      $httpBackend.expectGET(UserApiService.ENDPOINT + '/name?name=oj206&token=123&userId=456').respond({ id: '987' });
      $httpBackend.expectPOST(ClusterApiService.ENDPOINT + 'update?userId=456&token=123&clusterId=ABC', {
        admins: ['987']
      }).respond({});
      scope.editAdmin();
      $httpBackend.flush();
    });
  });

  describe('editing the list of subreddits', function() {
    it('makes an update with the right details and busts the cache', function() {
      $httpBackend.flush();
      scope.tagSubreddits = [ { text: 'vim' } ];
      $httpBackend.expectPOST(ClusterApiService.ENDPOINT + 'update?userId=456&token=123&clusterId=ABC', {
        subreddits: ['vim']
      }).respond({});
      $httpBackend.expectGET(ClusterApiService.ENDPOINT + 'cache_bust?clusterId=ABC&token=123&userId=456').respond({});
      scope.editSubreddits();
      $httpBackend.flush();
    });
    it('notifies with toaster', function() {
      spyOn(toaster, 'pop');
      $httpBackend.flush();
      scope.tagSubreddits = [ { text: 'vim' } ];
      $httpBackend.whenPOST(ClusterApiService.ENDPOINT + 'update?userId=456&token=123&clusterId=ABC', {
        subreddits: ['vim']
      }).respond({});
      $httpBackend.whenGET(ClusterApiService.ENDPOINT + 'cache_bust?clusterId=ABC&token=123&userId=456').respond({});
      scope.editSubreddits();
      $httpBackend.flush();
      expect(toaster.pop).toHaveBeenCalledWith('success', 'Subreddits updated', '');
    });
  });
});
<commit_msg>remove extra test<commit_after>describe('ClusterCtrl Spec', function() {

  beforeEach(module('app'));

  var ClusterCtrl, scope, $httpBackend, createController,
      ClusterApiService, UserApiService, toaster;

  beforeEach(inject(function($injector, $controller, $rootScope) {
    scope = $rootScope.$new();
    $httpBackend = $injector.get('$httpBackend');
    AuthService = $injector.get('AuthService');
    ClusterApiService = $injector.get('ClusterApiService');
    UserApiService = $injector.get('UserApiService');
    toaster = $injector.get('toaster');

    createController = function(params) {
      return $controller('ClusterCtrl', params);
    };
  }));

  beforeEach(function() {
    $httpBackend.when('GET', ClusterApiService.ENDPOINT +
                           'name?clusterRoute=jack%2Ffoo&token=123&userId=456').respond({
      id: 'ABC',
      owner: { id: '456' },
      subreddits: [],
      admins: []
    });
    $httpBackend.when('GET', ClusterApiService.ENDPOINT +
                           'listing?clusterId=ABC&token=123&userId=456').respond({});
  });

  afterEach(function() {
    $httpBackend.verifyNoOutstandingExpectation();
    $httpBackend.verifyNoOutstandingRequest();
  });

  var ctrl;
  beforeEach(function() {
    ctrl = createController({
      $scope: scope,
      $routeParams: {
        token: '123', user_id: '456', user_name: 'jack', last_active: 'some date',
        username: 'jack', clusterName: 'foo'
      }
    });
  });

  it('calls getCluster', function() {
    $httpBackend.expectGET(ClusterApiService.ENDPOINT +
                           'name?clusterRoute=jack%2Ffoo&token=123&userId=456').respond({
      id: 'ABC',
      owner: { id: '456' },
      subreddits: [],
      admins: []
    });
    $httpBackend.flush();
    expect(scope.cluster.id).toEqual('ABC');
  });

  it('sets canEdit to true if the cluster\'s owner is the logged in user', function() {
    $httpBackend.when('GET', ClusterApiService.ENDPOINT +
                           'name?clusterRoute=jack%2Ffoo&token=123&userId=456').respond({
      id: 'ABC',
      owner: { id: '456' }
    });
    $httpBackend.flush();
    expect(scope.canEdit).toEqual(true);
  });

  it('sets canEdit to true if the owner is an admin', function() {
    ctrl = createController({
      $scope: scope,
      $routeParams: {
        token: '123', user_id: '987', user_name: 'jack', last_active: 'some date',
        username: 'olly', clusterName: 'foo'
      }
    });
    $httpBackend.when('GET', ClusterApiService.ENDPOINT +
                           'name?clusterRoute=olly%2Ffoo&token=123&userId=987').respond({
      id: 'ABC',
      owner: { id: '456' },
      admins: [ { id: '987' } ],
      subreddits: []
    });
    $httpBackend.when('GET', ClusterApiService.ENDPOINT +
                           'listing?clusterId=ABC&token=123&userId=987').respond({ sorted: [1] });
    $httpBackend.flush();
    expect(scope.canEdit).toEqual(true);
  });

  it('gets the cluster listings', function() {
    $httpBackend.expectGET(ClusterApiService.ENDPOINT +
                           'listing?clusterId=ABC&token=123&userId=456').respond({ sorted: [1] });
    $httpBackend.flush();
    expect(scope.listings.sorted).toEqual([1]);
  });

  describe('editing the list of admins', function() {
    it('makes an update request with the right details', function() {
      $httpBackend.flush();
      scope.tagAdmins = [ { text: 'oj206' } ];
      $httpBackend.expectGET(UserApiService.ENDPOINT + '/name?name=oj206&token=123&userId=456').respond({ id: '987' });
      $httpBackend.expectPOST(ClusterApiService.ENDPOINT + 'update?userId=456&token=123&clusterId=ABC', {
        admins: ['987']
      }).respond({});
      scope.editAdmin();
      $httpBackend.flush();
    });
  });

  describe('editing the list of subreddits', function() {
    it('makes an update with the right details and busts the cache', function() {
      $httpBackend.flush();
      scope.tagSubreddits = [ { text: 'vim' } ];
      $httpBackend.expectPOST(ClusterApiService.ENDPOINT + 'update?userId=456&token=123&clusterId=ABC', {
        subreddits: ['vim']
      }).respond({});
      $httpBackend.expectGET(ClusterApiService.ENDPOINT + 'cache_bust?clusterId=ABC&token=123&userId=456').respond({});
      scope.editSubreddits();
      $httpBackend.flush();
    });
  });
});
<|endoftext|>


<commit_before>//Dependencies
var express = require('express');
var util = require('util');

//Express App
var server = express();

//Custom logging function
var log = function () {
  'use strict';
  var pre = server.get('log prefix') || '';
  console.log(pre + '[server] ' + util.format.apply(this, arguments));
};

/**
 * Configures the server
 * @param {{port, environment, logPrefix}} config
 */
var init = function (config) {
  'use strict';

  if(server.get('initiated') && config == server.get('initial config')){
    return this;
  }

  if(typeof config !== 'object'){
    config = {};
  }else{
    if(typeof config.logPrefix === 'string'){
      server.set('log prefix', config.logPrefix);
    }
  }

  console.log('');
  log('Initiating server...');

  //Some server config values
  server.set('env', config.environment || process.env.NODE_ENV || 'development');
  server.set('port', config.port || process.env.PORT || -1);

  if(server.get('port') === -1){
    throw new Error('Port must be provided through config object (config.port) or environment variable ($PORT).');
  }

  //Filter Middleware
  server.use(express.bodyParser());
  server.use(express.methodOverride());

  //Development Only Middleware
  if ('development' === server.get('env')) {
    //Minimal Logger
    server.use(function(req, res, next){
      log('%s %s', req.method, req.url);
      next();
    });
    //Comment out the following line if you have the livereload browser extension installed
    server.use( require('connect-livereload')() );
    server.use( express.static( 'client/app' ) );
    server.use( express.static( 'client/.tmp' ) );
  }
  //Production-Only Middleware
  if ('production' === server.get('env')) {
    server.use( express.static( 'public' ) );
  }

  // Server logic/routes go here

  server.set('initiated', true);
  server.set('initial config', config);
  return this;
};

/**
 * Starts server if not running. If it hasn't been initiated yet, it does so with default settings
 * @param {!function} readyCallback
 */
var start = function (readyCallback) {
  'use strict';
  if (!this.serverInstance) {
    if(!server.get('initiated')){
      init();
    }
    log('Starting server...');
    this.serverInstance = server.listen(server.get('port'), function () {
      console.log('');
      log('Server running on port %d in %s mode', server.get('port'), server.get('env'));
      if (readyCallback) {
        readyCallback();
      }
    });
  }
  return this;
};

/**
 * Stops server if running
 */
var stop = function (stopAndExit) {
  'use strict';
  if(this && this.serverInstance){
    log('Stopping server...');
    this.serverInstance.close();
    if(stopAndExit){
      process.exit(0);
    }
    delete this.serverInstance;
  }
  return this;
};

process.on('exit', function(){
  'use strict';
  stop(true);
});
process.on('stopServer', stop);

exports.init  = exports.configure = init;
exports.start = exports.open      = start;
exports.stop  = exports.close     = stop;<commit_msg>Test modification<commit_after>//Dependencies
var express = require('express');
var util    = require('util');

//Express App
var server = express();

//Custom logging function
var log = function () {
  'use strict';
  var pre = server.get('log prefix') || '';
  console.log(pre + '[server] ' + util.format.apply(this, arguments));
};

/**
 * Configures the server
 * @param {{port, environment, logPrefix}} config
 */
var init = function (config) {
  'use strict';

  if(server.get('initiated') && config == server.get('initial config')){
    return this;
  }

  if(typeof config !== 'object'){
    config = {};
  }else{
    if(typeof config.logPrefix === 'string'){
      server.set('log prefix', config.logPrefix);
    }
  }

  console.log('');
  log('Initiating server...');

  //Some server config values
  server.set('env', config.environment || process.env.NODE_ENV || 'development');
  server.set('port', config.port || process.env.PORT || -1);

  if(server.get('port') === -1){
    throw new Error('Port must be provided through config object (config.port) or environment variable ($PORT).');
  }

  //Filter Middleware
  server.use(express.bodyParser());
  server.use(express.methodOverride());

  //Development Only Middleware
  if ('development' === server.get('env')) {
    //Minimal Logger
    server.use(function(req, res, next){
      log('%s %s', req.method, req.url);
      next();
    });
    //Comment out the following line if you have the livereload browser extension installed
    server.use( require('connect-livereload')() );
    server.use( express.static( 'client/app' ) );
    server.use( express.static( 'client/.tmp' ) );
  }
  //Production-Only Middleware
  if ('production' === server.get('env')) {
    server.use( express.static( 'public' ) );
  }

  // Server logic/routes go here

  server.set('initiated', true);
  server.set('initial config', config);
  return this;
};

/**
 * Starts server if not running. If it hasn't been initiated yet, it does so with default settings
 * @param {!function} readyCallback
 */
var start = function (readyCallback) {
  'use strict';
  if (!this.serverInstance) {
    if(!server.get('initiated')){
      init();
    }
    log('Starting server...');
    this.serverInstance = server.listen(server.get('port'), function () {
      console.log('');
      log('Server running on port %d in %s mode', server.get('port'), server.get('env'));
      if (readyCallback) {
        readyCallback();
      }
    });
  }
  return this;
};

/**
 * Stops server if running
 */
var stop = function (stopAndExit) {
  'use strict';
  if(this && this.serverInstance){
    log('Stopping server...');
    this.serverInstance.close();
    if(stopAndExit){
      process.exit(0);
    }
    delete this.serverInstance;
  }
  return this;
};

process.on('exit', function(){
  'use strict';
  stop(true);
});
process.on('stopServer', stop);

exports.init  = exports.configure = init;
exports.start = exports.open      = start;
exports.stop  = exports.close     = stop;<|endoftext|>


<commit_before>// metaConfirm.js

var exports = module.exports = {};

var request = require('request');

function findInPage(complete_url, searchType, searchForTag, searchContent, cb) {

    request(complete_url, function (error, response, body) {

            if (error) {
                cb(error, null);

            }
            else {

                var specificTag = '<meta ' + searchType + '="' + searchForTag + '" content="' + searchContent + '">';

                var cnt = body.split(specificTag).length;

                if (cnt === 1) {
                    cb(null, false);
                }
                else {
                    cb(null, true);

                }
            }
        }
    );

}

exports.FindInPage = findInPage;
<commit_msg>return Error if null url parameter<commit_after>// metaConfirm.js

var exports = module.exports = {};

var request = require('request');

function findInPage(complete_url, searchType, searchForTag, searchContent, cb) {

    if (complete_url === null)
    {
        cb(new Error("url cannot be null"), null);
        return;
    }

    request(complete_url, function (error, response, body) {

            if (error) {
                cb(error, null);

            }
            else {

                var specificTag = '<meta ' + searchType + '="' + searchForTag + '" content="' + searchContent + '">';

                var cnt = body.split(specificTag).length;

                if (cnt === 1) {
                    cb(null, false);
                }
                else {
                    cb(null, true);

                }
            }
        }
    );

}

exports.FindInPage = findInPage;
<|endoftext|>


<commit_before>/* eslint-disable global-require */

const express = require('express');
const path = require('path');
const chalk = require('chalk');
const webpack = require('webpack');
const webpackMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const config = require('../../config/webpack.config');
const log = require('debug')('flint');

module.exports = (app) => {
  const admin = express();

  admin.use(require('./routes/auth')());
  admin.use('/api', require('./api')(app));

  if (process.env.BUILD_DASHBOARD) {
    const compiler = webpack(config);
    const middleware = webpackMiddleware(compiler, {
      publicPath: '/',
      stats: {
        colors: true,
        hash: false,
        timings: true,
        chunks: false,
        chunkModules: false,
        modules: false,
      },
    });

    admin.use(middleware);
    admin.use(webpackHotMiddleware(compiler));

    admin.get('*', (req, res) => {
      res.write(middleware.fileSystem.readFileSync(path.join(__dirname, '..', '..', 'admin', 'index.html')));
      res.end();
    });

    log(`${chalk.cyan('[App: Admin]')} initialized in Dev mode.`);
  } else {
    const STATIC_PATH = path.join(__dirname, '..', '..', 'admin');
    const STATIC_OPTS = {
      maxAge: 31536000000, // One year
    };

    admin.use(express.static(STATIC_PATH, STATIC_OPTS));

    admin.get('*', (req, res) => {
      res.sendFile(path.join(__dirname, '..', '..', 'admin', 'index.html'));
    });

    log(`${chalk.gray('[App: Admin]')} initialized.`);
  }

  return admin;
};
<commit_msg>Fix test coverage with webpack dev server<commit_after>/* eslint-disable global-require */

const express = require('express');
const path = require('path');
const chalk = require('chalk');
const webpack = require('webpack');
const webpackMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const config = require('../../config/webpack.config');
const log = require('debug')('flint');

module.exports = (app) => {
  const admin = express();

  admin.use(require('./routes/auth')());
  admin.use('/api', require('./api')(app));

  /* istanbul ignore next */
  if (process.env.BUILD_DASHBOARD) {
    const compiler = webpack(config);
    const middleware = webpackMiddleware(compiler, {
      publicPath: '/',
      stats: {
        colors: true,
        hash: false,
        timings: true,
        chunks: false,
        chunkModules: false,
        modules: false,
      },
    });

    admin.use(middleware);
    admin.use(webpackHotMiddleware(compiler));

    admin.get('*', (req, res) => {
      res.write(middleware.fileSystem.readFileSync(path.join(__dirname, '..', '..', 'admin', 'index.html')));
      res.end();
    });

    log(`${chalk.cyan('[App: Admin]')} initialized in Dev mode.`);
  } else {
    const STATIC_PATH = path.join(__dirname, '..', '..', 'admin');
    const STATIC_OPTS = {
      maxAge: 31536000000, // One year
    };

    admin.use(express.static(STATIC_PATH, STATIC_OPTS));

    admin.get('*', (req, res) => {
      res.sendFile(path.join(__dirname, '..', '..', 'admin', 'index.html'));
    });

    log(`${chalk.gray('[App: Admin]')} initialized.`);
  }

  return admin;
};
<|endoftext|>


<commit_before>'use strict';
import Joi from 'joi';
import Boom from 'boom';

import db from '../db/';
import { putFile as putFileToS3, removeLocalFile } from '../s3/utils';
import {
  ProjectNotFoundError,
  ScenarioNotFoundError,
  FileExistsError,
  DataValidationError,
  ProjectStatusError
} from '../utils/errors';
import { parseFormData } from '../utils/utils';

export default [
  {
    path: '/projects/{projId}/scenarios/{scId}/source-data',
    method: 'POST',
    config: {
      validate: {
        params: {
          projId: Joi.number(),
          scId: Joi.number()
        }
      },
      payload: {
        maxBytes: 1 * Math.pow(1024, 3), // 1GB
        output: 'stream',
        parse: false,
        allow: 'multipart/form-data'
      }
    },
    handler: (request, reply) => {
      const projId = parseInt(request.params.projId);
      const scId = parseInt(request.params.scId);

      // Check if project exists and is still in setup phase.
      db('projects')
        .select('*')
        .where('id', projId)
        .first()
        .then(project => {
          if (!project) throw new ProjectNotFoundError();
          if (project.status !== 'pending') throw new ProjectStatusError('Project no longer in the setup phase. Source data can not be uploaded');
        })
        .then(() => db('scenarios')
          .select('id')
          .where('id', projId)
          .first()
          .then(scenario => { if (!scenario) throw new ScenarioNotFoundError(); })
        )
        .then(() => parseFormData(request.raw.req))
        .then(result => {
          if (!result.fields['source-type']) {
            throw new DataValidationError('"source-type" is required');
          }

          if (!result.fields['source-name']) {
            throw new DataValidationError('"source-name" is required');
          }

          let sourceType = result.fields['source-type'][0];
          let sourceName = result.fields['source-name'][0];

          if (['poi', 'road-network'].indexOf(sourceName) === -1) {
            throw new DataValidationError(`"source-name" must be one of [poi, road-network]`);
          }

          switch (sourceType) {
            case 'file':
              if (!result.files.file) {
                throw new DataValidationError('"file" is required');
              }

              // With poi source the subtype is required.
              let subtype = result.fields['subtype'][0];
              if (sourceName === 'poi' && !subtype) {
                throw new DataValidationError('"subtype" is required for source "poi"');
              }

              let file = result.files.file[0];
              let fileName;

              if (subtype) {
                fileName = `${sourceType}_${subtype}_${Date.now()}`;
              } else {
                fileName = `${sourceType}_${Date.now()}`;
              }

              let filePath = `scenario-${scId}/${fileName}`;

              // Upsert source.
              return db('scenarios_source_data')
                .select('id')
                .where('scenario_id', scId)
                .where('name', sourceName)
                .first()
                .then(source => {
                  if (source) {
                    return db('scenarios_source_data')
                      .update({type: 'file'})
                      .where('id', source.id);
                  } else {
                    return db('scenarios_source_data')
                      .insert({
                        project_id: projId,
                        scenario_id: scId,
                        name: sourceName,
                        type: 'file'
                      });
                  }
                })
                // Check if the file exists.
                .then(() => {
                  let query = db('scenarios_files')
                    .select('id')
                    .where('scenario_id', scId)
                    .where('type', sourceName);

                  if (subtype) {
                    query = query.where('subtype', subtype);
                  }

                  return query;
                })
                .then(files => {
                  if (files.length) { throw new FileExistsError(); }
                })
                // Upload to S3.
                .then(() => putFileToS3(filePath, file.path))
                // Insert into database.
                .then(() => {
                  let data = {
                    name: fileName,
                    type: sourceName,
                    path: filePath,
                    project_id: projId,
                    scenario_id: scId,
                    created_at: (new Date()),
                    updated_at: (new Date())
                  };

                  if (subtype) {
                    data.subtype = subtype;
                  }

                  return db('scenarios_files')
                    .returning(['id', 'name', 'type', 'subtype', 'path', 'created_at'])
                    .insert(data)
                    .then(insertResponse => insertResponse[0])
                    .then(insertResponse => db('scenarios').update({updated_at: (new Date())}).where('id', scId).then(() => insertResponse))
                    .then(insertResponse => db('projects').update({updated_at: (new Date())}).where('id', projId).then(() => insertResponse));
                })
                // Delete temp file.
                .then(insertResponse => removeLocalFile(file.path, true).then(() => insertResponse))
                .then(insertResponse => reply(Object.assign({}, insertResponse, {
                  sourceType,
                  sourceName
                })))
                .catch(err => {
                  // Delete temp file in case of error. Re-throw error to continue.
                  file && removeLocalFile(file.path, true);
                  throw err;
                });
            case 'osm':
              throw new DataValidationError(`"osm" type not implemented`);
              // break;
            default:
              throw new DataValidationError(`"source-type" must be one of [osm, file]`);
          }
        })
        .catch(ProjectNotFoundError, e => reply(Boom.notFound(e.message)))
        .catch(ScenarioNotFoundError, e => reply(Boom.notFound(e.message)))
        .catch(FileExistsError, e => reply(Boom.conflict(e.message)))
        .catch(DataValidationError, e => reply(Boom.badRequest(e.message)))
        .catch(err => {
          console.log('err', err);
          reply(Boom.badImplementation(err));
        });
    }
  }
];
<commit_msg>Fix filename creation<commit_after>'use strict';
import Joi from 'joi';
import Boom from 'boom';

import db from '../db/';
import { putFile as putFileToS3, removeLocalFile } from '../s3/utils';
import {
  ProjectNotFoundError,
  ScenarioNotFoundError,
  FileExistsError,
  DataValidationError,
  ProjectStatusError
} from '../utils/errors';
import { parseFormData } from '../utils/utils';

export default [
  {
    path: '/projects/{projId}/scenarios/{scId}/source-data',
    method: 'POST',
    config: {
      validate: {
        params: {
          projId: Joi.number(),
          scId: Joi.number()
        }
      },
      payload: {
        maxBytes: 1 * Math.pow(1024, 3), // 1GB
        output: 'stream',
        parse: false,
        allow: 'multipart/form-data'
      }
    },
    handler: (request, reply) => {
      const projId = parseInt(request.params.projId);
      const scId = parseInt(request.params.scId);

      // Check if project exists and is still in setup phase.
      db('projects')
        .select('*')
        .where('id', projId)
        .first()
        .then(project => {
          if (!project) throw new ProjectNotFoundError();
          if (project.status !== 'pending') throw new ProjectStatusError('Project no longer in the setup phase. Source data can not be uploaded');
        })
        .then(() => db('scenarios')
          .select('id')
          .where('id', projId)
          .first()
          .then(scenario => { if (!scenario) throw new ScenarioNotFoundError(); })
        )
        .then(() => parseFormData(request.raw.req))
        .then(result => {
          if (!result.fields['source-type']) {
            throw new DataValidationError('"source-type" is required');
          }

          if (!result.fields['source-name']) {
            throw new DataValidationError('"source-name" is required');
          }

          let sourceType = result.fields['source-type'][0];
          let sourceName = result.fields['source-name'][0];

          if (['poi', 'road-network'].indexOf(sourceName) === -1) {
            throw new DataValidationError(`"source-name" must be one of [poi, road-network]`);
          }

          switch (sourceType) {
            case 'file':
              if (!result.files.file) {
                throw new DataValidationError('"file" is required');
              }

              // With poi source the subtype is required.
              let subtype = result.fields['subtype'][0];
              if (sourceName === 'poi' && !subtype) {
                throw new DataValidationError('"subtype" is required for source "poi"');
              }

              let file = result.files.file[0];
              let fileName;

              if (subtype) {
                fileName = `${sourceName}_${subtype}_${Date.now()}`;
              } else {
                fileName = `${sourceName}_${Date.now()}`;
              }

              let filePath = `scenario-${scId}/${fileName}`;

              // Upsert source.
              return db('scenarios_source_data')
                .select('id')
                .where('scenario_id', scId)
                .where('name', sourceName)
                .first()
                .then(source => {
                  if (source) {
                    return db('scenarios_source_data')
                      .update({type: 'file'})
                      .where('id', source.id);
                  } else {
                    return db('scenarios_source_data')
                      .insert({
                        project_id: projId,
                        scenario_id: scId,
                        name: sourceName,
                        type: 'file'
                      });
                  }
                })
                // Check if the file exists.
                .then(() => {
                  let query = db('scenarios_files')
                    .select('id')
                    .where('scenario_id', scId)
                    .where('type', sourceName);

                  if (subtype) {
                    query = query.where('subtype', subtype);
                  }

                  return query;
                })
                .then(files => {
                  if (files.length) { throw new FileExistsError(); }
                })
                // Upload to S3.
                .then(() => putFileToS3(filePath, file.path))
                // Insert into database.
                .then(() => {
                  let data = {
                    name: fileName,
                    type: sourceName,
                    path: filePath,
                    project_id: projId,
                    scenario_id: scId,
                    created_at: (new Date()),
                    updated_at: (new Date())
                  };

                  if (subtype) {
                    data.subtype = subtype;
                  }

                  return db('scenarios_files')
                    .returning(['id', 'name', 'type', 'subtype', 'path', 'created_at'])
                    .insert(data)
                    .then(insertResponse => insertResponse[0])
                    .then(insertResponse => db('scenarios').update({updated_at: (new Date())}).where('id', scId).then(() => insertResponse))
                    .then(insertResponse => db('projects').update({updated_at: (new Date())}).where('id', projId).then(() => insertResponse));
                })
                // Delete temp file.
                .then(insertResponse => removeLocalFile(file.path, true).then(() => insertResponse))
                .then(insertResponse => reply(Object.assign({}, insertResponse, {
                  sourceType,
                  sourceName
                })))
                .catch(err => {
                  // Delete temp file in case of error. Re-throw error to continue.
                  file && removeLocalFile(file.path, true);
                  throw err;
                });
            case 'osm':
              throw new DataValidationError(`"osm" type not implemented`);
              // break;
            default:
              throw new DataValidationError(`"source-type" must be one of [osm, file]`);
          }
        })
        .catch(ProjectNotFoundError, e => reply(Boom.notFound(e.message)))
        .catch(ScenarioNotFoundError, e => reply(Boom.notFound(e.message)))
        .catch(FileExistsError, e => reply(Boom.conflict(e.message)))
        .catch(DataValidationError, e => reply(Boom.badRequest(e.message)))
        .catch(err => {
          console.log('err', err);
          reply(Boom.badImplementation(err));
        });
    }
  }
];
<|endoftext|>


<commit_before>//----------------------------------------------------------------------------------------------------------------------
// Main Angular application.
//
// @module app.js
//----------------------------------------------------------------------------------------------------------------------

angular.module("battlemap", ['ngResource', 'battlemap.controllers', 'monospaced.mousewheel'])
	.config(['$locationProvider', function($locationProvider) {
		$locationProvider.html5Mode(true);
	}])
	.config(['$routeProvider', function($routeProvider) {
		$routeProvider
			.when('/', {templateUrl: '/partials/list_maps.html',   controller: 'ListMapsCtrl'})
			.when('/maps/:mapid', {templateUrl: '/partials/map.html',   controller: 'ViewMapCtrl'})
			.when('/maps/:mapid/edit', {templateUrl: '/partials/map.html',   controller: 'EditMapCtrl'})
			.otherwise({redirectTo: '/'});
	}])
	.factory('MapSocket', ['$q', '$rootScope', function($q, $rootScope){

		var nextReplyToId = 0;
		var requests = {};
		var mapUrl = false;
		var ws = false;
		var listeners = {};

		var connectingDefer = false;
		var connectingResolved = true;

		var connectMap = function(mapObj){
			if(mapObj.websocketUrl == mapUrl){
				console.log('connection already established', mapObj.websocketUrl);
				return connectingDefer.promise;
			}

			if(ws){
				ws.close();
				if(! connectingResolved){
					$rootScope.$apply(connectingDefer.reject("connection closed"));
				}
			}

			connectingDefer = $q.defer();

			console.log('connecting to map ws', mapObj.websocketUrl, ws ? true : false);

			mapUrl = mapObj.websocketUrl;
			ws = new WebSocket(mapUrl);

			ws.onopen = function(ev){
				console.log('resolving connection', $rootScope);
				connectingResolved = true;
				$rootScope.$apply(connectingDefer.resolve(ev));
				console.log('emiting open socket');
				$rootScope.$apply($rootScope.$emit('mapsocket_open'));
			};

			ws.onmessage = function(ev){
				onMessage(ev);
			};

			ws.onclose = function(ev){
				if(! connectingResolved){
					$rootScope.$apply(connectingDefer.reject("connection closed"));
				}
				mapUrl = false;
				connectinDefer = false;
				connectingResolved = true;
				console.log('connection closing', connectingDefer);
				$rootScope.$apply($rootScope.$emit('mapsocket_closed'));
			}

			return connectingDefer.promise;
		}

		var nextReplyId = function(){
			nextReplyToId++;
			if(nextReplyToId > 10000){
				nextReplyToId = 0;
			}
			return nextReplyToId;
		}

		var sendRequest = function(action, type, id, data){
			var request = {'action':action, 'type': type};
			if(id){
				request.id = id;
			}
			if(data){
				request.data = data;
			}
			request.reply_with = nextReplyId();

			var defer = $q.defer();
			requests[request.reply_with] = {
				'posted': new Date(),
				'defer': defer
			};
			if(connectingDefer){
				connectingDefer.promise.then(function(){
					ws.send(JSON.stringify(request));
				}, function(error){
					defer.reject(error);
				})
			}
			return defer.promise;
		}

		var onMessage = function(ev){
			var messageObj = JSON.parse(ev.data);
			if(messageObj.type == 'reply'){
				maybeReply(messageObj);
				return;
			}
			var eventName = messageObj.action + '_' + messageObj.type;
			var emitData = messageObj.action == 'delete' ? messageObj.type_id : messageObj.data;
			$rootScope.$emit(eventName, emitData);
		};

		var maybeReply = function(reply){
			if(! reply.type_id){
				return;
			}
			if(requests.hasOwnProperty(reply.type_id)){
				var defer = requests[reply.type_id].defer;
				if(reply.accepted){
					defer.resolve(reply.data);
				} else {
					defer.reject(reply.data);
				}
				delete requests[reply.type_id];
			}
		};

		var query = function(type){
			var outDefer = $q.defer();
			var q = sendRequest('get', type);
			q.then(function(success){
				success = success.map(function(obj){
					return attach(type, obj);
				});
				outDefer.resolve(success);
			},
			function(fail){
				outDefer.reject(fail);
			});
			return outDefer.promise;
		};

		var get = function(type, id){
			var outDefer = $q.defer();
			var q = sendRequest('get', type, id);
			q.then(function(success){
				attach(type, success);
				/*success.save = function(){
					return sendRequest('put', type, this.id, this);
				};
				success.delete = function(){
					return sendRequest('delete', type, this.id);
				};*/
				outDefer.resolve(success);
			},
			function(fail){
				outDefer.reject(fail);
			});
			return outDefer.promise;
		};

		var attach = function(type, obj){
			obj.$save = function(){
				return sendRequest('put', type, this.id, this);
			};
			obj.$delete = function(){
				return sendRequest('delete', type, this.id);
			};
			var eventName = 'put_' + type
			$rootScope.$on(eventName, function(ev, newObjParams){
				if(newObjParams.id != obj.id){
					return obj;
				}

				var k;
				for(k in newObjParams){
					if(newObjParams.hasOwnProperty(k)){
						obj[k] = newObjParams[k];
					}
				}
				console.log('put event', obj.id, ev, newObjParams);
			});
			return obj;
		};

		connectMap.sendRequest = sendRequest;
		connectMap.query = query;
		connectMap.get = get;
		connectMap.attach = attach;
		return connectMap;
	}])
	.factory('SocketResource', ['MapSocket', '$q', '$rootScope', function(MapSocket, $q, $rootScope){

		return function(typeString){
			var localArray = [];

			var safePush = function(newObj){
				var foundObjs = localArray.filter(function(obj){
					return obj.id == newObj.id;
				});
				if(foundObjs.length > 0){
					return foundObjs[0];
				}
				newObj = MapSocket.attach(typeString, newObj);
				localArray.push(newObj);
				return newObj;
			}

			var loadObjects = function(){
				MapSocket.query(typeString).then(function(success){
					localArray.splice(0, localArray.length);
					success.forEach(function(e){
						localArray.push(e);
					});
				},
				function(fail){
					console.error('socket res failed to load data', typeString, fail);
					localArray.splice(0, localArray.length)
				})
			};

			var create = function(args){
				var retDefer = $q.defer();
				var localDefer = MapSocket.sendRequest('post', typeString, false, args);
				localDefer.then(function(success){
					success = safePush(success);
					retDefer.resolve(success);
				},
				function(fail){
					retDefer.reject(fail);
				});
				return retDefer.promise;
			};

			$rootScope.$on('put_' + typeString, function(ev, obj){
				safePush(obj);
			});

			$rootScope.$on('delete_' + typeString, function(ev, id){
				var found = localArray.filter(function(obj){
					return obj.id == id;
				});
				found.forEach(function(o){
					localArray.splice(localArray.indexOf(o), 1);
				});
			});

			$rootScope.$on('mapsocket_closed', function(){
				localArray.splice(0, localArray.length);
			});

			$rootScope.$on('mapsocket_open', function(){
				loadObjects();
			});

			return {
				'create': create,
				'models': localArray
			};
		}
	}]).
	factory('Tool', ['$rootScope', function($rootScope){
		var defaultTool = {
			name: 'default',
			selected: function(){
				return true;
			},
			deselected: function(){
				return true;
			},
			grid_mousedown: function(origin, ev, cellx, celly){
				return true;
			},
			grid_mouseup: function(origin, ev, cellx, celly){
				return true;
			},
			grid_mousemove: function(origin, ev, cellx, celly){
				return true;
			}
		};

		var currentTool = defaultTool;

		var setTool = function(makeFunc){
			var newTool = Object.create(defaultTool);
			currentTool.deselected();
			updateTool = makeFunc(newTool);
			updateTool.selected();
			$rootScope.$broadcast('grid_toolchange', updateTool, currentTool);
			currentTool = updateTool;
			return currentTool;
		};

		setTool.currentTool = currentTool;

		$rootScope.$on('grid_mousedown', function(){
			currentTool.grid_mousedown.apply(currentTool, arguments);
		});

		$rootScope.$on('grid_mousemove', function(){
			currentTool.grid_mousemove.apply(currentTool, arguments);
		});

		$rootScope.$on('grid_mouseup', function(){
			currentTool.grid_mouseup.apply(currentTool, arguments);
		});

		return setTool;
	}])
	.run(function($rootScope, $resource, SocketResource){
		$rootScope.user = window.currentUser;
		$rootScope.loginUrl = window.loginUrl;
		$rootScope.logoutUrl = window.logoutUrl;

		$rootScope.Map = $resource('/maps/:mapid', {}, {
		'save': {'method':'PUT'},
		'create':{'method':'POST', },
		'query':{'method':'GET', 'isArray':true, 'params':{'mapid':''}}});

		$rootScope.Layers = SocketResource('layer');
		$rootScope.Combatants = SocketResource('combatant');
		$rootScope.Zones = SocketResource('zone');
		$rootScope.Auras = SocketResource('aura');
		$rootScope.Scenery = SocketResource('scenery');

		$rootScope.stopPropagation = function(ev){
			ev.stopPropagation();
		};

		$rootScope.nearest = function(i, fractions){
			if(! fractions){
				return Math.round(i);
			}
			return Math.round(i * fractions) / fractions;
		};

		$rootScope.contained = function(i, fractions){
			if(! fractions){
				return Math.floor(i);
			}
			return Math.floor(i * fractions) / fractions;
		};

		$rootScope.translate = {x:0,y:0};
		$rootScope.scale = 1;
		$rootScope.CELL_SIZE = 32;

		$rootScope.transform = function(n, trans){
			return (n + trans) * $rootScope.scale;
		};

		$rootScope.transformX = function(n){
			return $rootScope.transform(n, $rootScope.translate.x);
		};

		$rootScope.transformY = function(n){
			return $rootScope.transform(n, $rootScope.translate.y);
		};

		$rootScope.dimensionToCell = function(d, pan){
			return ( (d - pan) / $rootScope.scale) / $rootScope.CELL_SIZE;
		};

		$rootScope.pixelsToCells = function(pixelX, pixelY){
			var cellX = $rootScope.dimensionToCell(pixelX, $rootScope.translate.x);
			var cellY = $rootScope.dimensionToCell(pixelY, $rootScope.translate.y);
			return [cellX, cellY];
		};

	});

//----------------------------------------------------------------------------------------------------------------------
<commit_msg>Fixed ui updates only on ui events.<commit_after>//----------------------------------------------------------------------------------------------------------------------
// Main Angular application.
//
// @module app.js
//----------------------------------------------------------------------------------------------------------------------

angular.module("battlemap", ['ngResource', 'battlemap.controllers', 'monospaced.mousewheel'])
	.config(['$locationProvider', function($locationProvider) {
		$locationProvider.html5Mode(true);
	}])
	.config(['$routeProvider', function($routeProvider) {
		$routeProvider
			.when('/', {templateUrl: '/partials/list_maps.html',   controller: 'ListMapsCtrl'})
			.when('/maps/:mapid', {templateUrl: '/partials/map.html',   controller: 'ViewMapCtrl'})
			.when('/maps/:mapid/edit', {templateUrl: '/partials/map.html',   controller: 'EditMapCtrl'})
			.otherwise({redirectTo: '/'});
	}])
	.factory('MapSocket', ['$q', '$rootScope', function($q, $rootScope){

		var nextReplyToId = 0;
		var requests = {};
		var mapUrl = false;
		var ws = false;
		var listeners = {};

		var connectingDefer = false;
		var connectingResolved = true;

		var connectMap = function(mapObj){
			if(mapObj.websocketUrl == mapUrl){
				console.log('connection already established', mapObj.websocketUrl);
				return connectingDefer.promise;
			}

			if(ws){
				ws.close();
				if(! connectingResolved){
					$rootScope.$apply(connectingDefer.reject("connection closed"));
				}
			}

			connectingDefer = $q.defer();

			console.log('connecting to map ws', mapObj.websocketUrl, ws ? true : false);

			mapUrl = mapObj.websocketUrl;
			ws = new WebSocket(mapUrl);

			ws.onopen = function(ev){
				console.log('resolving connection', $rootScope);
				connectingResolved = true;
				$rootScope.$apply(connectingDefer.resolve(ev));
				console.log('emiting open socket');
				$rootScope.$apply($rootScope.$emit('mapsocket_open'));
			};

			ws.onmessage = function(ev){
				onMessage(ev);
			};

			ws.onclose = function(ev){
				if(! connectingResolved){
					$rootScope.$apply(connectingDefer.reject("connection closed"));
				}
				mapUrl = false;
				connectinDefer = false;
				connectingResolved = true;
				console.log('connection closing', connectingDefer);
				$rootScope.$apply($rootScope.$emit('mapsocket_closed'));
			}

			return connectingDefer.promise;
		}

		var nextReplyId = function(){
			nextReplyToId++;
			if(nextReplyToId > 10000){
				nextReplyToId = 0;
			}
			return nextReplyToId;
		}

		var sendRequest = function(action, type, id, data){
			var request = {'action':action, 'type': type};
			if(id){
				request.id = id;
			}
			if(data){
				request.data = data;
			}
			request.reply_with = nextReplyId();

			var defer = $q.defer();
			requests[request.reply_with] = {
				'posted': new Date(),
				'defer': defer
			};
			if(connectingDefer){
				connectingDefer.promise.then(function(){
					ws.send(JSON.stringify(request));
				}, function(error){
					defer.reject(error);
				})
			}
			return defer.promise;
		}

		var onMessage = function(ev){
			$rootScope.$apply(function(){
				var messageObj = JSON.parse(ev.data);
				if(messageObj.type == 'reply'){
					maybeReply(messageObj);
					return;
				}
				var eventName = messageObj.action + '_' + messageObj.type;
				var emitData = messageObj.action == 'delete' ? messageObj.type_id : messageObj.data;
				$rootScope.$emit(eventName, emitData);
			});
		};

		var maybeReply = function(reply){
			if(! reply.type_id){
				return;
			}
			if(requests.hasOwnProperty(reply.type_id)){
				var defer = requests[reply.type_id].defer;
				if(reply.accepted){
					defer.resolve(reply.data);
				} else {
					defer.reject(reply.data);
				}
				delete requests[reply.type_id];
			}
		};

		var query = function(type){
			var outDefer = $q.defer();
			var q = sendRequest('get', type);
			q.then(function(success){
				success = success.map(function(obj){
					return attach(type, obj);
				});
				outDefer.resolve(success);
			},
			function(fail){
				outDefer.reject(fail);
			});
			return outDefer.promise;
		};

		var get = function(type, id){
			var outDefer = $q.defer();
			var q = sendRequest('get', type, id);
			q.then(function(success){
				attach(type, success);
				/*success.save = function(){
					return sendRequest('put', type, this.id, this);
				};
				success.delete = function(){
					return sendRequest('delete', type, this.id);
				};*/
				outDefer.resolve(success);
			},
			function(fail){
				outDefer.reject(fail);
			});
			return outDefer.promise;
		};

		var attach = function(type, obj){
			obj.$save = function(){
				return sendRequest('put', type, this.id, this);
			};
			obj.$delete = function(){
				return sendRequest('delete', type, this.id);
			};
			var eventName = 'put_' + type
			$rootScope.$on(eventName, function(ev, newObjParams){
				if(newObjParams.id != obj.id){
					return obj;
				}

				var k;
				for(k in newObjParams){
					if(newObjParams.hasOwnProperty(k)){
						obj[k] = newObjParams[k];
					}
				}
				console.log('put event', obj.id, ev, newObjParams);
			});
			return obj;
		};

		connectMap.sendRequest = sendRequest;
		connectMap.query = query;
		connectMap.get = get;
		connectMap.attach = attach;
		return connectMap;
	}])
	.factory('SocketResource', ['MapSocket', '$q', '$rootScope', function(MapSocket, $q, $rootScope){

		return function(typeString){
			var localArray = [];

			var safePush = function(newObj){
				var foundObjs = localArray.filter(function(obj){
					return obj.id == newObj.id;
				});
				if(foundObjs.length > 0){
					return foundObjs[0];
				}
				newObj = MapSocket.attach(typeString, newObj);
				localArray.push(newObj);
				return newObj;
			}

			var loadObjects = function(){
				MapSocket.query(typeString).then(function(success){
					localArray.splice(0, localArray.length);
					success.forEach(function(e){
						localArray.push(e);
					});
				},
				function(fail){
					console.error('socket res failed to load data', typeString, fail);
					localArray.splice(0, localArray.length)
				})
			};

			var create = function(args){
				var retDefer = $q.defer();
				var localDefer = MapSocket.sendRequest('post', typeString, false, args);
				localDefer.then(function(success){
					success = safePush(success);
					retDefer.resolve(success);
				},
				function(fail){
					retDefer.reject(fail);
				});
				return retDefer.promise;
			};

			$rootScope.$on('put_' + typeString, function(ev, obj){
				safePush(obj);
			});

			$rootScope.$on('delete_' + typeString, function(ev, id){
				var found = localArray.filter(function(obj){
					return obj.id == id;
				});
				found.forEach(function(o){
					localArray.splice(localArray.indexOf(o), 1);
				});
			});

			$rootScope.$on('mapsocket_closed', function(){
				localArray.splice(0, localArray.length);
			});

			$rootScope.$on('mapsocket_open', function(){
				loadObjects();
			});

			return {
				'create': create,
				'models': localArray
			};
		}
	}]).
	factory('Tool', ['$rootScope', function($rootScope){
		var defaultTool = {
			name: 'default',
			selected: function(){
				return true;
			},
			deselected: function(){
				return true;
			},
			grid_mousedown: function(origin, ev, cellx, celly){
				return true;
			},
			grid_mouseup: function(origin, ev, cellx, celly){
				return true;
			},
			grid_mousemove: function(origin, ev, cellx, celly){
				return true;
			}
		};

		var currentTool = defaultTool;

		var setTool = function(makeFunc){
			var newTool = Object.create(defaultTool);
			currentTool.deselected();
			updateTool = makeFunc(newTool);
			updateTool.selected();
			$rootScope.$broadcast('grid_toolchange', updateTool, currentTool);
			currentTool = updateTool;
			return currentTool;
		};

		setTool.currentTool = currentTool;

		$rootScope.$on('grid_mousedown', function(){
			currentTool.grid_mousedown.apply(currentTool, arguments);
		});

		$rootScope.$on('grid_mousemove', function(){
			currentTool.grid_mousemove.apply(currentTool, arguments);
		});

		$rootScope.$on('grid_mouseup', function(){
			currentTool.grid_mouseup.apply(currentTool, arguments);
		});

		return setTool;
	}])
	.run(function($rootScope, $resource, SocketResource){
		$rootScope.user = window.currentUser;
		$rootScope.loginUrl = window.loginUrl;
		$rootScope.logoutUrl = window.logoutUrl;

		$rootScope.Map = $resource('/maps/:mapid', {}, {
		'save': {'method':'PUT'},
		'create':{'method':'POST', },
		'query':{'method':'GET', 'isArray':true, 'params':{'mapid':''}}});

		$rootScope.Layers = SocketResource('layer');
		$rootScope.Combatants = SocketResource('combatant');
		$rootScope.Zones = SocketResource('zone');
		$rootScope.Auras = SocketResource('aura');
		$rootScope.Scenery = SocketResource('scenery');

		$rootScope.stopPropagation = function(ev){
			ev.stopPropagation();
		};

		$rootScope.nearest = function(i, fractions){
			if(! fractions){
				return Math.round(i);
			}
			return Math.round(i * fractions) / fractions;
		};

		$rootScope.contained = function(i, fractions){
			if(! fractions){
				return Math.floor(i);
			}
			return Math.floor(i * fractions) / fractions;
		};

		$rootScope.translate = {x:0,y:0};
		$rootScope.scale = 1;
		$rootScope.CELL_SIZE = 32;

		$rootScope.transform = function(n, trans){
			return (n + trans) * $rootScope.scale;
		};

		$rootScope.transformX = function(n){
			return $rootScope.transform(n, $rootScope.translate.x);
		};

		$rootScope.transformY = function(n){
			return $rootScope.transform(n, $rootScope.translate.y);
		};

		$rootScope.dimensionToCell = function(d, pan){
			return ( (d - pan) / $rootScope.scale) / $rootScope.CELL_SIZE;
		};

		$rootScope.pixelsToCells = function(pixelX, pixelY){
			var cellX = $rootScope.dimensionToCell(pixelX, $rootScope.translate.x);
			var cellY = $rootScope.dimensionToCell(pixelY, $rootScope.translate.y);
			return [cellX, cellY];
		};

	});

//----------------------------------------------------------------------------------------------------------------------
<|endoftext|>


<commit_before>// Cause: Ukraine

// We have a little templating "engine" that replaces placeholders.
/* eslint no-template-curly-in-string: 0 */

const data = {
  id: 'JmClR7bmy',
  about:
    '### About Tab for Global Health\n\n**Tab for Global Health** is proud to support [Partners In Health](https://www.pih.org/) and Dr. Paul Farmer’s legacy. With each tab opened, you are helping provide access to health care to those in need and strengthening public health systems all around the world. \n \nPartners In Health’s mission is to provide a preferential option for the poor in health care. By establishing long-term relationships with sister organizations based in settings of poverty, Partners In Health strives to achieve two overarching goals: to bring the benefits of modern medical science to those most in need of them and to serve as an antidote to despair.\n \nThey draw on the resources of the world’s leading medical and academic institutions and on the lived experience of the world’s most impoverished communities. At its root, their mission is both medical and moral. It is based on solidarity, rather than charity alone.\n \nWhen their patients are ill and have no access to care, their team of health professionals, scholars, and activists will do whatever it takes to make them well.\n \nPartners In Health has used a community-based model to provide health care and support for the last 30 years and now serves millions of patients across 12 countries.\n \n**Your tabs are making an impact**. The counter on your new tab page (with the medical bag icon) estimates how many in-home visits from a [community health worker](https://www.pih.org/programs/community-health-workers) your tabs have helped support.  Each counter increment represents raising ~1/50th of the money needed to support an in-home visit. Thank you for making a difference!',
  name: 'Ukraine',
  isAvailableToSelect: false,
  icon: 'medical-bag',
  backgroundImageCategory: 'trees', // TODO: update with new collection
  charityId: '90bfe202-54a9-4eea-9003-5e91572387dd',
  individualImpactEnabled: true,
  impactVisits: 131,
  landingPagePath: '/global-health/',
  slug: 'global-health',
  impact: {
    impactCounterText:
      '##### **Your positive impact!**\n\nThis counter shows your progress toward helping provide an at-home medical visit. Every tab you open helps. Keep it up!\n\n',
    claimImpactSubtitle:
      '##### You did it! Your tabs are on their way to providing an at-home medical visit.',
    impactIcon: 'medical-bag',
    // @feature/generalize-referral-reward
    // Note: this is not used until we generalize referral incentives:
    // https://github.com/gladly-team/tab/blob/master/graphql/database/users/rewardReferringUser.js#L91
    referralRewardNotification:
      '##### Congrats! You recruited a friend to help provide access to health care around the world.\n\n',
    // Note: this is not used until we generalize referral incentives.
    // @feature/generalize-referral-reward
    referralRewardTitle: '#### You just moved global health forward!\n\n',
    referralRewardSubtitle:
      "##### Congratulations! You're making a huge impact to support the wellbeing of people around the world. Want to do more? Invite a few more friends!\n\n",
    impactWalkthroughText:
      "##### When you do, you’ll help provide at-home healthcare visits to people in need. We'll track your progress at the top of the page:\n\n",
    // @feature/generalize-referral-reward
    // Note: this is not used until we generalize referral incentives.
    newlyReferredImpactWalkthroughText:
      "##### Your friend has started you off with some impact! Open a new tab now to help provide at-home healthcare visits to people in need. We'll track your progress at the top of the page:\n\n",
    confirmImpactSubtitle:
      "##### Each time you open a tab, you'll be helping provide at-home healthcare visits through Partners In Health. Ready to get started?\n\n",
    // walkMeGif: undefined,
  },
  onboarding: {
    steps: [
      {
        title: '### Your tabs are doing great things',
        subtitle:
          'Now, every tab you open helps train and support community healthcare workers to provide at-home visits.\n\nTabbers like you are supporting critical nonprofit work all around the world. Thank you!\n\n',
        imgName: 'globalHealth/onboarding1.svg',
      },
      {
        title: '### Do more with your squad',
        subtitle:
          'With your squad, help even more people access safe health care!\n\nYou and your friends can team up to do more good.\n\n',
        imgName: 'globalHealth/onboarding2.svg',
      },
      {
        title: "### It doesn't cost you a thing",
        subtitle:
          "Ads on the new tab page raise money that we give to nonprofits. Most ads aren't good—but these ones are :)\n\n",
        imgName: 'globalHealth/onboarding3.svg',
      },
    ],
  },
  sharing: {
    title: '### **Get a friend on board**',
    subtitle:
      '##### Everyone can and _should_ make a difference: invite a friend to join in.\n\n',
    shareImage: 'globalHealth/sharing.svg',
    sentImage: 'globalHealth/sharing.svg',
    imgCategory: 'global-health',
    email: {
      image:
        'https://prod-tab2017-media.gladly.io/img/cause/globalHealth/emailInvite.jpg',
      title:
        'With Tab for Global Health, supporting healthcare access has never been easier.',
      about:
        '
Tab for Global Health turns your web browser into a force for good. With each tab opened, ad revenue supports community healthcare training through Partners In Health. This training helps ensure the people in need of care will receive it, long term.
\n\n',
      faq:
        'We’re partnered with Partners In Health to ensure the money raised will directly support their mission of providing high-quality health care globally to those who need it.',
      sendgridEmailTemplateId: 'd-c925270aca954ff3b4c05f436cb52b54',
    },
    sendgridEmailTemplateId: 'd-c925270aca954ff3b4c05f436cb52b54',
    facebookButtonTitle:
      'People around the world are struggling to access safe and reliable healthcare, but this is a problem we can all help solve. Now, with Tab for Global Health, every tab I open helps raise money for Partners In Health to train community healthcare workers. Check it out - it’s free!',
    twitterButtonTitle:
      'People around the world are struggling to access quality health care, but this is a problem we can solve. Now, with Tab for Global Health, every tab I open raises money for Partners In Health to train community healthcare workers. Check it out - it’s free!',
    redditButtonTitle: 'Help provide quality healthcare around the world',
    tumblrTitle: 'Help provide quality healthcare around the world ',
    tumblrCaption:
      'Every time I open a new tab with Tab for Global Health I am raising money to train healthcare workers through Partners In Health. Join today for free! ',
  },
  // TODO
  squads: {
    currentMissionAlert: 'TODO',
    currentMissionDetails: 'TODO',
    currentMissionStep2: 'TODO',
    currentMissionStep3: 'TODO',
    currentMissionSummary: 'TODO',
    impactCounterText: 'TODO',
    missionCompleteAlert: 'TODO',
    missionCompleteDescription: 'TODO',
    missionCompleteSubtitle: 'TODO',
    squadCounterText: 'TODO',
    squadInviteTemplateId: 'TODO',
  },
  theme: {
    primaryColor: '#FB5050',
    secondaryColor: '#29BEBA',
  },
}

export default data
<commit_msg>Set cause data<commit_after>// Cause: Ukraine

// We have a little templating "engine" that replaces placeholders.
/* eslint no-template-curly-in-string: 0 */

const data = {
  id: 'JmClR7bmy',
  about:
    '### About Tab for Ukraine\n\n**Tab for Ukraine** is proud to support [Save the Children](https://ukraine.savethechildren.net/), an incredible organization working to save lives and provide ongoing humanitarian aid to Ukrainian families.\n\n**Save the Children** has been working to provide lifesaving interventions in Ukraine since 2014. They work to provide the basic needs of the most vulnerable families including food, shelter, clothing, cash grants, and medicine. They also provide children and their caregivers with psychosocial support as well as work with schools to increase awareness of the dangers of mines and other remnants of war.\n\nIf you would like to make an immediate monetary impact, donations to Save the Children can be made [here](https://support.savethechildren.org/site/Donation2?df_id=1620&1620.donation=form1).\n\n**Your tabs are making an impact**. The counter on your new tab page (with the water icon) estimates how many water purifying aquatabs your tabs have funded. **This is a representation of just one small part of the humanitarian aid that Save the Children provides.** The money this page raises for Save the Children will support whatever supplies and support are most needed.',
  name: 'Ukraine',
  isAvailableToSelect: false,
  icon: 'water',
  backgroundImageCategory: 'trees', // TODO: update with new collection
  charityId: '90bfe202-54a9-4eea-9003-5e91572387dd',
  individualImpactEnabled: true,
  impactVisits: 13,
  landingPagePath: '/ukraine/',
  slug: 'ukraine',
  impact: {
    impactCounterText:
      '##### **Your positive impact!**\n\nThis shows how many water purifying aquatabs your tabs can provide to families in Ukraine.\n\nThis is a representation of just one small part of the humanitarian aid that Save the Children provides. The money this page raises for Save the Children will support whatever supplies and support they determine is most needed.\n\nEvery tab you open helps. Keep it up!\n\n',
    claimImpactSubtitle:
      '##### You did it! You just turned your tab into a water-purifying aquatab. Keep it up!\n\n##### This is a representation of just one small part of the humanitarian aid that Save the Children provides. The money you raise for Save the Children will support whatever supplies are most needed.',
    impactIcon: 'water',
    // @feature/generalize-referral-reward
    // Note: this is not used until we generalize referral incentives:
    // https://github.com/gladly-team/tab/blob/master/graphql/database/users/rewardReferringUser.js#L91
    referralRewardNotification:
      '##### Thank you! You recruited a friend to support ongoing humanitarian relief to families in Ukraine.\n\n',
    // Note: this is not used until we generalize referral incentives.
    // @feature/generalize-referral-reward
    referralRewardTitle: '#### You are doing good\n\n',
    referralRewardSubtitle:
      "##### Thank you! You're making a huge impact by supporting ongoing humanitarian relief to families in Ukraine. Want to help even more people for free? Invite a few more friends.\n\n",
    impactWalkthroughText:
      "##### When you do, you'll provide ongoing humanitarian relief to families in Ukraine.\n\nWe have chosen water purifying aquatabs as a representation of just one small part of the humanitarian aid that Save the Children provides. The money this page raises for Save the Children will support whatever supplies and support are most needed. \n\nWe'll track how many aquatabs you’ve provided on the top of the page:\n\n",
    // @feature/generalize-referral-reward
    // Note: this is not used until we generalize referral incentives.
    newlyReferredImpactWalkthroughText:
      "##### When you do, you'll provide ongoing humanitarian relief to families in Ukraine.\n\nWe have chosen water purifying aquatabs as a representation of just one small part of the humanitarian aid that Save the Children provides. The money this page raises for Save the Children will support whatever supplies and support are most needed. \n\nWe'll track how many aquatabs you’ve provided on the top of the page:\n\n",
    confirmImpactSubtitle:
      "##### Each time you open a tab, you'll be helping Save the Children provide ongoing humanitarian aid to Ukrainian families. Ready to get started?\n\n",
    // walkMeGif: undefined,
  },
  onboarding: {
    steps: [
      {
        title: '### Your tabs are doing great things',
        subtitle:
          'Now, every tab you open supports ongoing humanitarian aid to Ukrainian families. Tabbers like you are supporting critical nonprofit work. Thank you!\n\n',
        imgName: 'ukraine/onboarding1.svg',
      },
      {
        title: '### Do more with your squad',
        subtitle:
          'With a squad, you and your friends can team up to provide even more humanitarian aid.\n\n',
        imgName: 'ukraine/onboarding2.svg',
      },
      {
        title: "### It doesn't cost you a thing",
        subtitle:
          "Ads on the new tab page raise money that we give to nonprofits. Most ads aren't good—but these ones are :)\n\n",
        imgName: 'ukraine/onboarding3.svg',
      },
    ],
  },
  sharing: {
    title: '### **Get a friend on board**',
    subtitle:
      '##### Everyone can and _should_ make a difference: invite a friend to join in.\n\n',
    shareImage: 'ukraine/sharing.svg',
    sentImage: 'ukraine/sharing.svg',
    imgCategory: 'ukraine',
    email: {
      image:
        'https://prod-tab2017-media.gladly.io/img/cause/ukraine/emailInvite.jpg',
      title:
        'We all can and should support the people of Ukraine. Turn your internet browsing into ongoing support.',
      about:
        '
Tab for Ukraine turns your web browser into a force for good. With each tab opened, ad revenue supports Save the Children, which has been working to provide lifesaving interventions in Ukraine since 2014.
\n\n',
      faq:
        'Save the Children ensures the money raised will directly support Ukrainian families affected by the war.',
      sendgridEmailTemplateId: 'd-c925270aca954ff3b4c05f436cb52b54',
    },
    sendgridEmailTemplateId: 'd-c925270aca954ff3b4c05f436cb52b54',
    facebookButtonTitle:
      'The people of Ukraine are under attack and need support. Now, with Tab for Ukraine, every tab I open helps raise money for Save the Children to provide ongoing humanitarian support to Ukrainian families. Check it out - it’s free!',
    twitterButtonTitle:
      'The people of Ukraine are under attack and need support. Now, with Tab for Ukraine, every tab I open helps raise money for Save the Children to provide ongoing humanitarian support to Ukrainian families. Check it out - it’s free!',
    redditButtonTitle: 'Help support Ukrainian families',
    tumblrTitle: 'Help support Ukrainian families',
    tumblrCaption:
      'Every time I open a new tab with Tab for Ukraine I am raising money for Save the Children to provide ongoing humanitarian support to Ukrainian families. Join today for free!',
  },
  // TODO
  squads: {
    currentMissionAlert: 'TODO',
    currentMissionDetails: 'TODO',
    currentMissionStep2: 'TODO',
    currentMissionStep3: 'TODO',
    currentMissionSummary: 'TODO',
    impactCounterText: 'TODO',
    missionCompleteAlert: 'TODO',
    missionCompleteDescription: 'TODO',
    missionCompleteSubtitle: 'TODO',
    squadCounterText: 'TODO',
    squadInviteTemplateId: 'TODO',
  },
  theme: {
    primaryColor: '#FB5050',
    secondaryColor: '#29BEBA',
  },
}

export default data
<|endoftext|>


<commit_before>'use strict';
/* global moment:false */

angular.module('openhimWebui2App')
  .controller('DashboardCtrl', function ($scope, $modal, $location, $interval, Api, Alerting) {

    /***************************************************/
    /**         Initial page load functions           **/
    /***************************************************/

    var dashboardInterval = $interval(function() {
      //$scope.getTransactionLoadMetrics();
      $scope.getLoadMetrics();
      $scope.getTimeMetrics();
      $scope.getStatusMetrics();
    }, 5000);

    /***************************************************/
    /**         Initial page load functions           **/
    /***************************************************/

    /*********************************************************/
    /**         Transaction Load Metric Functions           **/
    /*********************************************************/

    $scope.updateTransactionLoadLineChart = function(loadResults){

      var value;
      var hour;
      var transactionLoadData = [];
      $scope.loadTotal = 0;

      for ( var i=1; i<=moment().add(1, 'hours').format('H'); i++ ){
        value = 0;
        // needs to be in date format
        hour = moment().format('YYYY-MM-DD')+' '+i+':00:00';
        for ( var x=0; x<loadResults.length; x++ ){
          var date = loadResults[x].timestamp;

          // check if the result has value for current hour in the loop
          // add one hour to simulate transactions for the end of the hour and not in the hour
          if ( moment( date ).add(1, 'hours').format('H') === moment( hour, 'YYYY-MM-DD H' ).format('H') ){
            value = loadResults[x].load;
          }
        }

        $scope.loadTotal += value;
        transactionLoadData.push({ hour: hour, value: value });
      }

      $scope.transactionLoadData = {data: transactionLoadData, xkey: 'hour', ykeys: ['value'], labels: ['Load'], postunits: ' per hour'};
    };

    $scope.loadMetricsSuccess = function(loadResults){
      if ( loadResults.length === 0 ){
        Alerting.AlertAddMsg('load', 'warning', 'There has been no transactions received for today');
      }else{
        $scope.updateTransactionLoadLineChart(loadResults);
      }
    };

    $scope.loadMetricsError = function(err){
      // add warning message when unable to get data
      Alerting.AlertAddMsg('load', 'danger', 'Transaction Load Error: ' + err.status + ' ' + err.data);
    };

    $scope.getLoadMetrics = function(){
      // reset any load metric alert warnings
      Alerting.AlertReset('load');

      var startDate = moment().startOf('day').toDate();
      var endDate = moment().startOf('hour').add(1, 'hours').toDate();

      // do API call here to pull load metrics
      Api.Metrics.query({
        startDate: startDate,
        endDate: endDate
      }, $scope.loadMetricsSuccess, $scope.loadMetricsError);
    };

    $scope.getLoadMetrics();

    /*********************************************************/
    /**         Transaction Load Metric Functions           **/
    /*********************************************************/

    /******************************************************************/
    /**         Transaction Response Time Metric Functions           **/
    /******************************************************************/

    $scope.updateResponseTimeLineChart = function(timeResults){

      var value = 0;
      var hour;
      var avgResponseTimeTotal = 0;
      var responseTimeData = [];
      $scope.avgResponseTime = 0;

      for ( var i=1; i<=moment().add(1, 'hours').format('H'); i++ ){
        value = 0;
        // needs to be in date format
        hour = moment().format('YYYY-MM-DD')+' '+i+':00';
        for ( var x=0; x<timeResults.length; x++ ){
          var date = timeResults[x].timestamp;
          // check if the result has value for current hour in the loop
          if ( moment( date ).add(1, 'hours').format('H') === moment( hour, 'YYYY-MM-DD H' ).format('H') ){
            value = timeResults[x].avgResp;
            avgResponseTimeTotal += value;
          }
        }

        $scope.avgResponseTime += value;
        responseTimeData.push({ hour: hour, value: value.toFixed(2) });
      }

      $scope.avgResponseTime = (avgResponseTimeTotal / timeResults.length).toFixed(2);

      // set data for morris graph to create/update
      $scope.transactionResponseTimeData = {data: responseTimeData, xkey: 'hour', ykeys: ['value'], labels: ['Load'], postunits: ' ms'};

    };

    $scope.timeMetricsSuccess = function(timeResults){
      if ( timeResults.length === 0 ){
        Alerting.AlertAddMsg('responseTime', 'warning', 'There has been no transactions received for today');
      }else{
        $scope.updateResponseTimeLineChart(timeResults);
      }
    };

    $scope.timeMetricsError = function(err){
      // add warning message when unable to get data
      Alerting.AlertAddMsg('responseTime', 'danger', 'Transaction Response Time Error: ' + err.status + ' ' + err.data);
    };

    $scope.getTimeMetrics = function(){
      // reset any load metric alert warnings
      Alerting.AlertReset('responseTime');

      var startDate = moment().startOf('day').toDate();
      var endDate = moment().startOf('hour').add(1, 'hours').toDate();

      // do API call here to pull load metrics
      Api.Metrics.query({
        startDate: startDate,
        endDate: endDate
      }, $scope.timeMetricsSuccess, $scope.timeMetricsError);
    };

    $scope.getTimeMetrics();

    /******************************************************************/
    /**         Transaction Response Time Metric Functions           **/
    /******************************************************************/

    /********************************************************************/
    /**         Channel Transactions Status Metric Functions           **/
    /********************************************************************/

    $scope.updateStatusBarChart = function(statusResults){

      // set scope variable for amount of active channels
      $scope.activeChannels = statusResults.length;

      var channelsMap;

      // create channelsMap for status name reference
      Api.Channels.query(function(channels){
        channelsMap = {};
        angular.forEach(channels, function(channel){
          channelsMap[channel._id] = channel.name;
        });

        // define varables for graph data set
        var objectData;
        var statusData = [];
        var statusKeys = [];
        var statusLabels = [];
        var statusColors = [];

        // loop through each channels found in result and construct graph objects
        for ( var i=0; i<statusResults.length; i++ ){

          objectData = {};

          // create a link object for when the user clicks on the bar
          objectData.link = 'channels/'+statusResults[i]._id.channelID;
          objectData.channel = channelsMap[statusResults[i]._id.channelID];

          // check if Processing status has any records
          if ( statusResults[i].processing !== 0 ){
            objectData.processing = statusResults[i].processing;

            // only add these if it isnt yet present
            if ( statusKeys.indexOf('processing') === -1 ){
              statusKeys.push('processing');
              statusLabels.push('Processing');
              statusColors.push('#777777');
            }
          }

          // check if Failed status has any records
          if ( statusResults[i].failed !== 0 ){
            objectData.failed = statusResults[i].failed;

            // only add these if it isnt yet present
            if ( statusKeys.indexOf('failed') === -1 ){
              statusKeys.push('failed');
              statusLabels.push('Failed');
              statusColors.push('#d9534f');
            }
          }

          // check if Completed status has any records
          if ( statusResults[i].completed !== 0 ){
            objectData.completed = statusResults[i].completed;

            // only add these if it isnt yet present
            if ( statusKeys.indexOf('completed') === -1 ){
              statusKeys.push('completed');
              statusLabels.push('Completed');
              statusColors.push('#f0ad4e');
            }
          }

          // check if Completed with error(s) status has any records
          if ( statusResults[i].completedWErrors !== 0 ){
            objectData.completedWErrors = statusResults[i].completedWErrors;

            // only add these if it isnt yet present
            if ( statusKeys.indexOf('completedWErrors') === -1 ){
              statusKeys.push('completedWErrors');
              statusLabels.push('Completed With Errors');
              statusColors.push('#5bc0de');
            }
          }

          // check if Successful status has any records
          if ( statusResults[i].successful !== 0 ){
            objectData.successful = statusResults[i].successful;

            // only add these if it isnt yet present
            if ( statusKeys.indexOf('successful') === -1 ){
              statusKeys.push('successful');
              statusLabels.push('Successful');
              statusColors.push('#5cb85c');
            }
          }

          // push objectData to main statusData object
          statusData.push(objectData);

        }

        $scope.statusData = {data: statusData, xkey: 'channel', ykeys: statusKeys, labels: statusLabels, colors: statusColors, stacked: true,};

      },
      function(err){
        Alerting.AlertAddMsg('status', 'danger', 'Channel Load Error: ' + err.status + ' ' + err.data);
      });

    };

    $scope.statusMetricsSuccess = function(statusResults){
    if ( statusResults.length === 0 ){
    Alerting.AlertAddMsg('status', 'warning', 'There has been no transactions received for today');
    }else{
    $scope.updateStatusBarChart(statusResults);
    }
    };

    $scope.statusMetricsError = function(err){
    // add warning message when unable to get data
    Alerting.AlertAddMsg('status', 'danger', 'Transaction Load Error: ' + err.status + ' ' + err.data);
    };

    $scope.getStatusMetrics = function(){
    // reset any load metric alert warnings
    Alerting.AlertReset('status');

    var startDate = moment().startOf('day').toDate();
    var endDate = moment().startOf('hour').add(1, 'hours').toDate();

    // do API call here to pull load metrics
    Api.MetricsStatus.query({
    startDate: startDate,
    endDate: endDate
    }, $scope.statusMetricsSuccess, $scope.statusMetricsError);
    };

    // do the inital load of the transaction status metrics
    $scope.getStatusMetrics();

    /********************************************************************/
    /**         Channel Transactions Status Metric Functions           **/
    /********************************************************************/

    $scope.$on('$destroy', function() {
      // Make sure that the interval is destroyed too
      if (angular.isDefined(dashboardInterval)) {
        $interval.cancel(dashboardInterval);
        dashboardInterval = undefined;
      }
    });

  });
<commit_msg>Indentation fix<commit_after>'use strict';
/* global moment:false */

angular.module('openhimWebui2App')
  .controller('DashboardCtrl', function ($scope, $modal, $location, $interval, Api, Alerting) {

    /***************************************************/
    /**         Initial page load functions           **/
    /***************************************************/

    var dashboardInterval = $interval(function() {
      //$scope.getTransactionLoadMetrics();
      $scope.getLoadMetrics();
      $scope.getTimeMetrics();
      $scope.getStatusMetrics();
    }, 5000);

    /***************************************************/
    /**         Initial page load functions           **/
    /***************************************************/

    /*********************************************************/
    /**         Transaction Load Metric Functions           **/
    /*********************************************************/

    $scope.updateTransactionLoadLineChart = function(loadResults){
      var value;
      var hour;
      var transactionLoadData = [];
      $scope.loadTotal = 0;

      for ( var i=1; i<=moment().add(1, 'hours').format('H'); i++ ){
        value = 0;
        // needs to be in date format
        hour = moment().format('YYYY-MM-DD')+' '+i+':00:00';
        for ( var x=0; x<loadResults.length; x++ ){
          var date = loadResults[x].timestamp;

          // check if the result has value for current hour in the loop
          // add one hour to simulate transactions for the end of the hour and not in the hour
          if ( moment( date ).add(1, 'hours').format('H') === moment( hour, 'YYYY-MM-DD H' ).format('H') ){
            value = loadResults[x].load;
          }
        }

        $scope.loadTotal += value;
        transactionLoadData.push({ hour: hour, value: value });
      }

      $scope.transactionLoadData = {data: transactionLoadData, xkey: 'hour', ykeys: ['value'], labels: ['Load'], postunits: ' per hour'};
    };

    $scope.loadMetricsSuccess = function(loadResults){
      if ( loadResults.length === 0 ){
        Alerting.AlertAddMsg('load', 'warning', 'There has been no transactions received for today');
      }else{
        $scope.updateTransactionLoadLineChart(loadResults);
      }
    };

    $scope.loadMetricsError = function(err){
      // add warning message when unable to get data
      Alerting.AlertAddMsg('load', 'danger', 'Transaction Load Error: ' + err.status + ' ' + err.data);
    };

    $scope.getLoadMetrics = function(){
      // reset any load metric alert warnings
      Alerting.AlertReset('load');

      var startDate = moment().startOf('day').toDate();
      var endDate = moment().startOf('hour').add(1, 'hours').toDate();

      // do API call here to pull load metrics
      Api.Metrics.query({
        startDate: startDate,
        endDate: endDate
      }, $scope.loadMetricsSuccess, $scope.loadMetricsError);
    };

    $scope.getLoadMetrics();

    /*********************************************************/
    /**         Transaction Load Metric Functions           **/
    /*********************************************************/

    /******************************************************************/
    /**         Transaction Response Time Metric Functions           **/
    /******************************************************************/

    $scope.updateResponseTimeLineChart = function(timeResults){
      var value = 0;
      var hour;
      var avgResponseTimeTotal = 0;
      var responseTimeData = [];
      $scope.avgResponseTime = 0;

      for ( var i=1; i<=moment().add(1, 'hours').format('H'); i++ ){
        value = 0;
        // needs to be in date format
        hour = moment().format('YYYY-MM-DD')+' '+i+':00';
        for ( var x=0; x<timeResults.length; x++ ){
          var date = timeResults[x].timestamp;
          // check if the result has value for current hour in the loop
          if ( moment( date ).add(1, 'hours').format('H') === moment( hour, 'YYYY-MM-DD H' ).format('H') ){
            value = timeResults[x].avgResp;
            avgResponseTimeTotal += value;
          }
        }

        $scope.avgResponseTime += value;
        responseTimeData.push({ hour: hour, value: value.toFixed(2) });
      }

      $scope.avgResponseTime = (avgResponseTimeTotal / timeResults.length).toFixed(2);

      // set data for morris graph to create/update
      $scope.transactionResponseTimeData = {data: responseTimeData, xkey: 'hour', ykeys: ['value'], labels: ['Load'], postunits: ' ms'};

    };

    $scope.timeMetricsSuccess = function(timeResults){
      if ( timeResults.length === 0 ){
        Alerting.AlertAddMsg('responseTime', 'warning', 'There has been no transactions received for today');
      }else{
        $scope.updateResponseTimeLineChart(timeResults);
      }
    };

    $scope.timeMetricsError = function(err){
      // add warning message when unable to get data
      Alerting.AlertAddMsg('responseTime', 'danger', 'Transaction Response Time Error: ' + err.status + ' ' + err.data);
    };

    $scope.getTimeMetrics = function(){
      // reset any load metric alert warnings
      Alerting.AlertReset('responseTime');

      var startDate = moment().startOf('day').toDate();
      var endDate = moment().startOf('hour').add(1, 'hours').toDate();

      // do API call here to pull load metrics
      Api.Metrics.query({
        startDate: startDate,
        endDate: endDate
      }, $scope.timeMetricsSuccess, $scope.timeMetricsError);
    };

    $scope.getTimeMetrics();

    /******************************************************************/
    /**         Transaction Response Time Metric Functions           **/
    /******************************************************************/

    /********************************************************************/
    /**         Channel Transactions Status Metric Functions           **/
    /********************************************************************/

    $scope.updateStatusBarChart = function(statusResults){

      // set scope variable for amount of active channels
      $scope.activeChannels = statusResults.length;

      var channelsMap;

      // create channelsMap for status name reference
      Api.Channels.query(function(channels){
        channelsMap = {};
        angular.forEach(channels, function(channel){
          channelsMap[channel._id] = channel.name;
        });

        // define varables for graph data set
        var objectData;
        var statusData = [];
        var statusKeys = [];
        var statusLabels = [];
        var statusColors = [];

        // loop through each channels found in result and construct graph objects
        for ( var i=0; i<statusResults.length; i++ ){

          objectData = {};

          // create a link object for when the user clicks on the bar
          objectData.link = 'channels/'+statusResults[i]._id.channelID;
          objectData.channel = channelsMap[statusResults[i]._id.channelID];

          // check if Processing status has any records
          if ( statusResults[i].processing !== 0 ){
            objectData.processing = statusResults[i].processing;

            // only add these if it isnt yet present
            if ( statusKeys.indexOf('processing') === -1 ){
              statusKeys.push('processing');
              statusLabels.push('Processing');
              statusColors.push('#777777');
            }
          }

          // check if Failed status has any records
          if ( statusResults[i].failed !== 0 ){
            objectData.failed = statusResults[i].failed;

            // only add these if it isnt yet present
            if ( statusKeys.indexOf('failed') === -1 ){
              statusKeys.push('failed');
              statusLabels.push('Failed');
              statusColors.push('#d9534f');
            }
          }

          // check if Completed status has any records
          if ( statusResults[i].completed !== 0 ){
            objectData.completed = statusResults[i].completed;

            // only add these if it isnt yet present
            if ( statusKeys.indexOf('completed') === -1 ){
              statusKeys.push('completed');
              statusLabels.push('Completed');
              statusColors.push('#f0ad4e');
            }
          }

          // check if Completed with error(s) status has any records
          if ( statusResults[i].completedWErrors !== 0 ){
            objectData.completedWErrors = statusResults[i].completedWErrors;

            // only add these if it isnt yet present
            if ( statusKeys.indexOf('completedWErrors') === -1 ){
              statusKeys.push('completedWErrors');
              statusLabels.push('Completed With Errors');
              statusColors.push('#5bc0de');
            }
          }

          // check if Successful status has any records
          if ( statusResults[i].successful !== 0 ){
            objectData.successful = statusResults[i].successful;

            // only add these if it isnt yet present
            if ( statusKeys.indexOf('successful') === -1 ){
              statusKeys.push('successful');
              statusLabels.push('Successful');
              statusColors.push('#5cb85c');
            }
          }

          // push objectData to main statusData object
          statusData.push(objectData);

        }

        $scope.statusData = {data: statusData, xkey: 'channel', ykeys: statusKeys, labels: statusLabels, colors: statusColors, stacked: true,};

      },
      function(err){
        Alerting.AlertAddMsg('status', 'danger', 'Channel Load Error: ' + err.status + ' ' + err.data);
      });

    };

    $scope.statusMetricsSuccess = function(statusResults){
      if ( statusResults.length === 0 ){
        Alerting.AlertAddMsg('status', 'warning', 'There has been no transactions received for today');
      }else{
        $scope.updateStatusBarChart(statusResults);
      }
    };

    $scope.statusMetricsError = function(err){
      // add warning message when unable to get data
      Alerting.AlertAddMsg('status', 'danger', 'Transaction Load Error: ' + err.status + ' ' + err.data);
    };

    $scope.getStatusMetrics = function(){
      // reset any load metric alert warnings
      Alerting.AlertReset('status');

      var startDate = moment().startOf('day').toDate();
      var endDate = moment().startOf('hour').add(1, 'hours').toDate();

      // do API call here to pull load metrics
      Api.MetricsStatus.query({
        startDate: startDate,
        endDate: endDate
      }, $scope.statusMetricsSuccess, $scope.statusMetricsError);
    };

    // do the inital load of the transaction status metrics
    $scope.getStatusMetrics();

    /********************************************************************/
    /**         Channel Transactions Status Metric Functions           **/
    /********************************************************************/

    $scope.$on('$destroy', function() {
      // Make sure that the interval is destroyed too
      if (angular.isDefined(dashboardInterval)) {
        $interval.cancel(dashboardInterval);
        dashboardInterval = undefined;
      }
    });

  });
<|endoftext|>