<commit_before>'use strict';

var DimensionError = require('../../../error/DimensionError');

function factory (type, config, load) {

  var equalScalar = load(require('../../../function/relational/equalScalar'));

  var SparseMatrix = type.SparseMatrix;

  /**
   * Iterates over SparseMatrix A and SparseMatrix B nonzero items and invokes the callback function f(Aij, Bij).
   * Callback function invoked MAX(NNZA, NNZB) times
   *
   *
   *          ┌  f(Aij, Bij)  ; A(i,j) !== 0 && B(i,j) !== 0
   * C(i,j) = ┤  A(i,j)       ; A(i,j) !== 0
   *          └  B(i,j)       ; B(i,j) !== 0
   *
   *
   * @param {Matrix}   a                 The SparseMatrix instance (A)
   * @param {Matrix}   b                 The SparseMatrix instance (B)
   * @param {function} callback          The f(Aij,Bij) operation to invoke
   *
   * @return {Matrix}                    SparseMatrix (C)
   *
   * see https://github.com/josdejong/mathjs/pull/346#issuecomment-97620294
   */
  var algorithm04 = function (a, b, callback) {
    // sparse matrix arrays
    var avalues = a._values;
    var aindex = a._index;
    var aptr = a._ptr;
    var asize = a._size;
    var adt = a._datatype;
    var azero = a._zero;
    // sparse matrix arrays
    var bvalues = b._values;
    var bindex = b._index;
    var bptr = b._ptr;
    var bsize = b._size;
    var bdt = b._datatype;
    var bzero = b._zero;

    // validate dimensions
    if (asize.length !== bsize.length)
      throw new DimensionError(asize.length, bsize.length);

    // check rows & columns
    if (asize[0] !== bsize[0] || asize[1] !== bsize[1])
      throw new RangeError('Dimension mismatch. Matrix A (' + asize + ') must match Matrix B (' + bsize + ')');

    // rows & columns
    var rows = asize[0];
    var columns = asize[1];

    // process data types
    var dt = adt && bdt && typeof(azero) !== 'undefined' && azero !== null && typeof(bzero) !== 'undefined' && bzero !== null && adt === bdt ? adt : undefined;
    // callback implementation
    var cf = dt && callback.signatures ? callback.signatures[dt + ',' + dt] || callback : callback;
    // equal
    var ef = dt ? equalScalar.signatures[dt + ',' + dt] || equalScalar : equalScalar;

    // sparse matrix zero value
    var zero = dt ? azero : 0;

    // result arrays
    var cvalues = avalues && bvalues ? [] : undefined;
    var cindex = [];
    var cptr = [];
    // matrix
    var c = new SparseMatrix({
      values: cvalues,
      index: cindex,
      ptr: cptr,
      size: [rows, columns],
      datatype: dt
    });

    // workspace
    var x = avalues && bvalues ? [] : undefined;
    // marks indicating we have a value in x for a given column
    var w = [];

    // vars
    var i, j, k, k0, k1;

    // loop columns
    for (j = 0; j < columns; j++) {
      // update cptr
      cptr[j] = cindex.length;
      // columns mark
      var mark = j + 1;
      // loop A(:,j)
      for (k0 = aptr[j], k1 = aptr[j + 1], k = k0; k < k1; k++) {
        // row
        i = aindex[k];
        // update c
        cindex.push(i);
        // update workspace
        w[i] = mark;
        // check we need to process values
        if (x)
          x[i] = avalues[k];
      }
      // loop B(:,j)
      for (k0 = bptr[j], k1 = bptr[j + 1], k = k0; k < k1; k++) {
        // row
        i = bindex[k];
        // check row exists in workspace
        if (w[i] === mark) {
          // check we need to process values
          if (x)
            x[i] = cf(x[i], bvalues[k]);
        }
        else {
          // update c
          cindex.push(i);
          // update workspace
          w[i] = mark;
          // check we need to process values
          if (x)
            x[i] = cf(x[i], bvalues[k]);
        }
      }
      // check we need to process values (non pattern matrix)
      if (x) {
        // initialize first index in j
        k = cptr[j];
        // loop index in j
        while (k < cindex.length) {
          // row
          i = cindex[k];
          // value @ i
          var v = x[i];
          // check for zero value
          if (!ef(v, zero)) {
            // push value
            cvalues.push(v);
            // increment pointer
            k++;
          }
          else {
            // remove value @ i, do not increment pointer
            cindex.splice(k, 1);
          }
        }
      }
    }
    // update cptr
    cptr[columns] = cindex.length;

    // return sparse matrix
    return c;
  };

  return algorithm04;
}

exports.name = 'algorithm04';
exports.factory = factory;
<commit_msg>Fixed bug in algorithm04<commit_after>'use strict';

var DimensionError = require('../../../error/DimensionError');

function factory (type, config, load) {

  var equalScalar = load(require('../../../function/relational/equalScalar'));

  var SparseMatrix = type.SparseMatrix;

  /**
   * Iterates over SparseMatrix A and SparseMatrix B nonzero items and invokes the callback function f(Aij, Bij).
   * Callback function invoked MAX(NNZA, NNZB) times
   *
   *
   *          ┌  f(Aij, Bij)  ; A(i,j) !== 0 && B(i,j) !== 0
   * C(i,j) = ┤  A(i,j)       ; A(i,j) !== 0
   *          └  B(i,j)       ; B(i,j) !== 0
   *
   *
   * @param {Matrix}   a                 The SparseMatrix instance (A)
   * @param {Matrix}   b                 The SparseMatrix instance (B)
   * @param {function} callback          The f(Aij,Bij) operation to invoke
   *
   * @return {Matrix}                    SparseMatrix (C)
   *
   * see https://github.com/josdejong/mathjs/pull/346#issuecomment-97620294
   */
  var algorithm04 = function (a, b, callback) {
    // sparse matrix arrays
    var avalues = a._values;
    var aindex = a._index;
    var aptr = a._ptr;
    var asize = a._size;
    var adt = a._datatype;
    var azero = a._zero;
    // sparse matrix arrays
    var bvalues = b._values;
    var bindex = b._index;
    var bptr = b._ptr;
    var bsize = b._size;
    var bdt = b._datatype;
    var bzero = b._zero;

    // validate dimensions
    if (asize.length !== bsize.length)
      throw new DimensionError(asize.length, bsize.length);

    // check rows & columns
    if (asize[0] !== bsize[0] || asize[1] !== bsize[1])
      throw new RangeError('Dimension mismatch. Matrix A (' + asize + ') must match Matrix B (' + bsize + ')');

    // rows & columns
    var rows = asize[0];
    var columns = asize[1];

    // process data types
    var dt = adt && bdt && typeof(azero) !== 'undefined' && azero !== null && typeof(bzero) !== 'undefined' && bzero !== null && adt === bdt ? adt : undefined;
    // callback implementation
    var cf = dt && callback.signatures ? callback.signatures[dt + ',' + dt] || callback : callback;
    // equal
    var ef = dt ? equalScalar.signatures[dt + ',' + dt] || equalScalar : equalScalar;

    // sparse matrix zero value
    var zero = dt ? azero : 0;

    // result arrays
    var cvalues = avalues && bvalues ? [] : undefined;
    var cindex = [];
    var cptr = [];
    // matrix
    var c = new SparseMatrix({
      values: cvalues,
      index: cindex,
      ptr: cptr,
      size: [rows, columns],
      datatype: dt
    });

    // workspace
    var x = avalues && bvalues ? [] : undefined;
    // marks indicating we have a value in x for a given column
    var w = [];

    // vars
    var i, j, k, k0, k1;

    // loop columns
    for (j = 0; j < columns; j++) {
      // update cptr
      cptr[j] = cindex.length;
      // columns mark
      var mark = j + 1;
      // loop A(:,j)
      for (k0 = aptr[j], k1 = aptr[j + 1], k = k0; k < k1; k++) {
        // row
        i = aindex[k];
        // update c
        cindex.push(i);
        // update workspace
        w[i] = mark;
        // check we need to process values
        if (x)
          x[i] = avalues[k];
      }
      // loop B(:,j)
      for (k0 = bptr[j], k1 = bptr[j + 1], k = k0; k < k1; k++) {
        // row
        i = bindex[k];
        // check row exists in workspace
        if (w[i] === mark) {
          // check we need to process values
          if (x)
            x[i] = cf(x[i], bvalues[k]);
        }
        else {
          // update c
          cindex.push(i);
          // update workspace
          w[i] = mark;
          // check we need to process values
          if (x)
            x[i] = bvalues[k];
        }
      }
      // check we need to process values (non pattern matrix)
      if (x) {
        // initialize first index in j
        k = cptr[j];
        // loop index in j
        while (k < cindex.length) {
          // row
          i = cindex[k];
          // value @ i
          var v = x[i];
          // check for zero value
          if (!ef(v, zero)) {
            // push value
            cvalues.push(v);
            // increment pointer
            k++;
          }
          else {
            // remove value @ i, do not increment pointer
            cindex.splice(k, 1);
          }
        }
      }
    }
    // update cptr
    cptr[columns] = cindex.length;

    // return sparse matrix
    return c;
  };

  return algorithm04;
}

exports.name = 'algorithm04';
exports.factory = factory;
<|endoftext|>


<commit_before>'use strict';

// Simple and configurable map and reduce dataflow transforms

const _ = require('underscore');
const request = require('abacus-request');
const router = require('abacus-router');
const cluster = require('abacus-cluster');
const transform = require('abacus-transform');
const seqid = require('abacus-seqid');
const dbclient = require('abacus-dbclient');

const extend = _.extend;
const last = _.last;
const rest = _.rest;
const reduce = _.reduce;
const omit = _.omit;
const map = _.map;
const keys = _.keys;

const treduce = transform.reduce;

// Configure test db URL prefix and sink service URLs
process.env.DB = process.env.DB || 'test';

// Mock the cluster module
require.cache[require.resolve('abacus-cluster')].exports =
  extend((app) => app, cluster);

const webapp = require('abacus-webapp');

// Mock the request module
let postspy;
const reqmock = extend({}, request, {
  batch_post: (reqs, cb) => postspy(reqs, cb)
});
require.cache[require.resolve('abacus-request')].exports = reqmock;

const dataflow = require('..');

