server/helpers/achievement.js

"use strict";

require("core-js/modules/es.object.get-own-property-descriptor.js");
require("core-js/modules/es.weak-map.js");
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getConsecutiveDaysWithEdits = getConsecutiveDaysWithEdits;
exports.getEntityVisits = getEntityVisits;
exports.getTypeCreation = getTypeCreation;
exports.processEdit = processEdit;
exports.processPageVisit = processPageVisit;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
require("core-js/modules/es.array.concat.js");
require("core-js/modules/es.array.iterator.js");
require("core-js/modules/es.array.join.js");
require("core-js/modules/es.array.map.js");
require("core-js/modules/es.date.to-json.js");
require("core-js/modules/es.function.name.js");
require("core-js/modules/es.object.keys.js");
require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.promise.js");
require("core-js/modules/es.string.iterator.js");
require("core-js/modules/web.dom-collections.for-each.js");
require("core-js/modules/web.dom-collections.iterator.js");
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _isNil2 = _interopRequireDefault(require("lodash/isNil"));
var _flattenDeep2 = _interopRequireDefault(require("lodash/flattenDeep"));
var error = _interopRequireWildcard(require("../../common/helpers/error"));
var _dateFns = require("date-fns");
var _log = _interopRequireDefault(require("log"));
var cache;
function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var r, c, a = Object.defineProperty, n = { __proto__: null, default: e }; if (Object(e) !== e) return n; if (cache || "function" != typeof WeakMap || (cache = new WeakMap()), cache) { if (cache.has(e)) return cache.get(e); cache.set(e, n); } for (c in e) "default" !== c && {}.hasOwnProperty.call(e, c) && ((r = a && Object.getOwnPropertyDescriptor(e, c)) && (r.get || r.set) ? a(n, c, r) : n[c] = e[c]); return n; }
/*
 * Copyright (C) 2016  Max Prettyjohns
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
/**
 * Achievement Module
 * @module Achievement
 */
/**
 * Awards an Unlock type with awardAttribs if not already awarded
 * @param {function} UnlockType - Either TitleUnlock or AchievementUnlock
 * @param {object} awardAttribs - Values that are supplied to Unlock constructor
 * @returns {object} - 'already unlocked' or JSON of new unlock
 */
function awardUnlock(_x, _x2) {
  return _awardUnlock.apply(this, arguments);
}
/**
 * Awards an Achievement
 * @param {object} orm - the BookBrainz ORM, initialized during app setup
 * @param {int} editorId - The editor the achievement will be awarded to
 * @param {string} achievementName - Name of achievement in database
 * @returns {object} - {achievementName: unlock} where unlock is JSON returned
 * from awardUnlock
 * @memberof module:Achievement
 */
function _awardUnlock() {
  _awardUnlock = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee(UnlockType, awardAttribs) {
    var award, unlock;
    return _regenerator.default.wrap(function (_context) {
      while (1) switch (_context.prev = _context.next) {
        case 0:
          _context.next = 1;
          return new UnlockType(awardAttribs).fetch({
            require: false
          });
        case 1:
          award = _context.sent;
          if (!(award === null || typeof award === 'undefined')) {
            _context.next = 3;
            break;
          }
          _context.next = 2;
          return new UnlockType(awardAttribs).save(null, {
            method: 'insert'
          });
        case 2:
          unlock = _context.sent;
          return _context.abrupt("return", unlock.toJSON());
        case 3:
          return _context.abrupt("return", 'Already unlocked');
        case 4:
        case "end":
          return _context.stop();
      }
    }, _callee);
  }));
  return _awardUnlock.apply(this, arguments);
}
function awardAchievement(_x3, _x4, _x5) {
  return _awardAchievement.apply(this, arguments);
}
/**
 * Awards a Title
 * @param {object} orm - the BookBrainz ORM, initialized during app setup
 * @param {int} editorId - The editor the title will be assigned to
 * @param {object} tier - Achievement Tier the Title (if it exists) belongs to
 * @returns {object} - {tier.titleName: unlock} where unlock comes from
 * awardUnlock or false if the title is not in the tier
 * @memberof module:Achievement
 */