describe('abacus-dataflow', () => {
  it('runs a map transform and stores its inputs and outputs', (done) => {

    // Create a test Web app
    const app = webapp();

    // Create a schema for our test docs, representing pairs of numbers
    const Pair = {
      validate: (doc) => doc
    };

    // Define a test map transform that computes the sum of a pair of
    // numbers
    const sum = function *(doc, auth) {
      const res = {
        t: doc.t,
        x: doc.x,
        y: doc.y,
        val: doc.x + doc.y
      };
      return [res];
    };

    // Define key and time functions
    const t0 = 1443650828616;
    const iwscope = (doc) => undefined;
    const irscope = (doc) => undefined;
    const ikey = (doc) => '' + doc.x + '/' + doc.y;
    const itime = (doc) => seqid();
    const orscope = (doc) => undefined;
    const okeys = (doc) => ['' + doc.x + '/' + doc.y];
    const otimes = (doc) => [doc.t];

    // Add a dataflow mapper middleware to our test app
    const mapper = dataflow.mapper(sum, {
      input: {
        type: 'pair',
        schema: Pair,
        post: '/v1/pairs',
        get: '/v1/pairs/t/:t/k/:kx/:ky',
        dbname: 'pair',
        wscope: iwscope,
        rscope: irscope,
        key: ikey,
        time: itime
      },
      output: {
        type: 'sum',
        get: '/v1/maps/t/:t/k/:kx/:ky',
        dbname: 'maps',
        rscope: orscope,
        keys: okeys,
        times: otimes
      },
      sink: {
        host: 'http://localhost:9081',
        authentication: () => 'Bearer authentication',
        post: '/v2/maps'
      }
    });
    app.use(mapper);

    app.use(router.batch(app));

    // Initiate a replay of any old inputs
    dataflow.replay(mapper, 1000, (err, vals) => {
      expect(err).to.equal(null);
      expect(vals).to.deep.equal([]);

      // Listen on an ephemeral port
      const server = app.listen(0);

      // Handle callback checks
      let checks = 0;
      const check = () => {
        if(++checks == 6) done();
      };

      // Expect output docs to be posted to the sink service
      const odocs = [];
      postspy = (reqs, cb) => {
        expect(reqs[0][0]).to.equal('http://localhost:9081/v2/maps');

        const val = reqs[0][1];
        expect(val.headers).to.deep.equal({
          authorization: 'Bearer authentication'
        });

        // Check for the expected output doc
        const odoc = odocs[val.body.t];
        expect(omit(
          val.body,'id', 'pair_id', 'processed', 'processed_id'))
          .to.deep.equal(odoc);
        expect(val.body.id).to.match(new RegExp(
          'k/' + odoc.x + '/' + odoc.y + '/t/' + dbclient.pad16(odoc.t)));
        expect(val.body.pair_id).to.match(new RegExp(
          't/00014.*-0-0-0/k/' + odoc.x + '/' + odoc.y));

        cb(undefined, [[undefined, {
          statusCode: 201
        }]]);

        check();
      };

      // Post a set of input docs, including a duplicate
      treduce([1, 2, 3, 3, 4, 5], (accum, ival, i, l, cb) => {

        // The test input doc
        const idoc = {
          t: t0 + ival,
          x: ival,
          y: ival + 1
        };

        // The expected output doc
        const odoc = {
          t: idoc.t,
          x: idoc.x,
          y: idoc.y,
          val: idoc.x + idoc.y
        };
        odocs[odoc.t] = odoc;

        // Post the input doc
        request.post('http://localhost::p/v1/pairs', {
          p: server.address().port,
          auth: {
            bearer: 'test'
          },
          body: idoc

        }, (err, pval) => {

          expect(err).to.equal(undefined);

          if(i === 3) {
            // Expect a duplicate to be detected
            expect(pval.statusCode).to.equal(409);
            cb();
            return;
          }

          // Expect a 201 result
          expect(pval.statusCode).to.equal(201);

          // Get the input doc
          request.get(pval.headers.location, {}, (err, val) => {
            expect(err).to.equal(undefined);
            expect(val.statusCode).to.equal(200);

            expect(omit(val.body,
              'id', 'processed', 'processed_id')).to.deep.equal(idoc);
            expect(val.body.id).to.match(new RegExp(
              't/00014.*-0-0-0/k/' + idoc.x + '/' + idoc.y));

            cb();
          });
        });
      }, undefined, (err, res) => {
        expect(err).to.equal(undefined);
        check();
      });
    });
  });

  it('runs a reduce transform and stores its inputs and outputs', (done) => {

    // Create a test Web app
    const app = webapp();

    // Create a schema for our test docs, representing pairs of numbers
    const Nb = {
      validate: (doc) => doc
    };

    // Define a test reduce transform that accumulates the sum of
    // numbers
    const sum = function *(accums, docs, auth) {
      return rest(reduce(docs, (log, doc) => {
        const res = {
          t: doc.t,
          x: doc.x,
          val: last(log)[0].val + doc.x
        }
        return log.concat([[res]]);
      }, [[
        accums[0] ? accums[0] : {
          val: 0
        }
      ]]));
    };

    // Define key, time and group functions
    const t0 = 1443650828616;
    const iwscope = (doc) => undefined;
    const irscope = (doc) => undefined;
    const ikey = (doc) => '' + doc.x;
    const itime = (doc) => seqid();
    const igroups = (doc) => [doc.x % 2 ? 'odd' : 'even'];
    const orscope = (doc) => undefined;
    const okeys = (doc) => igroups(doc);
    const otimes = (doc) => [doc.t];

    // Add a dataflow reducer middleware to our test app
    const reducer = dataflow.reducer(sum, {
      input: {
        type: 'nb',
        schema: Nb,
        post: '/v1/nbs',
        get: '/v1/nbs/t/:t/k/:kx',
        dbname: 'nbs',
        wscope: iwscope,
        rscope: irscope,
        key: ikey,
        time: itime,
        groups: igroups
      },
      output: {
        type: 'sum',
        get: '/v1/reductions/t/:t/k/:kg',
        dbname: 'reductions',
        rscope: orscope,
        keys: okeys,
        times: otimes
      },
      sink: {
        host: 'http://localhost:9081',
        authentication: () => 'Bearer authentication',
        post: '/v2/reductions'
      }
    });
    app.use(reducer);

    app.use(router.batch(app));

    // Replay any old inputs
    dataflow.replay(reducer, 1000, (err, vals) => {
      expect(err).to.equal(null);
      expect(vals).to.deep.equal([]);

      // Listen on an ephemeral port
      const server = app.listen(0);

      // Handle callback checks
      let checks = 0;
      const check = () => {
        if(++checks == 3) done();
      };

      // Expect output docs to be posted to the sink service
      const oaccum = {
        odd: {
          val: 9
        },
        even: {
          val: 6
        }
      };

      postspy = (reqs, cb) => {
        expect(reqs[0][0]).to.equal('http://localhost:9081/v2/reductions');

        const val = reqs[0][1];
        expect(val.headers).to.deep.equal({
          authorization: 'Bearer authentication'
        });

        // Check for the expected output docs
        const odoc = val.body;
        expect(odoc.id).to.match(new RegExp(
          'k/' + igroups(odoc).join('/') + '/t/' + dbclient.pad16(odoc.t)));
        expect(odoc.nb_id).to.match(new RegExp(
          't/00014.*-0-0-0/k/' + odoc.x));

        map(keys(oaccum), (group) => {
          try {
            expect(omit(odoc,
              'id', 'nb_id', 'processed', 'processed_id', 't', 'x'))
              .to.deep.equal(oaccum[group]);
            check();
          }
          catch(e) {
          }
        });

        cb(undefined, [[undefined, {
          statusCode: 201
        }]]);

        check();
      };

      // Post a set of input docs
      treduce([1, 2, 3, 3, 4, 5], (accum, ival, i, l, cb) => {

        // The test input doc
        const idoc = {
          t: t0 + ival,
          x: ival
        };

        // Post input doc
        request.post('http://localhost::p/v1/nbs', {
          p: server.address().port,
          auth: {
            bearer: 'test'
          },
          body: idoc
        }, (err, pval) => {

          expect(err).to.equal(undefined);

          if(i === 3) {
            // Expect a duplicate to be detected
            expect(pval.statusCode).to.equal(409);
            cb();
            return;
          }

          // Expect a 201 result
          expect(pval.statusCode).to.equal(201);

          // Get the input doc
          request.get(pval.headers.location, {}, (err, val) => {
            expect(err).to.equal(undefined);
            expect(val.statusCode).to.equal(200);

            expect(omit(val.body,
              'id', 'processed', 'processed_id')).to.deep.equal(idoc);
            expect(val.body.id).to.match(
              new RegExp('t/00014.*-0-0-0/k/' + idoc.x));

            cb();
          });
        });
      }, {
        even: 0,
        odd: 0
      }, (err, res) => {
        expect(err).to.equal(undefined);
        check();
      });
    });
  });
});
<commit_msg>Fixed dataflow running incomplete test on reduce function<commit_after>'use strict';

// Simple and configurable map and reduce dataflow transforms

const _ = require('underscore');
const request = require('abacus-request');
const router = require('abacus-router');
const cluster = require('abacus-cluster');
const transform = require('abacus-transform');
const seqid = require('abacus-seqid');
const dbclient = require('abacus-dbclient');

const extend = _.extend;
const last = _.last;
const rest = _.rest;
const reduce = _.reduce;
const omit = _.omit;
const map = _.map;
const keys = _.keys;

const treduce = transform.reduce;

// Configure test db URL prefix and sink service URLs
process.env.DB = process.env.DB || 'test';

// Mock the cluster module
require.cache[require.resolve('abacus-cluster')].exports =
  extend((app) => app, cluster);

const webapp = require('abacus-webapp');

// Mock the request module
let postspy;
const reqmock = extend({}, request, {
  batch_post: (reqs, cb) => postspy(reqs, cb)
});
require.cache[require.resolve('abacus-request')].exports = reqmock;

const dataflow = require('..');

describe('abacus-dataflow', () => {
  it('runs a map transform and stores its inputs and outputs', (done) => {

    // Create a test Web app
    const app = webapp();

    // Create a schema for our test docs, representing pairs of numbers
    const Pair = {
      validate: (doc) => doc
    };

    // Define a test map transform that computes the sum of a pair of
    // numbers
    const sum = function *(doc, auth) {
      const res = {
        t: doc.t,
        x: doc.x,
        y: doc.y,
        val: doc.x + doc.y
      };
      return [res];
    };

    // Define key and time functions
    const t0 = 1443650828616;
    const iwscope = (doc) => undefined;
    const irscope = (doc) => undefined;
    const ikey = (doc) => '' + doc.x + '/' + doc.y;
    const itime = (doc) => seqid();
    const orscope = (doc) => undefined;
    const okeys = (doc) => ['' + doc.x + '/' + doc.y];
    const otimes = (doc) => [doc.t];

    // Add a dataflow mapper middleware to our test app
    const mapper = dataflow.mapper(sum, {
      input: {
        type: 'pair',
        schema: Pair,
        post: '/v1/pairs',
        get: '/v1/pairs/t/:t/k/:kx/:ky',
        dbname: 'pair',
        wscope: iwscope,
        rscope: irscope,
        key: ikey,
        time: itime
      },
      output: {
        type: 'sum',
        get: '/v1/maps/t/:t/k/:kx/:ky',
        dbname: 'maps',
        rscope: orscope,
        keys: okeys,
        times: otimes
      },
      sink: {
        host: 'http://localhost:9081',
        authentication: () => 'Bearer authentication',
        post: '/v2/maps'
      }
    });
    app.use(mapper);

    app.use(router.batch(app));

    // Initiate a replay of any old inputs
    dataflow.replay(mapper, 1000, (err, vals) => {
      expect(err).to.equal(null);
      expect(vals).to.deep.equal([]);

      // Listen on an ephemeral port
      const server = app.listen(0);

      // Handle callback checks
      let checks = 0;
      const check = () => {
        if(++checks == 6) done();
      };

      // Expect output docs to be posted to the sink service
      const odocs = [];
      postspy = (reqs, cb) => {
        expect(reqs[0][0]).to.equal('http://localhost:9081/v2/maps');

        const val = reqs[0][1];
        expect(val.headers).to.deep.equal({
          authorization: 'Bearer authentication'
        });

        // Check for the expected output doc
        const odoc = odocs[val.body.t];
        expect(omit(
          val.body,'id', 'pair_id', 'processed', 'processed_id'))
          .to.deep.equal(odoc);
        expect(val.body.id).to.match(new RegExp(
          'k/' + odoc.x + '/' + odoc.y + '/t/' + dbclient.pad16(odoc.t)));
        expect(val.body.pair_id).to.match(new RegExp(
          't/00014.*-0-0-0/k/' + odoc.x + '/' + odoc.y));

        cb(undefined, [[undefined, {
          statusCode: 201
        }]]);

        check();
      };

      // Post a set of input docs, including a duplicate
      treduce([1, 2, 3, 3, 4, 5], (accum, ival, i, l, cb) => {

        // The test input doc
        const idoc = {
          t: t0 + ival,
          x: ival,
          y: ival + 1
        };

        // The expected output doc
        const odoc = {
          t: idoc.t,
          x: idoc.x,
          y: idoc.y,
          val: idoc.x + idoc.y
        };
        odocs[odoc.t] = odoc;

        // Post the input doc
        request.post('http://localhost::p/v1/pairs', {
          p: server.address().port,
          auth: {
            bearer: 'test'
          },
          body: idoc

        }, (err, pval) => {

          expect(err).to.equal(undefined);

          if(i === 3) {
            // Expect a duplicate to be detected
            expect(pval.statusCode).to.equal(409);
            cb();
            return;
          }

          // Expect a 201 result
          expect(pval.statusCode).to.equal(201);

          // Get the input doc
          request.get(pval.headers.location, {}, (err, val) => {
            expect(err).to.equal(undefined);
            expect(val.statusCode).to.equal(200);

            expect(omit(val.body,
              'id', 'processed', 'processed_id')).to.deep.equal(idoc);
            expect(val.body.id).to.match(new RegExp(
              't/00014.*-0-0-0/k/' + idoc.x + '/' + idoc.y));

            cb();
          });
        });
      }, undefined, (err, res) => {
        expect(err).to.equal(undefined);
        check();
      });
    });
  });

  it('runs a reduce transform and stores its inputs and outputs', (done) => {

    // Create a test Web app
    const app = webapp();

    // Create a schema for our test docs, representing pairs of numbers
    const Nb = {
      validate: (doc) => doc
    };

    // Define a test reduce transform that accumulates the sum of
    // numbers
    const sum = function *(accums, docs, auth) {
      return rest(reduce(docs, (log, doc) => {
        const res = {
          t: doc.t,
          x: doc.x,
          val: last(log)[0].val + doc.x
        }
        return log.concat([[res]]);
      }, [[
        accums[0] ? accums[0] : {
          val: 0
        }
      ]]));
    };

    // Define key, time and group functions
    const t0 = 1443650828616;
    const iwscope = (doc) => undefined;
    const irscope = (doc) => undefined;
    const ikey = (doc) => '' + doc.x;
    const itime = (doc) => seqid();
    const igroups = (doc) => [doc.x % 2 ? 'odd' : 'even'];
    const orscope = (doc) => undefined;
    const okeys = (doc) => igroups(doc);
    const otimes = (doc) => [doc.t];

    // Add a dataflow reducer middleware to our test app
    const reducer = dataflow.reducer(sum, {
      input: {
        type: 'nb',
        schema: Nb,
        post: '/v1/nbs',
        get: '/v1/nbs/t/:t/k/:kx',
        dbname: 'nbs',
        wscope: iwscope,
        rscope: irscope,
        key: ikey,
        time: itime,
        groups: igroups
      },
      output: {
        type: 'sum',
        get: '/v1/reductions/t/:t/k/:kg',
        dbname: 'reductions',
        rscope: orscope,
        keys: okeys,
        times: otimes
      },
      sink: {
        host: 'http://localhost:9081',
        authentication: () => 'Bearer authentication',
        post: '/v2/reductions'
      }
    });
    app.use(reducer);

    app.use(router.batch(app));

    // Replay any old inputs
    dataflow.replay(reducer, 1000, (err, vals) => {
      expect(err).to.equal(null);
      expect(vals).to.deep.equal([]);

      // Listen on an ephemeral port
      const server = app.listen(0);

      // Handle callback checks
      let checks = 0;
      const check = () => {
        if(++checks == 8) done();
      };

      // Expect output docs to be posted to the sink service
      const oaccum = {
        odd: {
          val: 9
        },
        even: {
          val: 6
        }
      };

      postspy = (reqs, cb) => {
        expect(reqs[0][0]).to.equal('http://localhost:9081/v2/reductions');

        const val = reqs[0][1];
        expect(val.headers).to.deep.equal({
          authorization: 'Bearer authentication'
        });

        // Check for the expected output docs
        const odoc = val.body;
        expect(odoc.id).to.match(new RegExp(
          'k/' + igroups(odoc).join('/') + '/t/' + dbclient.pad16(odoc.t)));
        expect(odoc.nb_id).to.match(new RegExp(
          't/00014.*-0-0-0/k/' + odoc.x));

        map(keys(oaccum), (group) => {
          try {
            expect(omit(odoc,
              'id', 'nb_id', 'processed', 'processed_id', 't', 'x'))
              .to.deep.equal(oaccum[group]);
            check();
          }
          catch(e) {
          }
        });

        cb(undefined, [[undefined, {
          statusCode: 201
        }]]);

        check();
      };

      // Post a set of input docs
      treduce([1, 2, 3, 3, 4, 5], (accum, ival, i, l, cb) => {

        // The test input doc
        const idoc = {
          t: t0 + ival,
          x: ival
        };

        // Post input doc
        request.post('http://localhost::p/v1/nbs', {
          p: server.address().port,
          auth: {
            bearer: 'test'
          },
          body: idoc
        }, (err, pval) => {

          expect(err).to.equal(undefined);

          if(i === 3) {
            // Expect a duplicate to be detected
            expect(pval.statusCode).to.equal(409);
            cb();
            return;
          }

          // Expect a 201 result
          expect(pval.statusCode).to.equal(201);

          // Get the input doc
          request.get(pval.headers.location, {}, (err, val) => {
            expect(err).to.equal(undefined);
            expect(val.statusCode).to.equal(200);

            expect(omit(val.body,
              'id', 'processed', 'processed_id')).to.deep.equal(idoc);
            expect(val.body.id).to.match(
              new RegExp('t/00014.*-0-0-0/k/' + idoc.x));

            cb();
          });
        });
      }, {
        even: 0,
        odd: 0
      }, (err, res) => {
        expect(err).to.equal(undefined);
        check();
      });
    });
  });
});
<|endoftext|>


<commit_before>/*jshint boss:true*/

var objectCreate = Object.create || function(obj) {
  function F() {}
  F.prototype = obj;
  return new F();
};

function isSpace(char) {
  return (/[\n\r\t ]/).test(char);
}

function isAlpha(char) {
  return (/[A-Za-z]/).test(char);
}

function Tokenizer(input) {
  this.input = input;
  this.char = 0;
  this.state = 'data';
  this.token = null;
}