function _awardAchievement() {
  _awardAchievement = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee2(orm, editorId, achievementName) {
    var AchievementType, AchievementUnlock, achievementTier, achievementAttribs, unlock, _t;
    return _regenerator.default.wrap(function (_context2) {
      while (1) switch (_context2.prev = _context2.next) {
        case 0:
          AchievementType = orm.AchievementType, AchievementUnlock = orm.AchievementUnlock;
          _context2.next = 1;
          return new AchievementType({
            name: achievementName
          }).fetch({
            require: false
          });
        case 1:
          achievementTier = _context2.sent;
          if (!(achievementTier === null)) {
            _context2.next = 2;
            break;
          }
          throw new error.AwardNotUnlockedError("Achievement ".concat(achievementName, " not found in database"));
        case 2:
          _context2.prev = 2;
          achievementAttribs = {
            achievementId: achievementTier.id,
            editorId: editorId
          };
          _context2.next = 3;
          return awardUnlock(AchievementUnlock, achievementAttribs);
        case 3:
          unlock = _context2.sent;
          return _context2.abrupt("return", (0, _defineProperty2.default)({}, achievementName, unlock));
        case 4:
          _context2.prev = 4;
          _t = _context2["catch"](2);
          throw new error.AwardNotUnlockedError(_t.message);
        case 5:
        case "end":
          return _context2.stop();
      }
    }, _callee2, null, [[2, 4]]);
  }));
  return _awardAchievement.apply(this, arguments);
}
function awardTitle(_x6, _x7, _x8) {
  return _awardTitle.apply(this, arguments);
}
/**
 * In testTiers a tier is mapped to a list of achievements/titles this
 * converts it to an object keyed by achievementName where it is easier
 * to find a specific achievement.
 * @example
 * awardListToAwardObject([[{'Achievement I': unlockI}]])
 * //returns {'Achievement I': unlockI}
 * @param {object} awardList - List of List of achievement unlocks
 * @returns {object} - Object keyed by achievement name with values
 * unlock json
 */
function _awardTitle() {
  _awardTitle = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee3(orm, editorId, tier) {
    var TitleType, TitleUnlock, title, titleAttribs, unlock, _t2;
    return _regenerator.default.wrap(function (_context3) {
      while (1) switch (_context3.prev = _context3.next) {
        case 0:
          TitleType = orm.TitleType, TitleUnlock = orm.TitleUnlock;
          if (!tier.titleName) {
            _context3.next = 6;
            break;
          }
          _context3.next = 1;
          return new TitleType({
            title: tier.titleName
          }).fetch({
            require: false
          });
        case 1:
          title = _context3.sent;
          if (!(title === null)) {
            _context3.next = 2;
            break;
          }
          throw new error.AwardNotUnlockedError("Title ".concat(tier.titleName, " not found in database"));
        case 2:
          _context3.prev = 2;
          titleAttribs = {
            editorId: editorId,
            titleId: title.id
          };
          _context3.next = 3;
          return awardUnlock(TitleUnlock, titleAttribs);
        case 3:
          unlock = _context3.sent;
          return _context3.abrupt("return", (0, _defineProperty2.default)({}, tier.titleName, unlock));
        case 4:
          _context3.prev = 4;
          _t2 = _context3["catch"](2);
          throw new error.AwardNotUnlockedError(_t2.message);
        case 5:
          _context3.next = 7;
          break;
        case 6:
          return _context3.abrupt("return", false);
        case 7:
        case "end":
          return _context3.stop();
      }
    }, _callee3, null, [[2, 4]]);
  }));
  return _awardTitle.apply(this, arguments);
}
function awardListToAwardObject(awardList) {
  var track = {};
  if (awardList) {
    awardList.forEach(function (awardSet) {
      if (!(0, _isNil2.default)(awardSet)) {
        awardSet.forEach(function (award) {
          if (!(0, _isNil2.default)(award)) {
            Object.keys(award).forEach(function (key) {
              track[key] = award[key];
            });
          }
        });
      }
    });
  }
  return track;
}

/**
 * Takes a list of achievement 'tiers' and awards the related achievement and
 * title if the signal is greater than or equal to the threshold
 * @param {object} orm - the BookBrainz ORM, initialized during app setup
 * @param {int} signal - Value tier threshold will be compared against
 * @param {int} editorId - Editor to award achievements/titles to
 * @param {object} tiers - Object with threshold and relatedachievement/title
 * names
 * @example
 * testTiers(10, 1, [{
 * 	threshold: 10, name: 'achievement I', titleName: 'achievement'
 * }])
 * //returns {'achievement I': achievementJSON}
 * @returns {object} - Returns a track of achievements keyed by achievement
 * name/title containing their respective unlockJSON each tier
 */
function testTiers(_x9, _x0, _x1, _x10) {
  return _testTiers.apply(this, arguments);
}
/**
 * Returns number of revisions of a certain type created by a specified
 * editor
 * @param {function} revisionType - Constructor for the revisionType
 * @param {string} revisionString - Snake case string of revisionType
 * @param {int} editor - Editor id being queried
 * @returns {int} - Number of revisions of type (type)
 */
function _testTiers() {
  _testTiers = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee5(orm, signal, editorId, tiers) {
    var tierPromise, awardList;
    return _regenerator.default.wrap(function (_context5) {
      while (1) switch (_context5.prev = _context5.next) {
        case 0:
          tierPromise = tiers.map(/*#__PURE__*/function () {
            var _ref3 = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee4(tier) {
              var achievementUnlock, title, out, _out, _t3;
              return _regenerator.default.wrap(function (_context4) {
                while (1) switch (_context4.prev = _context4.next) {
                  case 0:
                    if (!(signal >= tier.threshold)) {
                      _context4.next = 5;
                      break;
                    }
                    _context4.prev = 1;
                    _context4.next = 2;
                    return awardAchievement(orm, editorId, tier.name);
                  case 2:
                    achievementUnlock = _context4.sent;
                    _context4.next = 3;
                    return awardTitle(orm, editorId, tier);
                  case 3:
                    title = _context4.sent;
                    out = [];
                    if (title) {
                      out.push(title);
                    }
                    out.push(achievementUnlock);
                    return _context4.abrupt("return", out);
                  case 4:
                    _context4.prev = 4;
                    _t3 = _context4["catch"](1);
                    return _context4.abrupt("return", _log.default.debug(_t3));
                  case 5:
                    _out = {};
                    _out[tier.name] = false;
                    if (tier.titleName) {
                      _out[tier.titleName] = false;
                    }
                    return _context4.abrupt("return", [_out]);
                  case 6:
                  case "end":
                    return _context4.stop();
                }
              }, _callee4, null, [[1, 4]]);
            }));
            return function (_x54) {
              return _ref3.apply(this, arguments);
            };
          }());
          _context5.next = 1;
          return Promise.all(tierPromise);
        case 1:
          awardList = _context5.sent;
          return _context5.abrupt("return", awardListToAwardObject(awardList));
        case 2:
        case "end":
          return _context5.stop();
      }
    }, _callee5);
  }));
  return _testTiers.apply(this, arguments);
}
function getTypeCreation(_x11, _x12, _x13) {
  return _getTypeCreation.apply(this, arguments);
}
function _getTypeCreation() {
  _getTypeCreation = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee6(revisionType, revisionString, editor) {
    var out;
    return _regenerator.default.wrap(function (_context6) {
      while (1) switch (_context6.prev = _context6.next) {
        case 0:
          _context6.next = 1;
          return revisionType.query(function (qb) {
            qb.innerJoin('bookbrainz.revision', 'bookbrainz.revision.id', "bookbrainz.".concat(revisionString, ".id"));
            qb.groupBy("".concat(revisionString, ".id"), "".concat(revisionString, ".bbid"), 'revision.id');
            qb.where('bookbrainz.revision.author_id', '=', editor);
            qb.leftOuterJoin('bookbrainz.revision_parent', 'bookbrainz.revision_parent.child_id', "bookbrainz.".concat(revisionString, ".id"));
            qb.whereNull('bookbrainz.revision_parent.parent_id');
          }).fetchAll({
            require: false
          });
        case 1:
          out = _context6.sent;
          return _context6.abrupt("return", out.length);
        case 2:
        case "end":
          return _context6.stop();
      }
    }, _callee6);
  }));
  return _getTypeCreation.apply(this, arguments);
}
function processRevisionist(_x14, _x15) {
  return _processRevisionist.apply(this, arguments);
}
function _processRevisionist() {
  _processRevisionist = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee7(orm, editorId) {
    var Editor, editor, revisions, tiers;
    return _regenerator.default.wrap(function (_context7) {
      while (1) switch (_context7.prev = _context7.next) {
        case 0:
          Editor = orm.Editor;
          _context7.next = 1;
          return new Editor({
            id: editorId
          }).fetch({
            require: false
          });
        case 1:
          editor = _context7.sent;
          revisions = editor.get('revisionsApplied');
          tiers = [{
            name: 'Revisionist III',
            threshold: 250,
            titleName: 'Revisionist'
          }, {
            name: 'Revisionist II',
            threshold: 50
          }, {
            name: 'Revisionist I',
            threshold: 1
          }];
          return _context7.abrupt("return", testTiers(orm, revisions, editorId, tiers));
        case 2:
        case "end":
          return _context7.stop();
      }
    }, _callee7);
  }));
  return _processRevisionist.apply(this, arguments);
}
function processAuthorCreator(_x16, _x17) {
  return _processAuthorCreator.apply(this, arguments);
}
function _processAuthorCreator() {
  _processAuthorCreator = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee8(orm, editorId) {
    var AuthorRevision, rowCount, tiers;
    return _regenerator.default.wrap(function (_context8) {
      while (1) switch (_context8.prev = _context8.next) {
        case 0:
          AuthorRevision = orm.AuthorRevision;
          _context8.next = 1;
          return getTypeCreation(new AuthorRevision(), 'author_revision', editorId);
        case 1:
          rowCount = _context8.sent;
          tiers = [{
            name: 'Author Creator III',
            threshold: 100,
            titleName: 'Author Creator'
          }, {
            name: 'Author Creator II',
            threshold: 10
          }, {
            name: 'Author Creator I',
            threshold: 1
          }];
          return _context8.abrupt("return", testTiers(orm, rowCount, editorId, tiers));
        case 2:
        case "end":
          return _context8.stop();
      }
    }, _callee8);
  }));
  return _processAuthorCreator.apply(this, arguments);
}
function processLimitedEdition(_x18, _x19) {
  return _processLimitedEdition.apply(this, arguments);
}
function _processLimitedEdition() {
  _processLimitedEdition = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee9(orm, editorId) {
    var EditionRevision, rowCount, tiers;
    return _regenerator.default.wrap(function (_context9) {
      while (1) switch (_context9.prev = _context9.next) {
        case 0:
          EditionRevision = orm.EditionRevision;
          _context9.next = 1;
          return getTypeCreation(new EditionRevision(), 'edition_revision', editorId);
        case 1:
          rowCount = _context9.sent;
          tiers = [{
            name: 'Limited Edition III',
            threshold: 100,
            titleName: 'Limited Edition'
          }, {
            name: 'Limited Edition II',
            threshold: 10
          }, {
            name: 'Limited Edition I',
            threshold: 1
          }];
          return _context9.abrupt("return", testTiers(orm, rowCount, editorId, tiers));
        case 2:
        case "end":
          return _context9.stop();
      }
    }, _callee9);
  }));
  return _processLimitedEdition.apply(this, arguments);
}
function processPublisher(_x20, _x21) {
  return _processPublisher.apply(this, arguments);
}
function _processPublisher() {
  _processPublisher = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee0(orm, editorId) {
    var EditionGroupRevision, rowCount, tiers;
    return _regenerator.default.wrap(function (_context0) {
      while (1) switch (_context0.prev = _context0.next) {
        case 0:
          EditionGroupRevision = orm.EditionGroupRevision;
          _context0.next = 1;
          return getTypeCreation(new EditionGroupRevision(), 'edition_group_revision', editorId);
        case 1:
          rowCount = _context0.sent;
          tiers = [{
            name: 'Publisher III',
            threshold: 100,
            titleName: 'Publisher'
          }, {
            name: 'Publisher II',
            threshold: 10
          }, {
            name: 'Publisher I',
            threshold: 1
          }];
          return _context0.abrupt("return", testTiers(orm, rowCount, editorId, tiers));
        case 2:
        case "end":
          return _context0.stop();
      }
    }, _callee0);
  }));
  return _processPublisher.apply(this, arguments);
}
function processPublisherCreator(_x22, _x23) {
  return _processPublisherCreator.apply(this, arguments);
}
function _processPublisherCreator() {
  _processPublisherCreator = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee1(orm, editorId) {
    var PublisherRevision, rowCount, tiers;
    return _regenerator.default.wrap(function (_context1) {
      while (1) switch (_context1.prev = _context1.next) {
        case 0:
          PublisherRevision = orm.PublisherRevision;
          _context1.next = 1;
          return getTypeCreation(new PublisherRevision(), 'publisher_revision', editorId);
        case 1:
          rowCount = _context1.sent;
          tiers = [{
            name: 'Publisher Creator III',
            threshold: 100,
            titleName: 'Publisher Creator'
          }, {
            name: 'Publisher Creator II',
            threshold: 10
          }, {
            name: 'Publisher Creator I',
            threshold: 1
          }];
          return _context1.abrupt("return", testTiers(orm, rowCount, editorId, tiers));
        case 2:
        case "end":
          return _context1.stop();
      }
    }, _callee1);
  }));
  return _processPublisherCreator.apply(this, arguments);
}
function processWorkerBee(_x24, _x25) {
  return _processWorkerBee.apply(this, arguments);
}
function _processWorkerBee() {
  _processWorkerBee = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee10(orm, editorId) {
    var WorkRevision, rowCount, tiers;
    return _regenerator.default.wrap(function (_context10) {
      while (1) switch (_context10.prev = _context10.next) {
        case 0:
          WorkRevision = orm.WorkRevision;
          _context10.next = 1;
          return getTypeCreation(new WorkRevision(), 'work_revision', editorId);
        case 1:
          rowCount = _context10.sent;
          tiers = [{
            name: 'Worker Bee III',
            threshold: 100,
            titleName: 'Worker Bee'
          }, {
            name: 'Worker Bee II',
            threshold: 10
          }, {
            name: 'Worker Bee I',
            threshold: 1
          }];
          return _context10.abrupt("return", testTiers(orm, rowCount, editorId, tiers));
        case 2:
        case "end":
          return _context10.stop();
      }
    }, _callee10);
  }));
  return _processWorkerBee.apply(this, arguments);
}
function processSeriesCreator(_x26, _x27) {
  return _processSeriesCreator.apply(this, arguments);
}
function _processSeriesCreator() {
  _processSeriesCreator = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee11(orm, editorId) {
    var SeriesRevision, rowCount, tiers;
    return _regenerator.default.wrap(function (_context11) {
      while (1) switch (_context11.prev = _context11.next) {
        case 0:
          SeriesRevision = orm.SeriesRevision;
          _context11.next = 1;
          return getTypeCreation(new SeriesRevision(), 'series_revision', editorId);
        case 1:
          rowCount = _context11.sent;
          tiers = [{
            name: 'Series Creator III',
            threshold: 100,
            titleName: 'Series Creator'
          }, {
            name: 'Series Creator II',
            threshold: 10
          }, {
            name: 'Series Creator I',
            threshold: 1
          }];
          return _context11.abrupt("return", testTiers(orm, rowCount, editorId, tiers));
        case 2:
        case "end":
          return _context11.stop();
      }
    }, _callee11);
  }));
  return _processSeriesCreator.apply(this, arguments);
}
function processSprinter(_x28, _x29) {
  return _processSprinter.apply(this, arguments);
}
/**
 * Gets number of consecutive days from today the editor created revisions upto a specified
 * time limit
 * @param {object} orm - the BookBrainz ORM, initialized during app setup
 * @param {int} editorId - Editor to query on
 * @param {int} days - Number of days before today to collect edits from
 * @returns {int} - Number of consecutive days edits were performed on
 */