Tokenizer.prototype = {
  tokenize: function() {
    var tokens = [], token;

    while (true) {
      token = this.lex();
      if (token === 'EOF') { break; }
      if (token) { tokens.push(token); }
    }

    if (this.token) {
      tokens.push(this.token);
    }

    return tokens;
  },

  tokenizePart: function(string) {
    this.input += string;
    var tokens = [], token;

    while (this.char < this.input.length) {
      token = this.lex();
      if (token) { tokens.push(token); }
    }

    this.tokens = (this.tokens || []).concat(tokens);
    return tokens;
  },

  tokenizeEOF: function() {
    if (this.token) {
      return this.token;
    }
  },

  tag: function(Type, char) {
    char = char.toLowerCase();

    var lastToken = this.token;
    this.token = new Type(char);
    this.state = 'tagName';
    return lastToken;
  },

  selfClosing: function() {
    this.token.selfClosing = true;
  },

  attribute: function(char) {
    this.token.startAttribute(char);
    this.state = 'attributeName';
  },

  addToAttributeName: function(char) {
    this.token.addToAttributeName(char.toLowerCase());
  },

  addToAttributeValue: function(char) {
    this.token.addToAttributeValue(char);
  },

  commentStart: function() {
    var lastToken = this.token;
    this.token = new CommentToken();
    this.state = 'commentStart';
    return lastToken;
  },

  addToComment: function(char) {
    this.token.addChar(char);
  },

  emitData: function() {
    var lastToken = this.token;
    this.token = null;
    this.state = 'tagOpen';
    return lastToken;
  },

  emitToken: function() {
    var lastToken = this.token.finalize();
    this.token = null;
    this.state = 'data';
    return lastToken;
  },

  addData: function(char) {
    if (this.token === null) {
      this.token = new Chars();
    }

    this.token.addChar(char);
  },

  lex: function() {
    var char = this.input.charAt(this.char++);

    if (char) {
      // console.log(this.state, char);
      return this.states[this.state].call(this, char);
    } else {
      return 'EOF';
    }
  },

  states: {
    data: function(char) {
      if (char === "<") {
        return this.emitData();
      } else {
        this.addData(char);
      }
    },

    tagOpen: function(char) {
      if (char === "!") {
        this.state = 'markupDeclaration';
      } else if (char === "/") {
        this.state = 'endTagOpen';
      } else if (!isSpace(char)) {
        return this.tag(StartTag, char);
      }
    },

    markupDeclaration: function(char) {
      if (char === "-" && this.input[this.char] === "-") {
        this.char++;
        this.commentStart();
      }
    },

    commentStart: function(char) {
      if (char === "-") {
        this.state = 'commentStartDash';
      } else if (char === ">") {
        return this.emitToken();
      } else {
        this.addToComment(char);
        this.state = 'comment';
      }
    },

    commentStartDash: function(char) {
      if (char === "-") {
        this.state = 'commentEnd';
      } else if (char === ">") {
        return this.emitToken();
      } else {
        this.addToComment("-");
        this.state = 'comment';
      }
    },

    comment: function(char) {
      if (char === "-") {
        this.state = 'commentEndDash';
      } else {
        this.addToComment(char);
      }
    },

    commentEndDash: function(char) {
      if (char === "-") {
        this.state = 'commentEnd';
      } else {
        this.addToComment('-' + char);
        this.state = 'comment';
      }
    },

    commentEnd: function(char) {
      if (char === ">") {
        return this.emitToken();
      }
    },

    tagName: function(char) {
      if (isSpace(char)) {
        this.state = 'beforeAttributeName';
      } else if(/[A-Za-z]/.test(char)) {
        this.token.addToTagName(char);
      } else if (char === ">") {
        return this.emitToken();
      }
    },

    beforeAttributeName: function(char) {
      if (isSpace(char)) {
        return;
      } else if (char === "/") {
        this.state = 'selfClosingStartTag';
      } else if (char === ">") {
        return this.emitToken();
      } else {
        this.attribute(char);
      }
    },

    attributeName: function(char) {
      if (isSpace(char)) {
        this.state = 'afterAttributeName';
      } else if (char === "/") {
        this.state = 'selfClosingStartTag';
      } else if (char === "=") {
        this.state = 'beforeAttributeValue';
      } else if (char === ">") {
        return this.emitToken();
      } else {
        this.addToAttributeName(char);
      }
    },

    beforeAttributeValue: function(char) {
      if (isSpace(char)) {
        return;
      } else if (char === '"') {
        this.state = 'attributeValueDoubleQuoted';
      } else if (char === "'") {
        this.state = 'attributeValueSingleQuoted';
      } else if (char === ">") {
        return this.emitToken();
      } else {
        this.state = 'attributeValueUnquoted';
        this.addToAttributeValue(char);
      }
    },

    attributeValueDoubleQuoted: function(char) {
      if (char === '"') {
        this.state = 'afterAttributeValueQuoted';
      } else {
        this.addToAttributeValue(char);
      }
    },

    attributeValueSingleQuoted: function(char) {
      if (char === "'") {
        this.state = 'afterAttributeValueQuoted';
      } else {
        this.addToAttributeValue(char);
      }
    },

    attributeValueUnquoted: function(char) {
      if (isSpace(char)) {
        this.state = 'beforeAttributeName';
      } else if (char === ">") {
        return this.emitToken();
      } else {
        this.addToAttributeValue(char);
      }
    },

    afterAttributeValueQuoted: function(char) {
      if (isSpace(char)) {
        this.state = 'beforeAttributeName';
      } else if (char === "/") {
        this.state = 'selfClosingStartTag';
      } else if (char === ">") {
        return this.emitToken();
      } else {
        this.char--;
        this.state = 'beforeAttributeName';
      }
    },

    selfClosingStartTag: function(char) {
      if (char === ">") {
        this.selfClosing();
        return this.emitToken();
      } else {
        this.char--;
        this.state = 'beforeAttributeName';
      }
    },

    endTagOpen: function(char) {
      if (isAlpha(char)) {
        this.tag(EndTag, char);
      }
    }
  }
};

function Tag(tagName, attributes, options) {
  this.tagName = tagName || "";
  this.attributes = attributes || [];
  this.selfClosing = options ? options.selfClosing : false;
}

Tag.prototype = {
  constructor: Tag,

  addToTagName: function(char) {
    this.tagName += char;
  },

  startAttribute: function(char) {
    this.currentAttribute = [char.toLowerCase(), null];
    this.attributes.push(this.currentAttribute);
  },

  addToAttributeName: function(char) {
    this.currentAttribute[0] += char;
  },

  addToAttributeValue: function(char) {
    this.currentAttribute[1] = this.currentAttribute[1] || "";
    this.currentAttribute[1] += char;
  },

  finalize: function() {
    delete this.currentAttribute;
    return this;
  }
};

function StartTag() {
  Tag.apply(this, arguments);
}

StartTag.prototype = objectCreate(Tag.prototype);
StartTag.prototype.type = 'StartTag';
StartTag.prototype.constructor = StartTag;

StartTag.prototype.toHTML = function() {
  return config.generateTag(this);
};

function generateTag(tag) {
  var out = "<";
  out += tag.tagName;

  if (tag.attributes.length) {
    out += " " + config.generateAttributes(tag.attributes);
  }

  out += ">";

  return out;
}

function generateAttributes(attributes) {
  var out = [], attribute, attrString, value;

  for (var i=0, l=attributes.length; i<l; i++) {
    attribute = attributes[i];

    out.push(config.generateAttribute.apply(this, attribute));
  }

  return out.join(" ");
}