function _processSprinter() {
  _processSprinter = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee12(orm, editorId) {
    var bookshelf, rawSql, out, tiers;
    return _regenerator.default.wrap(function (_context12) {
      while (1) switch (_context12.prev = _context12.next) {
        case 0:
          bookshelf = orm.bookshelf;
          rawSql = "SELECT * from bookbrainz.revision WHERE author_id=".concat(editorId, " \t\tand created_at > (SELECT CURRENT_DATE - INTERVAL '1 hour');");
          _context12.next = 1;
          return bookshelf.knex.raw(rawSql);
        case 1:
          out = _context12.sent;
          tiers = [{
            name: 'Sprinter',
            threshold: 10,
            titleName: 'Sprinter'
          }];
          return _context12.abrupt("return", testTiers(orm, out.rowCount, editorId, tiers));
        case 2:
        case "end":
          return _context12.stop();
      }
    }, _callee12);
  }));
  return _processSprinter.apply(this, arguments);
}
function getConsecutiveDaysWithEdits(_x30, _x31, _x32) {
  return _getConsecutiveDaysWithEdits.apply(this, arguments);
}
function _getConsecutiveDaysWithEdits() {
  _getConsecutiveDaysWithEdits = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee13(orm, editorId, days) {
    var bookshelf, rawSql, out, dateList, countDays, lastDate, i;
    return _regenerator.default.wrap(function (_context13) {
      while (1) switch (_context13.prev = _context13.next) {
        case 0:
          bookshelf = orm.bookshelf;
          rawSql = "SELECT DISTINCT created_at::date from bookbrainz.revision \t\tWHERE author_id=".concat(editorId, " \t\tand created_at > (SELECT CURRENT_DATE - INTERVAL '").concat(days, " days');");
          _context13.next = 1;
          return bookshelf.knex.raw(rawSql);
        case 1:
          out = _context13.sent;
          dateList = out.rows.map(function (row) {
            return (0, _dateFns.parseISO)(row.created_at);
          });
          countDays = 0;
          lastDate = new Date(); // count number of consecutive days starting from today
          i = dateList.length - 1;
        case 2:
          if (!(i >= 0)) {
            _context13.next = 5;
            break;
          }
          if (!((0, _dateFns.differenceInCalendarDays)(lastDate, dateList[i]) > 1)) {
            _context13.next = 3;
            break;
          }
          return _context13.abrupt("continue", 5);
        case 3:
          countDays++;
          lastDate = dateList[i];
        case 4:
          i--;
          _context13.next = 2;
          break;
        case 5:
          return _context13.abrupt("return", countDays);
        case 6:
        case "end":
          return _context13.stop();
      }
    }, _callee13);
  }));
  return _getConsecutiveDaysWithEdits.apply(this, arguments);
}
function processFunRunner(_x33, _x34) {
  return _processFunRunner.apply(this, arguments);
}
function _processFunRunner() {
  _processFunRunner = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee14(orm, editorId) {
    var rowCount, tiers;
    return _regenerator.default.wrap(function (_context14) {
      while (1) switch (_context14.prev = _context14.next) {
        case 0:
          _context14.next = 1;
          return getConsecutiveDaysWithEdits(orm, editorId, 6);
        case 1:
          rowCount = _context14.sent;
          tiers = [{
            name: 'Fun Runner',
            threshold: 7,
            titleName: 'Fun Runner'
          }];
          return _context14.abrupt("return", testTiers(orm, rowCount, editorId, tiers));
        case 2:
        case "end":
          return _context14.stop();
      }
    }, _callee14);
  }));
  return _processFunRunner.apply(this, arguments);
}
function processMarathoner(_x35, _x36) {
  return _processMarathoner.apply(this, arguments);
}
/**
 * Converts achievementTier object to a list of achievementUnlock id's,
 * this will be used to notify the user which achievement they unlocked
 * @param {object}  achievementUnlock - A track of achievements containing
 * unlock JSON for each
 * @returns {list} - A list of achievementUnlock id's
 */
function _processMarathoner() {
  _processMarathoner = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee15(orm, editorId) {
    var rowCount, tiers;
    return _regenerator.default.wrap(function (_context15) {
      while (1) switch (_context15.prev = _context15.next) {
        case 0:
          _context15.next = 1;
          return getConsecutiveDaysWithEdits(orm, editorId, 29);
        case 1:
          rowCount = _context15.sent;
          tiers = [{
            name: 'Marathoner',
            threshold: 30,
            titleName: 'Marathoner'
          }];
          return _context15.abrupt("return", testTiers(orm, rowCount, editorId, tiers));
        case 2:
        case "end":
          return _context15.stop();
      }
    }, _callee15);
  }));
  return _processMarathoner.apply(this, arguments);
}
function achievementToUnlockId(achievementUnlock) {
  var unlockIds = [];
  Object.keys(achievementUnlock).forEach(function (key) {
    if (achievementUnlock[key].id) {
      unlockIds.push(String(achievementUnlock[key].id));
    }
  });
  return unlockIds;
}

/**
 * Gets days since edition release date, positive implies released in future
 * @param {object} orm - the BookBrainz ORM, initialized during app setup
 * @param {int} revisionId - Revision to get release date of
 * @returns {int} - Days since edition was released
 */
function getEditionDateDifference(_x37, _x38) {
  return _getEditionDateDifference.apply(this, arguments);
}
function _getEditionDateDifference() {
  _getEditionDateDifference = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee16(orm, revisionId) {
    var EditionRevision, edition, editionJSON, releaseEvents, msInOneDay, attribs, date, now, _t4;
    return _regenerator.default.wrap(function (_context16) {
      while (1) switch (_context16.prev = _context16.next) {
        case 0:
          EditionRevision = orm.EditionRevision;
          _context16.prev = 1;
          _context16.next = 2;
          return new EditionRevision({
            id: revisionId
          }).fetch({
            withRelated: 'data.releaseEventSet.releaseEvents'
          });
        case 2:
          edition = _context16.sent;
          editionJSON = edition.toJSON();
          releaseEvents = editionJSON.data.releaseEventSet.releaseEvents;
          if (!(releaseEvents.length > 0)) {
            _context16.next = 3;
            break;
          }
          msInOneDay = 86400000;
          attribs = releaseEvents[0];
          date = new Date(attribs.year, attribs.month - 1, attribs.day);
          now = Date.now();
          return _context16.abrupt("return", Math.round((date.getTime() - now) / msInOneDay));
        case 3:
          throw new Error('no date attribute');
        case 4:
          _context16.prev = 4;
          _t4 = _context16["catch"](1);
          throw new Error('no date attribute');
        case 5:
        case "end":
          return _context16.stop();
      }
    }, _callee16, null, [[1, 4]]);
  }));
  return _getEditionDateDifference.apply(this, arguments);
}
function processTimeTraveller(_x39, _x40, _x41) {
  return _processTimeTraveller.apply(this, arguments);
}
function _processTimeTraveller() {
  _processTimeTraveller = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee17(orm, editorId, revisionId) {
    var diff, tiers, _t5;
    return _regenerator.default.wrap(function (_context17) {
      while (1) switch (_context17.prev = _context17.next) {
        case 0:
          _context17.prev = 0;
          _context17.next = 1;
          return getEditionDateDifference(orm, revisionId);
        case 1:
          diff = _context17.sent;
          tiers = [{
            name: 'Time Traveller',
            threshold: 0,
            titleName: 'Time Traveller'
          }];
          return _context17.abrupt("return", testTiers(orm, diff, editorId, tiers));
        case 2:
          _context17.prev = 2;
          _t5 = _context17["catch"](0);
          return _context17.abrupt("return", {
            'Time Traveller': _t5
          });
        case 3:
        case "end":
          return _context17.stop();
      }
    }, _callee17, null, [[0, 2]]);
  }));
  return _processTimeTraveller.apply(this, arguments);
}
function processHotOffThePress(_x42, _x43, _x44) {
  return _processHotOffThePress.apply(this, arguments);
}
/**
 * Returns number of distinct entities viewed by an editor
 * @param {object} orm - the BookBrainz ORM, initialized during app setup
 * @param {int} editorId - Editor to get views for
 * @returns {int} - Number of views user has
 */