function generateAttribute(name, value) {
  var attrString = name;

  if (value) {
    value = value.replace(/"/, '\\"');
    attrString += "=\"" + value + "\"";
  }

  return attrString;
}

function EndTag() {
  Tag.apply(this, arguments);
}

EndTag.prototype = objectCreate(Tag.prototype);
EndTag.prototype.type = 'EndTag';
EndTag.prototype.constructor = EndTag;

EndTag.prototype.toHTML = function() {
  var out = "</";
  out += this.tagName;
  out += ">";

  return out;
};

function Chars(chars) {
  this.chars = chars || "";
}

Chars.prototype = {
  type: 'Chars',
  constructor: Chars,

  addChar: function(char) {
    this.chars += char;
  },

  toHTML: function() {
    return this.chars;
  }
};

function CommentToken() {
  this.chars = "";
}

CommentToken.prototype = {
  type: 'CommentToken',
  constructor: CommentToken,

  finalize: function() { return this; },

  addChar: function(char) {
    this.chars += char;
  },

  toHTML: function() {
    return "<!--" + this.chars + "-->";
  }
};

function tokenize(input) {
  var tokenizer = new Tokenizer(input);
  return tokenizer.tokenize();
}

function generate(tokens) {
  var output = "";

  for (var i=0, l=tokens.length; i<l; i++) {
    output += tokens[i].toHTML();
  }

  return output;
}

var config = {
  generateAttributes: generateAttributes,
  generateAttribute: generateAttribute,
  generateTag: generateTag
};

var original = {
  generateAttributes: generateAttributes,
  generateAttribute: generateAttribute,
  generateTag: generateTag
};

function configure(name, value) {
  config[name] = value;
}

export { Tokenizer, tokenize, generate, configure, original, StartTag, EndTag, Chars, CommentToken };<commit_msg>fix #1<commit_after>/*jshint boss:true*/

var objectCreate = Object.create || function(obj) {
  function F() {}
  F.prototype = obj;
  return new F();
};

function isSpace(char) {
  return (/[\n\r\t ]/).test(char);
}

function isAlpha(char) {
  return (/[A-Za-z]/).test(char);
}

function Tokenizer(input) {
  this.input = input;
  this.char = 0;
  this.state = 'data';
  this.token = null;
}

Tokenizer.prototype = {
  tokenize: function() {
    var tokens = [], token;

    while (true) {
      token = this.lex();
      if (token === 'EOF') { break; }
      if (token) { tokens.push(token); }
    }

    if (this.token) {
      tokens.push(this.token);
    }

    return tokens;
  },

  tokenizePart: function(string) {
    this.input += string;
    var tokens = [], token;

    while (this.char < this.input.length) {
      token = this.lex();
      if (token) { tokens.push(token); }
    }

    this.tokens = (this.tokens || []).concat(tokens);
    return tokens;
  },

  tokenizeEOF: function() {
    var token = this.token;
    if (token) {
      this.token = null;
      return token;
    }
  },

  tag: function(Type, char) {
    char = char.toLowerCase();

    var lastToken = this.token;
    this.token = new Type(char);
    this.state = 'tagName';
    return lastToken;
  },

  selfClosing: function() {
    this.token.selfClosing = true;
  },

  attribute: function(char) {
    this.token.startAttribute(char);
    this.state = 'attributeName';
  },

  addToAttributeName: function(char) {
    this.token.addToAttributeName(char.toLowerCase());
  },

  addToAttributeValue: function(char) {
    this.token.addToAttributeValue(char);
  },

  commentStart: function() {
    var lastToken = this.token;
    this.token = new CommentToken();
    this.state = 'commentStart';
    return lastToken;
  },

  addToComment: function(char) {
    this.token.addChar(char);
  },

  emitData: function() {
    var lastToken = this.token;
    this.token = null;
    this.state = 'tagOpen';
    return lastToken;
  },

  emitToken: function() {
    var lastToken = this.token.finalize();
    this.token = null;
    this.state = 'data';
    return lastToken;
  },

  addData: function(char) {
    if (this.token === null) {
      this.token = new Chars();
    }

    this.token.addChar(char);
  },

  lex: function() {
    var char = this.input.charAt(this.char++);

    if (char) {
      // console.log(this.state, char);
      return this.states[this.state].call(this, char);
    } else {
      return 'EOF';
    }
  },

  states: {
    data: function(char) {
      if (char === "<") {
        return this.emitData();
      } else {
        this.addData(char);
      }
    },

    tagOpen: function(char) {
      if (char === "!") {
        this.state = 'markupDeclaration';
      } else if (char === "/") {
        this.state = 'endTagOpen';
      } else if (!isSpace(char)) {
        return this.tag(StartTag, char);
      }
    },

    markupDeclaration: function(char) {
      if (char === "-" && this.input[this.char] === "-") {
        this.char++;
        this.commentStart();
      }
    },

    commentStart: function(char) {
      if (char === "-") {
        this.state = 'commentStartDash';
      } else if (char === ">") {
        return this.emitToken();
      } else {
        this.addToComment(char);
        this.state = 'comment';
      }
    },

    commentStartDash: function(char) {
      if (char === "-") {
        this.state = 'commentEnd';
      } else if (char === ">") {
        return this.emitToken();
      } else {
        this.addToComment("-");
        this.state = 'comment';
      }
    },

    comment: function(char) {
      if (char === "-") {
        this.state = 'commentEndDash';
      } else {
        this.addToComment(char);
      }
    },

    commentEndDash: function(char) {
      if (char === "-") {
        this.state = 'commentEnd';
      } else {
        this.addToComment('-' + char);
        this.state = 'comment';
      }
    },

    commentEnd: function(char) {
      if (char === ">") {
        return this.emitToken();
      }
    },

    tagName: function(char) {
      if (isSpace(char)) {
        this.state = 'beforeAttributeName';
      } else if(/[A-Za-z]/.test(char)) {
        this.token.addToTagName(char);
      } else if (char === ">") {
        return this.emitToken();
      }
    },

    beforeAttributeName: function(char) {
      if (isSpace(char)) {
        return;
      } else if (char === "/") {
        this.state = 'selfClosingStartTag';
      } else if (char === ">") {
        return this.emitToken();
      } else {
        this.attribute(char);
      }
    },

    attributeName: function(char) {
      if (isSpace(char)) {
        this.state = 'afterAttributeName';
      } else if (char === "/") {
        this.state = 'selfClosingStartTag';
      } else if (char === "=") {
        this.state = 'beforeAttributeValue';
      } else if (char === ">") {
        return this.emitToken();
      } else {
        this.addToAttributeName(char);
      }
    },

    beforeAttributeValue: function(char) {
      if (isSpace(char)) {
        return;
      } else if (char === '"') {
        this.state = 'attributeValueDoubleQuoted';
      } else if (char === "'") {
        this.state = 'attributeValueSingleQuoted';
      } else if (char === ">") {
        return this.emitToken();
      } else {
        this.state = 'attributeValueUnquoted';
        this.addToAttributeValue(char);
      }
    },

    attributeValueDoubleQuoted: function(char) {
      if (char === '"') {
        this.state = 'afterAttributeValueQuoted';
      } else {
        this.addToAttributeValue(char);
      }
    },

    attributeValueSingleQuoted: function(char) {
      if (char === "'") {
        this.state = 'afterAttributeValueQuoted';
      } else {
        this.addToAttributeValue(char);
      }
    },

    attributeValueUnquoted: function(char) {
      if (isSpace(char)) {
        this.state = 'beforeAttributeName';
      } else if (char === ">") {
        return this.emitToken();
      } else {
        this.addToAttributeValue(char);
      }
    },

    afterAttributeValueQuoted: function(char) {
      if (isSpace(char)) {
        this.state = 'beforeAttributeName';
      } else if (char === "/") {
        this.state = 'selfClosingStartTag';
      } else if (char === ">") {
        return this.emitToken();
      } else {
        this.char--;
        this.state = 'beforeAttributeName';
      }
    },

    selfClosingStartTag: function(char) {
      if (char === ">") {
        this.selfClosing();
        return this.emitToken();
      } else {
        this.char--;
        this.state = 'beforeAttributeName';
      }
    },

    endTagOpen: function(char) {
      if (isAlpha(char)) {
        this.tag(EndTag, char);
      }
    }
  }
};

function Tag(tagName, attributes, options) {
  this.tagName = tagName || "";
  this.attributes = attributes || [];
  this.selfClosing = options ? options.selfClosing : false;
}

Tag.prototype = {
  constructor: Tag,

  addToTagName: function(char) {
    this.tagName += char;
  },

  startAttribute: function(char) {
    this.currentAttribute = [char.toLowerCase(), null];
    this.attributes.push(this.currentAttribute);
  },

  addToAttributeName: function(char) {
    this.currentAttribute[0] += char;
  },

  addToAttributeValue: function(char) {
    this.currentAttribute[1] = this.currentAttribute[1] || "";
    this.currentAttribute[1] += char;
  },

  finalize: function() {
    delete this.currentAttribute;
    return this;
  }
};

function StartTag() {
  Tag.apply(this, arguments);
}

StartTag.prototype = objectCreate(Tag.prototype);
StartTag.prototype.type = 'StartTag';
StartTag.prototype.constructor = StartTag;

StartTag.prototype.toHTML = function() {
  return config.generateTag(this);
};

function generateTag(tag) {
  var out = "<";
  out += tag.tagName;

  if (tag.attributes.length) {
    out += " " + config.generateAttributes(tag.attributes);
  }

  out += ">";

  return out;
}

function generateAttributes(attributes) {
  var out = [], attribute, attrString, value;

  for (var i=0, l=attributes.length; i<l; i++) {
    attribute = attributes[i];

    out.push(config.generateAttribute.apply(this, attribute));
  }

  return out.join(" ");
}

function generateAttribute(name, value) {
  var attrString = name;

  if (value) {
    value = value.replace(/"/, '\\"');
    attrString += "=\"" + value + "\"";
  }

  return attrString;
}

function EndTag() {
  Tag.apply(this, arguments);
}

EndTag.prototype = objectCreate(Tag.prototype);
EndTag.prototype.type = 'EndTag';
EndTag.prototype.constructor = EndTag;

EndTag.prototype.toHTML = function() {
  var out = "</";
  out += this.tagName;
  out += ">";

  return out;
};

function Chars(chars) {
  this.chars = chars || "";
}

Chars.prototype = {
  type: 'Chars',
  constructor: Chars,

  addChar: function(char) {
    this.chars += char;
  },

  toHTML: function() {
    return this.chars;
  }
};

function CommentToken() {
  this.chars = "";
}

CommentToken.prototype = {
  type: 'CommentToken',
  constructor: CommentToken,

  finalize: function() { return this; },

  addChar: function(char) {
    this.chars += char;
  },

  toHTML: function() {
    return "<!--" + this.chars + "-->";
  }
};

function tokenize(input) {
  var tokenizer = new Tokenizer(input);
  return tokenizer.tokenize();
}

function generate(tokens) {
  var output = "";

  for (var i=0, l=tokens.length; i<l; i++) {
    output += tokens[i].toHTML();
  }

  return output;
}

var config = {
  generateAttributes: generateAttributes,
  generateAttribute: generateAttribute,
  generateTag: generateTag
};

var original = {
  generateAttributes: generateAttributes,
  generateAttribute: generateAttribute,
  generateTag: generateTag
};

function configure(name, value) {
  config[name] = value;
}

export { Tokenizer, tokenize, generate, configure, original, StartTag, EndTag, Chars, CommentToken };
<|endoftext|>


<commit_before>/**
 * @file enableCommand command
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license GPL-3.0
 */

exports.exec = async (Bastion, message, args) => {
  let disabledCommands, description;
  if (args.name) {
    let command = args.name.toLowerCase();

    if (Bastion.commands.has(command) || Bastion.aliases.has(command)) {
      if (Bastion.commands.has(command)) {
        command = Bastion.commands.get(command);
      }
      else if (Bastion.aliases.has(command)) {
        command = Bastion.commands.get(Bastion.aliases.get(command).toLowerCase());
      }
    }
    else {
      /**
      * Error condition is encountered.
      * @fires error
      */
      return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'notFound', 'command'), message.channel);
    }

    if (![ 'owner', 'guild_admin' ].includes(command.config.module)) {
      let guildModel = await Bastion.database.models.guild.findOne({
        attributes: [ 'disabledCommands' ],
        where: {
          guildID: message.guild.id
        }
      });
      if (guildModel.dataValues.disabledCommands) {
        if (guildModel.dataValues.disabledCommands.includes(command.help.name.toLowerCase())) {
          guildModel.dataValues.disabledCommands.splice(guildModel.dataValues.disabledCommands.indexOf(command.help.name.toLowerCase()), 1);

          await Bastion.database.models.guild.update({
            disabledCommands: guildModel.dataValues.disabledCommands
          },
          {
            where: {
              guildID: message.guild.id
            },
            fields: [ 'disabledCommands' ]
          });
        }
      }
    }

    description = Bastion.i18n.info(message.guild.language, 'enableCommand', message.author.tag, command.help.name);
  }
  else if (args.module) {
    args.module = args.module.join('_').toLowerCase();

    disabledCommands = Bastion.commands.filter(c => c.config.module === args.module).map(c => c.help.name.toLowerCase());

    let guildModel = await Bastion.database.models.guild.findOne({
      attributes: [ 'disabledCommands' ],
      where: {
        guildID: message.guild.id
      }
    });
    if (guildModel.dataValues.disabledCommands) {
      disabledCommands = guildModel.dataValues.disabledCommands.filter(value => !disabledCommands.includes(value));
    }

    description = Bastion.i18n.info(message.guild.language, 'enableModule', message.author.tag, args.module);

    await Bastion.database.models.guild.update({
      disabledCommands: disabledCommands
    },
    {
      where: {
        guildID: message.guild.id
      },
      fields: [ 'disabledCommands' ]
    });
  }
  else if (args.all) {
    await Bastion.database.models.guild.update({
      disabledCommands: null
    },
    {
      where: {
        guildID: message.guild.id
      },
      fields: [ 'disabledCommands' ]
    });
    description = Bastion.i18n.info(message.guild.language, 'enableAllCommands', message.author.tag);
  }
  else {
    /**
     * The command was ran with invalid parameters.
     * @fires commandUsage
     */
    return Bastion.emit('commandUsage', message, this.help);
  }

  message.channel.send({
    embed: {
      color: Bastion.colors.GREEN,
      description: description
    }
  }).catch(e => {
    Bastion.log.error(e);
  });
};

exports.config = {
  aliases: [ 'enablecmd' ],
  enabled: true,
  argsDefinitions: [
    { name: 'name', type: String, defaultOption: true },
    { name: 'module', type: String, multiple: true, alias: 'm' },
    { name: 'all', type: Boolean, alias: 'a' }
  ]
};

exports.help = {
  name: 'enableCommand',
  description: 'Enable disabled command/module in your server.',
  botPermission: '',
  userTextPermission: 'MANAGE_GUILD',
  userVoicePermission: '',
  usage: 'enableCommand < COMMAND_NAME | --module MODULE NAME | --all >',
  example: [ 'enableCommand echo', 'enableCommand --module game stats', 'enableCommand --all' ]
};
<commit_msg>commands: refactor enableCommand command<commit_after>/**
 * @file enableCommand command
 * @author Sankarsan Kampa (a.k.a k3rn31p4nic)
 * @license GPL-3.0
 */