function _processHotOffThePress() {
  _processHotOffThePress = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee18(orm, editorId, revisionId) {
    var diff, tiers, _t6;
    return _regenerator.default.wrap(function (_context18) {
      while (1) switch (_context18.prev = _context18.next) {
        case 0:
          _context18.prev = 0;
          _context18.next = 1;
          return getEditionDateDifference(orm, revisionId);
        case 1:
          diff = _context18.sent;
          if (!(diff < 0)) {
            _context18.next = 2;
            break;
          }
          tiers = [{
            name: 'Hot Off the Press',
            threshold: -7,
            titleName: 'Hot Off the Press'
          }];
          return _context18.abrupt("return", testTiers(orm, diff, editorId, tiers));
        case 2:
          return _context18.abrupt("return", {
            'Hot Off the Press': false
          });
        case 3:
          _context18.prev = 3;
          _t6 = _context18["catch"](0);
          return _context18.abrupt("return", {
            'Hot Off the Press': _t6
          });
        case 4:
        case "end":
          return _context18.stop();
      }
    }, _callee18, null, [[0, 3]]);
  }));
  return _processHotOffThePress.apply(this, arguments);
}
function getEntityVisits(_x45, _x46) {
  return _getEntityVisits.apply(this, arguments);
}
function _getEntityVisits() {
  _getEntityVisits = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee19(orm, editorId) {
    var EditorEntityVisits, visits, _t7;
    return _regenerator.default.wrap(function (_context19) {
      while (1) switch (_context19.prev = _context19.next) {
        case 0:
          EditorEntityVisits = orm.EditorEntityVisits;
          _context19.prev = 1;
          _context19.next = 2;
          return new EditorEntityVisits().where('editor_id', editorId).fetchAll({
            require: true
          });
        case 2:
          visits = _context19.sent;
          return _context19.abrupt("return", visits.length);
        case 3:
          _context19.prev = 3;
          _t7 = _context19["catch"](1);
          return _context19.abrupt("return", 0);
        case 4:
        case "end":
          return _context19.stop();
      }
    }, _callee19, null, [[1, 3]]);
  }));
  return _getEntityVisits.apply(this, arguments);
}
function processExplorer(_x47, _x48) {
  return _processExplorer.apply(this, arguments);
}
function _processExplorer() {
  _processExplorer = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee20(orm, editorId) {
    var visits, tiers, _t8;
    return _regenerator.default.wrap(function (_context20) {
      while (1) switch (_context20.prev = _context20.next) {
        case 0:
          _context20.prev = 0;
          _context20.next = 1;
          return getEntityVisits(orm, editorId);
        case 1:
          visits = _context20.sent;
          tiers = [{
            name: 'Explorer I',
            threshold: 10
          }, {
            name: 'Explorer II',
            threshold: 100
          }, {
            name: 'Explorer III',
            threshold: 1000,
            titleName: 'Explorer'
          }];
          return _context20.abrupt("return", testTiers(orm, visits, editorId, tiers));
        case 2:
          _context20.prev = 2;
          _t8 = _context20["catch"](0);
          return _context20.abrupt("return", {
            Explorer: _t8
          });
        case 3:
        case "end":
          return _context20.stop();
      }
    }, _callee20, null, [[0, 2]]);
  }));
  return _processExplorer.apply(this, arguments);
}
function processPageVisit(_x49, _x50) {
  return _processPageVisit.apply(this, arguments);
}
/**
 * Run each time an edit occurs on the site, will test for each achievement
 * type
 * @param {object} orm - the BookBrainz ORM, initialized during app setup
 * @param {int} userId - Id of the user to query
 * @param {int} revisionId - Id of latest revision
 * @returns {object} - Output of each achievement test as well as an alert
 * containing id's for each unlocked achievement in .alert
 */