exports.exec = async (Bastion, message, args) => {
  let disabledCommands, description;
  if (args.name) {
    let command = args.name.toLowerCase();

    if (Bastion.commands.has(command) || Bastion.aliases.has(command)) {
      if (Bastion.commands.has(command)) {
        command = Bastion.commands.get(command);
      }
      else if (Bastion.aliases.has(command)) {
        command = Bastion.commands.get(Bastion.aliases.get(command).toLowerCase());
      }
    }
    else {
      /**
       * Error condition is encountered.
       * @fires error
       */
      return Bastion.emit('error', '', Bastion.i18n.error(message.guild.language, 'notFound', 'command'), message.channel);
    }

    if (![ 'owner', 'guild_admin' ].includes(command.config.module)) {
      let guildModel = await Bastion.database.models.guild.findOne({
        attributes: [ 'disabledCommands' ],
        where: {
          guildID: message.guild.id
        }
      });
      if (guildModel.dataValues.disabledCommands) {
        if (guildModel.dataValues.disabledCommands.includes(command.help.name.toLowerCase())) {
          guildModel.dataValues.disabledCommands.splice(guildModel.dataValues.disabledCommands.indexOf(command.help.name.toLowerCase()), 1);

          await Bastion.database.models.guild.update({
            disabledCommands: guildModel.dataValues.disabledCommands
          },
          {
            where: {
              guildID: message.guild.id
            },
            fields: [ 'disabledCommands' ]
          });
        }
      }
    }

    description = Bastion.i18n.info(message.guild.language, 'enableCommand', message.author.tag, command.help.name);
  }
  else if (args.module) {
    args.module = args.module.join('_').toLowerCase();

    disabledCommands = Bastion.commands.filter(c => c.config.module === args.module).map(c => c.help.name.toLowerCase());

    let guildModel = await Bastion.database.models.guild.findOne({
      attributes: [ 'disabledCommands' ],
      where: {
        guildID: message.guild.id
      }
    });
    if (guildModel.dataValues.disabledCommands) {
      disabledCommands = guildModel.dataValues.disabledCommands.filter(command => !disabledCommands.includes(command));
    }

    description = Bastion.i18n.info(message.guild.language, 'enableModule', message.author.tag, args.module);

    await Bastion.database.models.guild.update({
      disabledCommands: disabledCommands
    },
    {
      where: {
        guildID: message.guild.id
      },
      fields: [ 'disabledCommands' ]
    });
  }
  else if (args.all) {
    await Bastion.database.models.guild.update({
      disabledCommands: null
    },
    {
      where: {
        guildID: message.guild.id
      },
      fields: [ 'disabledCommands' ]
    });
    description = Bastion.i18n.info(message.guild.language, 'enableAllCommands', message.author.tag);
  }
  else {
    /**
     * The command was ran with invalid parameters.
     * @fires commandUsage
     */
    return Bastion.emit('commandUsage', message, this.help);
  }

  message.channel.send({
    embed: {
      color: Bastion.colors.GREEN,
      description: description
    }
  }).catch(e => {
    Bastion.log.error(e);
  });
};

exports.config = {
  aliases: [ 'enablecmd' ],
  enabled: true,
  argsDefinitions: [
    { name: 'name', type: String, defaultOption: true },
    { name: 'module', type: String, multiple: true, alias: 'm' },
    { name: 'all', type: Boolean, alias: 'a' }
  ]
};

exports.help = {
  name: 'enableCommand',
  description: 'Enable disabled command/module in your server.',
  botPermission: '',
  userTextPermission: 'MANAGE_GUILD',
  userVoicePermission: '',
  usage: 'enableCommand < COMMAND_NAME | --module MODULE NAME | --all >',
  example: [ 'enableCommand echo', 'enableCommand --module game stats', 'enableCommand --all' ]
};
<|endoftext|>


<commit_before>/*jslint node: true */
"use strict";

var util = require('util'),
    qlobber = require('..'),
    Qlobber = qlobber.Qlobber;

function QlobberSub (options)
{
    Qlobber.call(this, options);
    this.subscriptionsCount = 0;
}

util.inherits(QlobberSub, Qlobber);

QlobberSub.prototype._initial_value = function (val)
{
    this.subscriptionsCount += 1;

    let r = {
        topic: val.topic,
        clientMap: new Map().set(val.clientId, val.qos),
    };

    r[Symbol.iterator] = function* (topic)
    {
        if (topic === undefined)
        {
            for (let [clientId, qos] of r.clientMap)
            {
                yield { topic: r.topic, clientId, qos };
            }
        }
        else if (r.topic === topic)
        {
            for (let [clientId, qos] of r.clientMap)
            {
                yield { clientId, qos };
            }
        }
    };

    return r;
};

QlobberSub.prototype._add_value = function (existing, val)
{
    var clientMap = existing.clientMap,
        size = clientMap.size;

    clientMap.set(val.clientId, val.qos);

    if (clientMap.size > size)
    {
        this.subscriptionsCount += 1;
    }
};

QlobberSub.prototype._add_values = function (dest, existing, topic)
{
    var clientIdAndQos;
    if (topic === undefined)
    {
        for (clientIdAndQos of existing.clientMap)
        {
            dest.push(
            {
                clientId: clientIdAndQos[0],
                topic: existing.topic,
                qos: clientIdAndQos[1]
            });
        }
    }
    else if (existing.topic === topic)
    {
        for (clientIdAndQos of existing.clientMap)
        {
            dest.push(
            {
                clientId: clientIdAndQos[0],
                qos: clientIdAndQos[1]
            });
        }
    }
};

QlobberSub.prototype._remove_value = function (existing, val)
{
    var clientMap = existing.clientMap,
        size_before = clientMap.size;

    clientMap.delete(val.clientId);

    var size_after = clientMap.size;

    if (size_after < size_before)
    {
        this.subscriptionsCount -= 1;
    }

    return size_after === 0;
};

// Returns whether client is last subscriber to topic
QlobberSub.prototype.test_values = function (existing, val)
{
    var clientMap = existing.clientMap;

    return (existing.topic === val.topic) &&
           (clientMap.size === 1) &&
           clientMap.has(val.clientId);
};

QlobberSub.prototype.match = function (topic, ctx)
{
    return this._match([], 0, topic.split(this._separator), this._trie, ctx);
};

QlobberSub.prototype.clear = function ()
{
    this.subscriptionsCount = 0;
    return Qlobber.prototype.clear.call(this);
};

QlobberSub.set_native = function (qlobber_native)
{
    // wrap_native.js uses 'async *' which isn't available on Node 8
    try
    {
        const wrap_native = require('../lib/wrap_native.js');
        QlobberSub.native = wrap_native(qlobber_native.QlobberSub, QlobberSub);
        return module.exports;
    }
    catch (ex)
    {
    }
};

module.exports = QlobberSub;
<commit_msg>Fix qlobber-sub on Node 8<commit_after>/*jslint node: true */
"use strict";

var util = require('util'),
    qlobber = require('..'),
    Qlobber = qlobber.Qlobber;

function QlobberSub (options)
{
    Qlobber.call(this, options);
    this.subscriptionsCount = 0;
}

util.inherits(QlobberSub, Qlobber);

QlobberSub.prototype._initial_value = function (val)
{
    this.subscriptionsCount += 1;

    let r = {
        topic: val.topic,
        clientMap: new Map().set(val.clientId, val.qos),
    };

    r[Symbol.iterator] = function* (topic)
    {
        if (topic === undefined)
        {
            for (let [clientId, qos] of r.clientMap)
            {
                yield { topic: r.topic, clientId, qos };
            }
        }
        else if (r.topic === topic)
        {
            for (let [clientId, qos] of r.clientMap)
            {
                yield { clientId, qos };
            }
        }
    };

    return r;
};

QlobberSub.prototype._add_value = function (existing, val)
{
    var clientMap = existing.clientMap,
        size = clientMap.size;

    clientMap.set(val.clientId, val.qos);

    if (clientMap.size > size)
    {
        this.subscriptionsCount += 1;
    }
};

QlobberSub.prototype._add_values = function (dest, existing, topic)
{
    var clientIdAndQos;
    if (topic === undefined)
    {
        for (clientIdAndQos of existing.clientMap)
        {
            dest.push(
            {
                clientId: clientIdAndQos[0],
                topic: existing.topic,
                qos: clientIdAndQos[1]
            });
        }
    }
    else if (existing.topic === topic)
    {
        for (clientIdAndQos of existing.clientMap)
        {
            dest.push(
            {
                clientId: clientIdAndQos[0],
                qos: clientIdAndQos[1]
            });
        }
    }
};

QlobberSub.prototype._remove_value = function (existing, val)
{
    var clientMap = existing.clientMap,
        size_before = clientMap.size;

    clientMap.delete(val.clientId);

    var size_after = clientMap.size;

    if (size_after < size_before)
    {
        this.subscriptionsCount -= 1;
    }

    return size_after === 0;
};

// Returns whether client is last subscriber to topic
QlobberSub.prototype.test_values = function (existing, val)
{
    var clientMap = existing.clientMap;

    return (existing.topic === val.topic) &&
           (clientMap.size === 1) &&
           clientMap.has(val.clientId);
};

QlobberSub.prototype.match = function (topic, ctx)
{
    return this._match([], 0, topic.split(this._separator), this._trie, ctx);
};

QlobberSub.prototype.clear = function ()
{
    this.subscriptionsCount = 0;
    return Qlobber.prototype.clear.call(this);
};

QlobberSub.set_native = function (qlobber_native)
{
    // wrap_native.js uses 'async *' which isn't available on Node 8
    try
    {
        const wrap_native = require('../lib/wrap_native.js');
        QlobberSub.native = wrap_native(qlobber_native.QlobberSub, QlobberSub);
    }
    catch (ex)
    {
    }

    return module.exports;
};

module.exports = QlobberSub;
<|endoftext|>


<commit_before>import m from 'mithril';
import prop from 'mithril/stream';
import moment from 'moment';
import _ from 'underscore';
import {
    catarse,
    catarseMoments,
    commonAnalytics
} from '../api';
import models from '../models';
import h from '../h';
import projectDashboardMenu from '../c/project-dashboard-menu';
import projectDataChart from '../c/project-data-chart';
import projectInviteCard from '../c/project-invite-card';
import subscriptionsPerMonthTable from '../c/subscriptions-per-month-table';
import projectGoalsBoxDashboard from './project-goals-box-dashboard';
import insightsInfoBox from './insights-info-box';
import projectGoalsVM from '../vms/project-goals-vm';
import subscriptionVM from '../vms/subscription-vm';
import userVM from '../vms/user-vm';

const I18nScope = _.partial(h.i18nScope, 'projects.insights');

const projectInsightsSub = {
    oninit: function(vnode) {
        const filtersVM = vnode.attrs.filtersVM,
            visitorsTotal = prop(0),
            visitorLoader = catarseMoments.loaderWithToken,
            loader = commonAnalytics.loaderWithToken,
            visitorsPerDay = prop([]);
        const weekSubscriptions = prop([]);
        const subscriptionsPerDay = prop([]);
        const lastWeekSubscriptions = prop([]);
        const weekTransitions = prop([]);
        const lastWeekTransitions = prop([]);
        const subscriptionsPerMonth = prop([]);
        const isSubscriptionsPerMonthLoaded = prop(false);
        const balanceData = prop(null);
        const subVM = commonAnalytics.filtersVM({
            project_id: 'eq'
        });
        const processVisitors = (data) => {
            if (!_.isEmpty(data)) {
                visitorsPerDay(data);
                visitorsTotal(_.first(data).total);
            }
        };
        const requestRedraw = h.createRequestAutoRedraw(
            weekSubscriptions,
            subscriptionsPerDay,
            lastWeekSubscriptions,
            weekTransitions,
            lastWeekTransitions,
            subscriptionsPerMonth,
            isSubscriptionsPerMonthLoaded,
            balanceData
        );

        subVM.project_id(vnode.attrs.project.common_id);
        const lVisitorsPerDay = visitorLoader(models.projectVisitorsPerDay.getRowOptions(filtersVM.parameters()));
        lVisitorsPerDay
            .load()
            .then(processVisitors)
            .then(requestRedraw);

        const lSubscriptionsPerDay = loader(models.projectSubscriptionsPerDay.getRowOptions(subVM.parameters()));
        lSubscriptionsPerDay
            .load()
            .then(subscriptionsPerDay)
            .then(requestRedraw);

        subscriptionVM
            .getNewSubscriptions(vnode.attrs.project.common_id, moment().utc().subtract(1, 'weeks').format(), moment().utc().format())
            .then(weekSubscriptions)
            .then(requestRedraw);

        subscriptionVM
            .getNewSubscriptions(vnode.attrs.project.common_id, moment().utc().subtract(2, 'weeks').format(), moment().utc().subtract(1, 'weeks').format())
            .then(lastWeekSubscriptions)
            .then(requestRedraw);

        subscriptionVM
            .getSubscriptionTransitions(vnode.attrs.project.common_id, ['inactive', 'canceled'], 'active', moment().utc().subtract(1, 'weeks').format(), moment().utc().format())
            .then(weekTransitions)
            .then(requestRedraw);

        subscriptionVM
            .getSubscriptionTransitions(vnode.attrs.project.common_id, ['inactive', 'canceled'], 'active', moment().utc().subtract(2, 'weeks').format(), moment().utc().subtract(1, 'weeks').format())
            .then(lastWeekTransitions)
            .then(requestRedraw);

        subscriptionVM
            .getSubscriptionsPerMonth(vnode.attrs.project.common_id)
            .then((subscriptions) => {
                subscriptionsPerMonth(subscriptions);
                isSubscriptionsPerMonthLoaded(true);
                requestRedraw();
            });

        projectGoalsVM.fetchGoals(filtersVM.project_id());
        const balanceLoader = userVM.getUserBalance(vnode.attrs.project.user_id);
        balanceLoader.then(balanceData).then(requestRedraw);

        vnode.state = {
            weekSubscriptions,
            subscriptionsPerMonth,
            lastWeekSubscriptions,
            weekTransitions,
            lastWeekTransitions,
            projectGoalsVM,
            lVisitorsPerDay,
            lSubscriptionsPerDay,
            subscriptionsPerDay,
            visitorsTotal,
            visitorsPerDay,
            balanceLoader,
            balanceData,
            isSubscriptionsPerMonthLoaded
        };
    },
    view: function({state, attrs}) {
        const sumAmount = list => _.reduce(list, (memo, sub) => memo + (sub.amount / 100), 0);
        const weekSum = sumAmount(state.weekSubscriptions());
        const lastWeekSum = sumAmount(state.lastWeekSubscriptions());
        const canceledWeekSum = sumAmount(state.weekTransitions());
        const canceledLastWeekSum = sumAmount(state.lastWeekTransitions());
        const project = attrs.project,
            subscribersDetails = attrs.subscribersDetails,
            balanceData = (state.balanceData() && !_.isNull(_.first(state.balanceData())) ? _.first(state.balanceData()) : null);
        const averageRevenue = subscribersDetails.total_subscriptions > 0 ? (subscribersDetails.amount_paid_for_valid_period / subscribersDetails.total_subscriptions) : null;

        return m('.project-insights', !attrs.l() ? [
            m(`.w-section.section-product.${project.mode}`),
            (project.is_owner_or_admin ? m(projectDashboardMenu, {
                project: prop(project)
            }) : ''),
            m('.dashboard-header.section-one-column', [
                m('.u-marginbottom-30.u-text-center', [
                    m('.fontsize-larger.fontweight-semibold',
                        `Olá, ${project.user.public_name || project.user.name}!`
                    ),
                    m('.fontsize-smaller',
                        `Este é o retrato de sua campanha hoje, ${moment().format('DD [de] MMMM [de] YYYY')}`
                    )
                ]),
                m('.w-container', [
                    m('.flex-row.u-marginbottom-40.u-text-center-small-only', [
                        subscribersDetails && !_.isEmpty(state.projectGoalsVM.goals()) ?
                        m(projectGoalsBoxDashboard, {
                            goalDetails: state.projectGoalsVM.goals,
                            amount: subscribersDetails.amount_paid_for_valid_period
                        }) : '',
                        m('.card.card-terciary.flex-column.u-marginbottom-10.u-radius', [
                            m('.fontsize-small.u-marginbottom-10',
                                'Assinaturas ativas'
                            ),
                            m('.fontsize-largest.fontweight-semibold',
                                subscribersDetails.total_subscriptions
                            )
                        ]),
                        m('.card.card-terciary.flex-column.u-marginbottom-10.u-radius', [
                            m('.fontsize-small.u-marginbottom-10',
                                'Receita Mensal'
                            ),
                            m('.fontsize-largest.fontweight-semibold.u-marginbottom-10',
                                `R$${h.formatNumber(subscribersDetails.amount_paid_for_valid_period, 2, 3)}`
                            ),
                            m('.fontsize-mini.fontcolor-secondary.lineheight-tighter',
                                'Caso não haja variação no número de assinaturas e todos os pagamentos sejam confirmados no período, essa é a sua receita mensal, já com taxas descontadas.'
                            )
                        ]),
                        m('.card.flex-column.u-marginbottom-10.u-radius', [
                            m('.fontsize-small.u-marginbottom-10', [
                                'Saldo',
                                m.trust(' '),
                                ' ',
                                m(`a.btn-inline.btn-terciary.fontsize-smallest.u-radius[href='/users/${project.user_id}/edit#balance'][target='_self']`,
                                    'Sacar'
                                )
                            ]),
                            m('.fontsize-largest.fontweight-semibold.text-success.u-marginbottom-10',
                                (balanceData && balanceData.amount ? `R$${h.formatNumber(balanceData.amount, 2, 3)}` : '')
                            ),
                            m('.fontsize-mini.fontcolor-secondary.lineheight-tighter',
                                'O saldo demora até 20 mins após o pagamento para ser atualizado.'
                            )
                        ])
                    ]),
                    (project.state === 'online' && !project.has_cancelation_request ? m('.w-container.u-marginbottom-60', m(projectInviteCard, {
                        project
                    })) : ''),

                    m('.u-marginbottom-30', [
                        m('.flex-row.u-marginbottom-40.u-text-center-small-only', [
                            m('.flex-column.card.u-radius.u-marginbottom-10', [
                                m('div',
                                    'Receita média por assinatura'
                                ),
                                m('.fontsize-smallest.fontcolor-secondary.lineheight-tighter',
                                    `em ${moment().format('DD/MM/YYYY')}`
                                ),
                                m('.fontsize-largest.fontweight-semibold',
                                    `R$${averageRevenue ? `${h.formatNumber(averageRevenue, 2, 3)}` : '--'}`
                                )

                            ]),
                            m(insightsInfoBox, {
                                label: 'Novas Assinaturas',
                                info: state.weekSubscriptions().length,
                                newCount: state.weekSubscriptions().length,
                                oldCount: state.lastWeekSubscriptions().length
                            }),
                            m(insightsInfoBox, {
                                label: 'Novas assinaturas (R$)',
                                info: `R$${weekSum}`,
                                newCount: weekSum,
                                oldCount: lastWeekSum
                            })
                        ]),
                        m(".fontsize-large.fontweight-semibold.u-marginbottom-10.u-text-center[id='origem']", [
                            window.I18n.t('visitors_per_day_label', I18nScope())
                        ]),
                        m('.u-text-center.fontsize-smaller.fontcolor-secondary.lineheight-tighter.u-marginbottom-20', [
                            window.I18n.t('last_30_days_indication', I18nScope())
                        ])
                    ]), !state.lVisitorsPerDay() ? m(projectDataChart, {
                        collection: state.visitorsPerDay,
                        dataKey: 'visitors',
                        limitDataset: 30,
                        xAxis: item => h.momentify(item.day),
                        emptyState: window.I18n.t('visitors_per_day_empty', I18nScope())
                    }) : h.loader(),

                    m('.u-text-center', {
                        style: {
                            'min-height': '300px'
                        }
                    }, [!state.lSubscriptionsPerDay() ? m(projectDataChart, {
                        collection: state.subscriptionsPerDay,
                        label: window.I18n.t('amount_per_day_label_sub', I18nScope()),
                        subLabel: window.I18n.t('last_30_days_indication', I18nScope()),
                        dataKey: 'total_amount',
                        xAxis: item => h.momentify(item.paid_at),
                        emptyState: m.trust(window.I18n.t('amount_per_day_empty_sub', I18nScope()))
                    }) : h.loader()]),
                    m('.u-text-center', {
                        style: {
                            'min-height': '300px'
                        }
                    }, [!state.lSubscriptionsPerDay() ? m(projectDataChart, {
                        collection: state.subscriptionsPerDay,
                        label: window.I18n.t('contributions_per_day_label_sub', I18nScope()),
                        subLabel: window.I18n.t('last_30_days_indication', I18nScope()),
                        dataKey: 'total',
                        xAxis: item => h.momentify(item.paid_at),
                        emptyState: m.trust(window.I18n.t('contributions_per_day_empty_sub', I18nScope()))
                    }) : h.loader()]),
                    (state.isSubscriptionsPerMonthLoaded() ?
                        m(subscriptionsPerMonthTable, { data: state.subscriptionsPerMonth() }) : h.loader())

                ])
            ])
        ] : h.loader());
    }
};

export default projectInsightsSub;
<commit_msg>Minor fix<commit_after>import m from 'mithril';
import prop from 'mithril/stream';
import moment from 'moment';
import _ from 'underscore';
import {
    catarse,
    catarseMoments,
    commonAnalytics
} from '../api';
import models from '../models';
import h from '../h';
import projectDashboardMenu from '../c/project-dashboard-menu';
import projectDataChart from '../c/project-data-chart';
import projectInviteCard from '../c/project-invite-card';
import subscriptionsPerMonthTable from '../c/subscriptions-per-month-table';
import projectGoalsBoxDashboard from './project-goals-box-dashboard';
import insightsInfoBox from './insights-info-box';
import projectGoalsVM from '../vms/project-goals-vm';
import subscriptionVM from '../vms/subscription-vm';
import userVM from '../vms/user-vm';

const I18nScope = _.partial(h.i18nScope, 'projects.insights');