function _processPageVisit() {
  _processPageVisit = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee21(orm, userId) {
    var explorer, unlockIds, alert;
    return _regenerator.default.wrap(function (_context21) {
      while (1) switch (_context21.prev = _context21.next) {
        case 0:
          _context21.next = 1;
          return processExplorer(orm, userId);
        case 1:
          explorer = _context21.sent;
          unlockIds = achievementToUnlockId(explorer);
          alert = unlockIds.join(',');
          return _context21.abrupt("return", {
            alert: alert,
            explorer: explorer
          });
        case 2:
        case "end":
          return _context21.stop();
      }
    }, _callee21);
  }));
  return _processPageVisit.apply(this, arguments);
}
function processEdit(_x51, _x52, _x53) {
  return _processEdit.apply(this, arguments);
}
function _processEdit() {
  _processEdit = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee22(orm, userId, revisionId) {
    var revisionist, authorCreator, limitedEdition, publisher, publisherCreator, workerBee, seriesCreator, sprinter, funRunner, marathoner, timeTraveller, hotOffThePress, alert;
    return _regenerator.default.wrap(function (_context22) {
      while (1) switch (_context22.prev = _context22.next) {
        case 0:
          _context22.next = 1;
          return processRevisionist(orm, userId);
        case 1:
          revisionist = _context22.sent;
          _context22.next = 2;
          return processAuthorCreator(orm, userId);
        case 2:
          authorCreator = _context22.sent;
          _context22.next = 3;
          return processLimitedEdition(orm, userId);
        case 3:
          limitedEdition = _context22.sent;
          _context22.next = 4;
          return processPublisher(orm, userId);
        case 4:
          publisher = _context22.sent;
          _context22.next = 5;
          return processPublisherCreator(orm, userId);
        case 5:
          publisherCreator = _context22.sent;
          _context22.next = 6;
          return processWorkerBee(orm, userId);
        case 6:
          workerBee = _context22.sent;
          _context22.next = 7;
          return processSeriesCreator(orm, userId);
        case 7:
          seriesCreator = _context22.sent;
          _context22.next = 8;
          return processSprinter(orm, userId);
        case 8:
          sprinter = _context22.sent;
          _context22.next = 9;
          return processFunRunner(orm, userId);
        case 9:
          funRunner = _context22.sent;
          _context22.next = 10;
          return processMarathoner(orm, userId);
        case 10:
          marathoner = _context22.sent;
          _context22.next = 11;
          return processTimeTraveller(orm, userId, revisionId);
        case 11:
          timeTraveller = _context22.sent;
          _context22.next = 12;
          return processHotOffThePress(orm, userId, revisionId);
        case 12:
          hotOffThePress = _context22.sent;
          alert = [];
          alert.push(achievementToUnlockId(revisionist), achievementToUnlockId(authorCreator), achievementToUnlockId(limitedEdition), achievementToUnlockId(publisher), achievementToUnlockId(publisherCreator), achievementToUnlockId(workerBee), achievementToUnlockId(seriesCreator), achievementToUnlockId(sprinter), achievementToUnlockId(funRunner), achievementToUnlockId(marathoner), achievementToUnlockId(timeTraveller), achievementToUnlockId(hotOffThePress));
          alert = (0, _flattenDeep2.default)(alert);
          alert = alert.join(',');
          return _context22.abrupt("return", {
            alert: alert,
            authorCreator: authorCreator,
            funRunner: funRunner,
            hotOffThePress: hotOffThePress,
            limitedEdition: limitedEdition,
            marathoner: marathoner,
            publisher: publisher,
            publisherCreator: publisherCreator,
            revisionist: revisionist,
            seriesCreator: seriesCreator,
            sprinter: sprinter,
            timeTraveller: timeTraveller,
            workerBee: workerBee
          });
        case 13:
        case "end":
          return _context22.stop();
      }
    }, _callee22);
  }));
  return _processEdit.apply(this, arguments);
}
//# sourceMappingURL=achievement.js.map