const projectInsightsSub = {
    oninit: function(vnode) {
        const filtersVM = vnode.attrs.filtersVM,
            visitorsTotal = prop(0),
            visitorLoader = catarseMoments.loaderWithToken,
            loader = commonAnalytics.loaderWithToken,
            visitorsPerDay = prop([]);
        const weekSubscriptions = prop([]);
        const subscriptionsPerDay = prop([]);
        const lastWeekSubscriptions = prop([]);
        const weekTransitions = prop([]);
        const lastWeekTransitions = prop([]);
        const subscriptionsPerMonth = prop([]);
        const isSubscriptionsPerMonthLoaded = prop(false);
        const balanceData = prop(null);
        const subVM = commonAnalytics.filtersVM({
            project_id: 'eq'
        });
        const processVisitors = (data) => {
            if (!_.isEmpty(data)) {
                visitorsPerDay(data);
                visitorsTotal(_.first(data).total);
            }
        };
        const requestRedraw = h.createRequestAutoRedraw(
            weekSubscriptions,
            subscriptionsPerDay,
            lastWeekSubscriptions,
            weekTransitions,
            lastWeekTransitions,
            subscriptionsPerMonth,
            isSubscriptionsPerMonthLoaded,
            balanceData
        );

        subVM.project_id(vnode.attrs.project.common_id);
        const lVisitorsPerDay = visitorLoader(models.projectVisitorsPerDay.getRowOptions(filtersVM.parameters()));
        lVisitorsPerDay
            .load()
            .then(processVisitors)
            .then(requestRedraw);

        const lSubscriptionsPerDay = loader(models.projectSubscriptionsPerDay.getRowOptions(subVM.parameters()));
        lSubscriptionsPerDay
            .load()
            .then(subscriptionsPerDay)
            .then(requestRedraw);

        subscriptionVM
            .getNewSubscriptions(vnode.attrs.project.common_id, moment().utc().subtract(1, 'weeks').format(), moment().utc().format())
            .then(weekSubscriptions)
            .then(requestRedraw);

        subscriptionVM
            .getNewSubscriptions(vnode.attrs.project.common_id, moment().utc().subtract(2, 'weeks').format(), moment().utc().subtract(1, 'weeks').format())
            .then(lastWeekSubscriptions)
            .then(requestRedraw);

        subscriptionVM
            .getSubscriptionTransitions(vnode.attrs.project.common_id, ['inactive', 'canceled'], 'active', moment().utc().subtract(1, 'weeks').format(), moment().utc().format())
            .then(weekTransitions)
            .then(requestRedraw);

        subscriptionVM
            .getSubscriptionTransitions(vnode.attrs.project.common_id, ['inactive', 'canceled'], 'active', moment().utc().subtract(2, 'weeks').format(), moment().utc().subtract(1, 'weeks').format())
            .then(lastWeekTransitions)
            .then(requestRedraw);

        subscriptionVM
            .getSubscriptionsPerMonth(vnode.attrs.project.common_id)
            .then((subscriptions) => {
                subscriptionsPerMonth(subscriptions);
                isSubscriptionsPerMonthLoaded(true);
                requestRedraw();
            });

        projectGoalsVM.fetchGoals(filtersVM.project_id());
        const balanceLoader = userVM.getUserBalance(vnode.attrs.project.user_id);
        balanceLoader.then(balanceData).then(requestRedraw);

        vnode.state = {
            weekSubscriptions,
            subscriptionsPerMonth,
            lastWeekSubscriptions,
            weekTransitions,
            lastWeekTransitions,
            projectGoalsVM,
            lVisitorsPerDay,
            lSubscriptionsPerDay,
            subscriptionsPerDay,
            visitorsTotal,
            visitorsPerDay,
            balanceLoader,
            balanceData,
            isSubscriptionsPerMonthLoaded
        };
    },
    view: function({state, attrs}) {
        const sumAmount = list => _.reduce(list, (memo, sub) => memo + (sub.amount / 100), 0);
        const weekSum = sumAmount(state.weekSubscriptions());
        const lastWeekSum = sumAmount(state.lastWeekSubscriptions());
        const canceledWeekSum = sumAmount(state.weekTransitions());
        const canceledLastWeekSum = sumAmount(state.lastWeekTransitions());
        const project = attrs.project,
            subscribersDetails = attrs.subscribersDetails,
            balanceData = (state.balanceData() && !_.isNull(_.first(state.balanceData())) ? _.first(state.balanceData()) : null);
        const averageRevenue = subscribersDetails.total_subscriptions > 0 ? (subscribersDetails.amount_paid_for_valid_period / subscribersDetails.total_subscriptions) : null;

        return m('.project-insights', !attrs.l() ? [
            m(`.w-section.section-product.${project.mode}`),
            (project.is_owner_or_admin ? m(projectDashboardMenu, {
                project: prop(project)
            }) : ''),
            m('.dashboard-header.section-one-column', [
                m('.u-marginbottom-30.u-text-center', [
                    m('.fontsize-larger.fontweight-semibold',
                        `Olá, ${project.user.public_name || project.user.name}!`
                    ),
                    m('.fontsize-smaller',
                        `Este é o retrato de sua campanha hoje, ${moment().format('DD [de] MMMM [de] YYYY')}`
                    )
                ]),
                m('.w-container', [
                    m('.flex-row.u-marginbottom-40.u-text-center-small-only', [
                        subscribersDetails && !_.isEmpty(state.projectGoalsVM.goals()) ?
                        m(projectGoalsBoxDashboard, {
                            goalDetails: state.projectGoalsVM.goals,
                            amount: subscribersDetails.amount_paid_for_valid_period
                        }) : '',
                        m('.card.card-terciary.flex-column.u-marginbottom-10.u-radius', [
                            m('.fontsize-small.u-marginbottom-10',
                                'Assinaturas ativas'
                            ),
                            m('.fontsize-largest.fontweight-semibold',
                                subscribersDetails.total_subscriptions
                            )
                        ]),
                        m('.card.card-terciary.flex-column.u-marginbottom-10.u-radius', [
                            m('.fontsize-small.u-marginbottom-10',
                                'Receita Mensal'
                            ),
                            m('.fontsize-largest.fontweight-semibold.u-marginbottom-10',
                                `R$${h.formatNumber(subscribersDetails.amount_paid_for_valid_period, 2, 3)}`
                            ),
                            m('.fontsize-mini.fontcolor-secondary.lineheight-tighter',
                                'Caso não haja variação no número de assinaturas e todos os pagamentos sejam confirmados no período, essa é a sua receita mensal, já com taxas descontadas.'
                            )
                        ]),
                        m('.card.flex-column.u-marginbottom-10.u-radius', [
                            m('.fontsize-small.u-marginbottom-10', [
                                'Saldo',
                                m.trust(' '),
                                ' ',
                                m(`a.btn-inline.btn-terciary.fontsize-smallest.u-radius[href='/users/${project.user_id}/edit#balance'][target='_self']`,
                                    'Sacar'
                                )
                            ]),
                            m('.fontsize-largest.fontweight-semibold.text-success.u-marginbottom-10',
                                (balanceData && balanceData.amount ? `R$${h.formatNumber(balanceData.amount, 2, 3)}` : '')
                            ),
                            m('.fontsize-mini.fontcolor-secondary.lineheight-tighter',
                                'O saldo demora até 20 mins após o pagamento para ser atualizado.'
                            )
                        ])
                    ]),
                    (project.state === 'online' && !project.has_cancelation_request ? m('.w-container.u-marginbottom-60', m(projectInviteCard, {
                        project
                    })) : ''),

                    m('.u-marginbottom-30', [
                        m('.flex-row.u-marginbottom-40.u-text-center-small-only', [
                            m('.flex-column.card.u-radius.u-marginbottom-10', [
                                m('div',
                                    'Receita média por assinatura'
                                ),
                                m('.fontsize-smallest.fontcolor-secondary.lineheight-tighter',
                                    `em ${moment().format('DD/MM/YYYY')}`
                                ),
                                m('.fontsize-largest.fontweight-semibold',
                                    `R$${averageRevenue ? `${h.formatNumber(averageRevenue, 2, 3)}` : '--'}`
                                )

                            ]),
                            m(insightsInfoBox, {
                                label: 'Novas Assinaturas',
                                info: state.weekSubscriptions().length,
                                newCount: state.weekSubscriptions().length,
                                oldCount: state.lastWeekSubscriptions().length
                            }),
                            m(insightsInfoBox, {
                                label: 'Novas assinaturas (R$)',
                                info: `R$${weekSum}`,
                                newCount: weekSum,
                                oldCount: lastWeekSum
                            })
                        ]),
                        m(".fontsize-large.fontweight-semibold.u-marginbottom-10.u-text-center[id='origem']", [
                            window.I18n.t('visitors_per_day_label', I18nScope())
                        ]),
                        m('.u-text-center.fontsize-smaller.fontcolor-secondary.lineheight-tighter.u-marginbottom-20', [
                            window.I18n.t('last_30_days_indication', I18nScope())
                        ])
                    ]), !state.lVisitorsPerDay() ? m(projectDataChart, {
                        collection: state.visitorsPerDay,
                        dataKey: 'visitors',
                        limitDataset: 30,
                        xAxis: item => h.momentify(item.day),
                        emptyState: window.I18n.t('visitors_per_day_empty', I18nScope())
                    }) : h.loader(),

                    m('.u-text-center', {
                        style: {
                            'min-height': '300px'
                        }
                    }, [!state.lSubscriptionsPerDay() ? m(projectDataChart, {
                        collection: state.subscriptionsPerDay,
                        label: window.I18n.t('amount_per_day_label_sub', I18nScope()),
                        subLabel: window.I18n.t('paid_date_indication', I18nScope()),
                        dataKey: 'total_amount',
                        xAxis: item => h.momentify(item.paid_at),
                        emptyState: m.trust(window.I18n.t('amount_per_day_empty_sub', I18nScope()))
                    }) : h.loader()]),
                    m('.u-text-center', {
                        style: {
                            'min-height': '300px'
                        }
                    }, [!state.lSubscriptionsPerDay() ? m(projectDataChart, {
                        collection: state.subscriptionsPerDay,
                        label: window.I18n.t('contributions_per_day_label_sub', I18nScope()),
                        subLabel: window.I18n.t('paid_date_indication', I18nScope()),
                        dataKey: 'total',
                        xAxis: item => h.momentify(item.paid_at),
                        emptyState: m.trust(window.I18n.t('contributions_per_day_empty_sub', I18nScope()))
                    }) : h.loader()]),
                    (state.isSubscriptionsPerMonthLoaded() ?
                        m(subscriptionsPerMonthTable, { data: state.subscriptionsPerMonth() }) : h.loader())

                ])
            ])
        ] : h.loader());
    }
};

export default projectInsightsSub;
<|endoftext|>