<commit_before>const listModelQuerySettings = (qb, table, column, opts) => {
  qb.limit(opts.limit || 20)

  // this will require the fetch or fetchAll call to have {columns: [column]}
  qb.groupBy(column)

  if (opts.autocomplete) {
    Search.addTermToQueryBuilder(opts.autocomplete, qb, {
      columns: [format('%s.%s', table, column)]
    })
    // qb.whereRaw('users_org.org_name ilike ?', opts.autocomplete + '%')
  }

  qb.whereRaw(format('length(%s) < 40', column))
}

module.exports = {
  forProjects: function (opts) {
    return Project.query(qb => {
      if (opts.user) {
        qb.leftJoin('projects_users', 'projects.id', 'projects_users.project_id')
        qb.where(function () {
          this.where('projects.user_id', opts.user)
          .orWhere('projects_users.user_id', opts.user)
        })
      }

      if (opts.community) {
        qb.where(function () {
          var clause = this.whereIn('community_id', opts.community)

          if (opts.includePublic) {
            clause.orWhere('visibility', Project.Visibility.PUBLIC)
          }

          if (opts.publicOnly) {
            clause.andWhere('visibility', Project.Visibility.PUBLIC)
          }
        })
      } else if (opts.publicOnly) {
        qb.where('visibility', Project.Visibility.PUBLIC)
      }

      if (opts.published) {
        qb.whereRaw('published_at is not null')
      }

      if (opts.term) {
        Search.addTermToQueryBuilder(opts.term, qb, {
          columns: ['projects.title', 'projects.intention', 'projects.details']
        })
      }

      // this counts total rows matching the criteria, disregarding limit,
      // which is useful for pagination
      qb.select(bookshelf.knex.raw('projects.*, count(*) over () as total'))

      qb.limit(opts.limit)
      qb.offset(opts.offset)
      qb.groupBy('projects.id')
      qb.orderBy('projects.updated_at', 'desc')
    })
  },

  forCommunities: function (opts) {
    return Community.query(qb => {
      if (opts.communities) {
        qb.whereIn('community.id', opts.communities)
      }

      if (opts.autocomplete) {
        qb.whereRaw('community.name ilike ?', opts.autocomplete + '%')
      }

      if (opts.term) {
        Search.addTermToQueryBuilder(opts.term, qb, {
          columns: ['community.name']
        })
      }

      // this counts total rows matching the criteria, disregarding limit,
      // which is useful for pagination
      qb.select(bookshelf.knex.raw('community.*, count(*) over () as total'))

      qb.limit(opts.limit)
      qb.offset(opts.offset)
      qb.groupBy('community.id')
      qb.orderBy('community.name', 'asc')
    })
  },

  forPosts: function (opts) {
    return Post.query(function (qb) {
      qb.limit(opts.limit || 20)
      qb.offset(opts.offset)
      qb.where({'post.active': true})

      // this counts total rows matching the criteria, disregarding limit,
      // which is useful for pagination
      qb.select(bookshelf.knex.raw('post.*, count(*) over () as total'))

      if (opts.users) {
        qb.whereIn('post.user_id', opts.users)
      }

      if (opts.excludeUsers) {
        qb.whereNotIn('post.user_id', opts.excludeUsers)
      }

      if (opts.tag) {
        qb.join('posts_tags', 'posts_tags.post_id', '=', 'post.id')
        qb.whereIn('posts_tags.tag_id', [opts.tag])
      }

      if (opts.project) {
        qb.join('posts_projects', 'posts_projects.post_id', '=', 'post.id')
        qb.where('posts_projects.project_id', opts.project)
      } else {
        qb.where('post.visibility', '!=', Post.Visibility.DRAFT_PROJECT)
      }

      if (opts.term) {
        Search.addTermToQueryBuilder(opts.term, qb, {
          columns: ['post.name', 'post.description']
        })
      }

      if (opts.follower) {
        qb.join('follower', 'follower.post_id', '=', 'post.id')
        qb.where('follower.user_id', opts.follower)
        qb.whereRaw('(post.user_id != ? or post.user_id is null)', opts.follower)
      }

      if (!opts.type || opts.type === 'all') {
        qb.where('post.type', '!=', 'welcome')
      } else if (opts.type !== 'all+welcome') {
        qb.where({type: opts.type})
      }

      if (opts.type === 'event' && opts.filter === 'future') {
        qb.whereRaw('(post.start_time > now())')
      }

      if (opts.type === 'project' && opts.filter === 'mine') {
        qb.leftJoin('follower', 'post.id', 'follower.post_id')
        qb.where(function () {
          this.where('post.user_id', opts.currentUserId)
          .orWhere('follower.user_id', opts.currentUserId)
        })
      }

      if (opts.start_time && opts.end_time) {
        qb.whereRaw('((post.created_at between ? and ?) or (post.updated_at between ? and ?))',
          [opts.start_time, opts.end_time, opts.start_time, opts.end_time])
      }

      if (opts.visibility) {
        qb.whereIn('visibility', opts.visibility)
      }

      if (opts.sort === 'suggested') {
        qb.join('user_post_relevance', 'user_post_relevance.post_id', '=', 'post.id')
        qb.where('user_post_relevance.user_id', opts.forUser)
        qb.orderBy('similarity', 'desc')
        qb.groupBy('similarity')
      } else if (opts.sort === 'fulfilled_at') {
        qb.orderByRaw('post.fulfilled_at desc, post.updated_at desc')
      } else if (Array.isArray(opts.sort)) {
        qb.orderBy(opts.sort[0], opts.sort[1])
      } else if (opts.sort) {
        qb.orderBy(opts.sort, 'desc')
      }

      if (opts.omit) {
        qb.whereNotIn('post.id', opts.omit)
      }

      if (opts.communities) {
        qb.join('post_community', 'post_community.post_id', '=', 'post.id')
        qb.whereIn('post_community.community_id', opts.communities)
        qb.groupBy(['post.id', 'post_community.post_id'])
      }

      if (!opts.includeChildren) {
        qb.where('parent_post_id', null)
      }
    })
  },

  forUsers: function (opts) {
    return User.query(function (qb) {
      qb.limit(opts.limit || 1000)
      qb.offset(opts.offset || 0)
      qb.where('users.active', '=', true)

      // this is not necessarily what any consumer desires, but
      // some ordering must be specified for pagination
      qb.orderBy('name', 'asc')

      // this counts total rows matching the criteria, disregarding limit,
      // which is useful for pagination
      qb.select(bookshelf.knex.raw('users.*, count(users.*) over () as total'))

      if (opts.communities && opts.project) {
        qb.leftJoin('users_community', 'users_community.user_id', 'users.id')
        qb.leftJoin('projects_users', 'projects_users.user_id', 'users.id')
        qb.leftJoin('projects', 'projects.user_id', 'users.id')

        qb.where(function () {
          this.where(function () {
            this.whereIn('users_community.community_id', opts.communities)
            this.where('users_community.active', true)
          })
          .orWhere(function () {
            this.where('projects.id', opts.project)
            .orWhere('projects_users.project_id', opts.project)
          })
        })
      } else if (opts.communities) {
        qb.join('users_community', 'users_community.user_id', '=', 'users.id')
        qb.whereIn('users_community.community_id', opts.communities)
        qb.where('users_community.active', true)
      } else if (opts.project) {
        qb.join('projects_users', 'projects_users.user_id', '=', 'users.id')
        qb.leftJoin('projects', 'projects.user_id', 'users.id')
        qb.where(function () {
          this.where('projects.id', opts.project)
          .orWhere('projects_users.project_id', opts.project)
        })
      }

      if (opts.autocomplete) {
        qb.whereRaw('users.name ilike ?', opts.autocomplete + '%')
      }

      if (opts.term) {
        qb.leftJoin('users_skill', 'users_skill.user_id', 'users.id')
        qb.leftJoin('users_org', 'users_org.user_id', 'users.id')
        Search.addTermToQueryBuilder(opts.term, qb, {
          columns: ['users.name', 'users.bio', 'users_skill.skill_name', 'users_org.org_name']
        })
      }

      // prevent duplicates due to the joins
      qb.groupBy('users.id')

      if (opts.start_time && opts.end_time) {
        qb.whereRaw('users.created_at between ? and ?', [opts.start_time, opts.end_time])
      }

      if (opts.exclude) {
        qb.whereNotIn('id', opts.exclude)
      }
    })
  },

  forSkills: function (opts) {
    return Skill.query(qb => {
      listModelQuerySettings(qb, 'users_skill', 'skill_name', opts)
    })
  },

  forOrganizations: function (opts) {
    return Organization.query(qb => {
      listModelQuerySettings(qb, 'users_org', 'org_name', opts)
    })
  },

  forTags: function (opts) {
    return Tag.query(q => {
      if (opts.communities) {
        q.join('communities_tags', 'communities_tags.tag_id', '=', 'tags.id')
        q.whereIn('communities_tags.community_id', opts.communities)
      }
      if (opts.autocomplete) {
        q.whereRaw('tags.name ilike ?', opts.autocomplete + '%')
      }

      q.groupBy('tags.id')
    })
  },

  addTermToQueryBuilder: function (term, qb, opts) {
    var query = _.chain(term.split(/\s*\s/)) // split on whitespace
    .map(word => word.replace(/[,;|:&()!\\]+/, ''))
    .reject(_.isEmpty)
    .map(word => word + ':*') // add prefix matching
    .reduce((result, word) => {
      // build the tsquery string using logical AND operands
      result += ' & ' + word
      return result
    }).value()

    var statement = format('(%s)',
      opts.columns
      .map(col => format("(to_tsvector('english', %s) @@ to_tsquery(?))", col))
      .join(' or '))

    var values = _.times(opts.columns.length, () => query)

    qb.where(function () {
      this.whereRaw(statement, values)
    })
  }

}
<commit_msg>set limit for tag searches<commit_after>const listModelQuerySettings = (qb, table, column, opts) => {
  qb.limit(opts.limit || 20)

  // this will require the fetch or fetchAll call to have {columns: [column]}
  qb.groupBy(column)

  if (opts.autocomplete) {
    Search.addTermToQueryBuilder(opts.autocomplete, qb, {
      columns: [format('%s.%s', table, column)]
    })
    // qb.whereRaw('users_org.org_name ilike ?', opts.autocomplete + '%')
  }

  qb.whereRaw(format('length(%s) < 40', column))
}

module.exports = {
  forProjects: function (opts) {
    return Project.query(qb => {
      if (opts.user) {
        qb.leftJoin('projects_users', 'projects.id', 'projects_users.project_id')
        qb.where(function () {
          this.where('projects.user_id', opts.user)
          .orWhere('projects_users.user_id', opts.user)
        })
      }

      if (opts.community) {
        qb.where(function () {
          var clause = this.whereIn('community_id', opts.community)

          if (opts.includePublic) {
            clause.orWhere('visibility', Project.Visibility.PUBLIC)
          }

          if (opts.publicOnly) {
            clause.andWhere('visibility', Project.Visibility.PUBLIC)
          }
        })
      } else if (opts.publicOnly) {
        qb.where('visibility', Project.Visibility.PUBLIC)
      }

      if (opts.published) {
        qb.whereRaw('published_at is not null')
      }

      if (opts.term) {
        Search.addTermToQueryBuilder(opts.term, qb, {
          columns: ['projects.title', 'projects.intention', 'projects.details']
        })
      }

      // this counts total rows matching the criteria, disregarding limit,
      // which is useful for pagination
      qb.select(bookshelf.knex.raw('projects.*, count(*) over () as total'))

      qb.limit(opts.limit)
      qb.offset(opts.offset)
      qb.groupBy('projects.id')
      qb.orderBy('projects.updated_at', 'desc')
    })
  },

  forCommunities: function (opts) {
    return Community.query(qb => {
      if (opts.communities) {
        qb.whereIn('community.id', opts.communities)
      }

      if (opts.autocomplete) {
        qb.whereRaw('community.name ilike ?', opts.autocomplete + '%')
      }

      if (opts.term) {
        Search.addTermToQueryBuilder(opts.term, qb, {
          columns: ['community.name']
        })
      }

      // this counts total rows matching the criteria, disregarding limit,
      // which is useful for pagination
      qb.select(bookshelf.knex.raw('community.*, count(*) over () as total'))

      qb.limit(opts.limit)
      qb.offset(opts.offset)
      qb.groupBy('community.id')
      qb.orderBy('community.name', 'asc')
    })
  },

  forPosts: function (opts) {
    return Post.query(function (qb) {
      qb.limit(opts.limit || 20)
      qb.offset(opts.offset)
      qb.where({'post.active': true})

      // this counts total rows matching the criteria, disregarding limit,
      // which is useful for pagination
      qb.select(bookshelf.knex.raw('post.*, count(*) over () as total'))

      if (opts.users) {
        qb.whereIn('post.user_id', opts.users)
      }

      if (opts.excludeUsers) {
        qb.whereNotIn('post.user_id', opts.excludeUsers)
      }

      if (opts.tag) {
        qb.join('posts_tags', 'posts_tags.post_id', '=', 'post.id')
        qb.whereIn('posts_tags.tag_id', [opts.tag])
      }

      if (opts.project) {
        qb.join('posts_projects', 'posts_projects.post_id', '=', 'post.id')
        qb.where('posts_projects.project_id', opts.project)
      } else {
        qb.where('post.visibility', '!=', Post.Visibility.DRAFT_PROJECT)
      }

      if (opts.term) {
        Search.addTermToQueryBuilder(opts.term, qb, {
          columns: ['post.name', 'post.description']
        })
      }

      if (opts.follower) {
        qb.join('follower', 'follower.post_id', '=', 'post.id')
        qb.where('follower.user_id', opts.follower)
        qb.whereRaw('(post.user_id != ? or post.user_id is null)', opts.follower)
      }

      if (!opts.type || opts.type === 'all') {
        qb.where('post.type', '!=', 'welcome')
      } else if (opts.type !== 'all+welcome') {
        qb.where({type: opts.type})
      }

      if (opts.type === 'event' && opts.filter === 'future') {
        qb.whereRaw('(post.start_time > now())')
      }

      if (opts.type === 'project' && opts.filter === 'mine') {
        qb.leftJoin('follower', 'post.id', 'follower.post_id')
        qb.where(function () {
          this.where('post.user_id', opts.currentUserId)
          .orWhere('follower.user_id', opts.currentUserId)
        })
      }

      if (opts.start_time && opts.end_time) {
        qb.whereRaw('((post.created_at between ? and ?) or (post.updated_at between ? and ?))',
          [opts.start_time, opts.end_time, opts.start_time, opts.end_time])
      }

      if (opts.visibility) {
        qb.whereIn('visibility', opts.visibility)
      }

      if (opts.sort === 'suggested') {
        qb.join('user_post_relevance', 'user_post_relevance.post_id', '=', 'post.id')
        qb.where('user_post_relevance.user_id', opts.forUser)
        qb.orderBy('similarity', 'desc')
        qb.groupBy('similarity')
      } else if (opts.sort === 'fulfilled_at') {
        qb.orderByRaw('post.fulfilled_at desc, post.updated_at desc')
      } else if (Array.isArray(opts.sort)) {
        qb.orderBy(opts.sort[0], opts.sort[1])
      } else if (opts.sort) {
        qb.orderBy(opts.sort, 'desc')
      }

      if (opts.omit) {
        qb.whereNotIn('post.id', opts.omit)
      }

      if (opts.communities) {
        qb.join('post_community', 'post_community.post_id', '=', 'post.id')
        qb.whereIn('post_community.community_id', opts.communities)
        qb.groupBy(['post.id', 'post_community.post_id'])
      }

      if (!opts.includeChildren) {
        qb.where('parent_post_id', null)
      }
    })
  },

  forUsers: function (opts) {
    return User.query(function (qb) {
      qb.limit(opts.limit || 1000)
      qb.offset(opts.offset || 0)
      qb.where('users.active', '=', true)

      // this is not necessarily what any consumer desires, but
      // some ordering must be specified for pagination
      qb.orderBy('name', 'asc')

      // this counts total rows matching the criteria, disregarding limit,
      // which is useful for pagination
      qb.select(bookshelf.knex.raw('users.*, count(users.*) over () as total'))

      if (opts.communities && opts.project) {
        qb.leftJoin('users_community', 'users_community.user_id', 'users.id')
        qb.leftJoin('projects_users', 'projects_users.user_id', 'users.id')
        qb.leftJoin('projects', 'projects.user_id', 'users.id')

        qb.where(function () {
          this.where(function () {
            this.whereIn('users_community.community_id', opts.communities)
            this.where('users_community.active', true)
          })
          .orWhere(function () {
            this.where('projects.id', opts.project)
            .orWhere('projects_users.project_id', opts.project)
          })
        })
      } else if (opts.communities) {
        qb.join('users_community', 'users_community.user_id', '=', 'users.id')
        qb.whereIn('users_community.community_id', opts.communities)
        qb.where('users_community.active', true)
      } else if (opts.project) {
        qb.join('projects_users', 'projects_users.user_id', '=', 'users.id')
        qb.leftJoin('projects', 'projects.user_id', 'users.id')
        qb.where(function () {
          this.where('projects.id', opts.project)
          .orWhere('projects_users.project_id', opts.project)
        })
      }

      if (opts.autocomplete) {
        qb.whereRaw('users.name ilike ?', opts.autocomplete + '%')
      }

      if (opts.term) {
        qb.leftJoin('users_skill', 'users_skill.user_id', 'users.id')
        qb.leftJoin('users_org', 'users_org.user_id', 'users.id')
        Search.addTermToQueryBuilder(opts.term, qb, {
          columns: ['users.name', 'users.bio', 'users_skill.skill_name', 'users_org.org_name']
        })
      }

      // prevent duplicates due to the joins
      qb.groupBy('users.id')

      if (opts.start_time && opts.end_time) {
        qb.whereRaw('users.created_at between ? and ?', [opts.start_time, opts.end_time])
      }

      if (opts.exclude) {
        qb.whereNotIn('id', opts.exclude)
      }
    })
  },

  forSkills: function (opts) {
    return Skill.query(qb => {
      listModelQuerySettings(qb, 'users_skill', 'skill_name', opts)
    })
  },

  forOrganizations: function (opts) {
    return Organization.query(qb => {
      listModelQuerySettings(qb, 'users_org', 'org_name', opts)
    })
  },

  forTags: function (opts) {
    return Tag.query(q => {
      if (opts.communities) {
        q.join('communities_tags', 'communities_tags.tag_id', '=', 'tags.id')
        q.whereIn('communities_tags.community_id', opts.communities)
      }
      if (opts.autocomplete) {
        q.whereRaw('tags.name ilike ?', opts.autocomplete + '%')
      }

      q.groupBy('tags.id')
      q.limit(opts.limit)
    })
  },

  addTermToQueryBuilder: function (term, qb, opts) {
    var query = _.chain(term.split(/\s*\s/)) // split on whitespace
    .map(word => word.replace(/[,;|:&()!\\]+/, ''))
    .reject(_.isEmpty)
    .map(word => word + ':*') // add prefix matching
    .reduce((result, word) => {
      // build the tsquery string using logical AND operands
      result += ' & ' + word
      return result
    }).value()

    var statement = format('(%s)',
      opts.columns
      .map(col => format("(to_tsvector('english', %s) @@ to_tsquery(?))", col))
      .join(' or '))

    var values = _.times(opts.columns.length, () => query)

    qb.where(function () {
      this.whereRaw(statement, values)
    })
  }

}
<|endoftext|>


<commit_before>const { Listener } = require('discord-akairo');
const Logger = require('../../../util/Logger');

function exec(err) {
    Logger.warn('An error occured. Contact someone who might know what it means.');
    Logger.error(err);
}

module.exports = new Listener('unhandledRejection', exec, {
    emitter: 'process',
    eventName: 'unhandledRejection',
    category: 'process'
});
<commit_msg>Delete unhandledRejection.js<commit_after><|endoftext|>


<commit_before>var bind = require('../internals/function-bind-context');
var has = require('../internals/has');
var IndexedObject = require('../internals/indexed-object');
var toObject = require('../internals/to-object');
var toLength = require('../internals/to-length');
var toPropertyKey = require('../internals/to-property-key');
var objectCreate = require('../internals/object-create');
var arrayFromConstructorAndList = require('../internals/array-from-constructor-and-list');

var push = [].push;

module.exports = function ($this, callbackfn, that, specificConstructor) {
  var O = toObject($this);
  var self = IndexedObject(O);
  var boundFunction = bind(callbackfn, that, 3);
  var target = objectCreate(null);
  var length = toLength(self.length);
  var index = 0;
  var Constructor, key, value;
  for (;length > index; index++) {
    value = self[index];
    key = toPropertyKey(boundFunction(value, index, O));
    if (has(target, key)) push.call(target[key], value);
    else target[key] = [value];
  }
  if (specificConstructor) {
    Constructor = specificConstructor(O);
    if (Constructor !== Array) {
      for (key in target) target[key] = arrayFromConstructorAndList(Constructor, target[key]);
    }
  } return target;
};
<commit_msg>IE10 fix<commit_after>var bind = require('../internals/function-bind-context');
var IndexedObject = require('../internals/indexed-object');
var toObject = require('../internals/to-object');
var toLength = require('../internals/to-length');
var toPropertyKey = require('../internals/to-property-key');
var objectCreate = require('../internals/object-create');
var arrayFromConstructorAndList = require('../internals/array-from-constructor-and-list');

var push = [].push;

module.exports = function ($this, callbackfn, that, specificConstructor) {
  var O = toObject($this);
  var self = IndexedObject(O);
  var boundFunction = bind(callbackfn, that, 3);
  var target = objectCreate(null);
  var length = toLength(self.length);
  var index = 0;
  var Constructor, key, value;
  for (;length > index; index++) {
    value = self[index];
    key = toPropertyKey(boundFunction(value, index, O));
    // in some IE10 builds, `hasOwnProperty` returns incorrect result on integer keys
    // but since it's a `null` prototype object, we can safely use `in`
    if (key in target) push.call(target[key], value);
    else target[key] = [value];
  }
  if (specificConstructor) {
    Constructor = specificConstructor(O);
    if (Constructor !== Array) {
      for (key in target) target[key] = arrayFromConstructorAndList(Constructor, target[key]);
    }
  } return target;
};
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import DashboardPopover from '../Dashboard/DashboardPopover'
import RaisedButton from 'material-ui/RaisedButton'
import Divider from 'material-ui/Divider'
import Menu from 'material-ui/Menu'
import MenuItem from 'material-ui/MenuItem'
import IconButton from 'material-ui/IconButton'
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert'
import SettingsIcon from 'material-ui/svg-icons/action/settings'
import PersonAddIcon from 'material-ui/svg-icons/social/person-add'
import ChartIcon from 'material-ui/svg-icons/editor/insert-chart'
import {
  goToInviteFriends,
  goToDonate,
  goToSettings,
  goToStats
} from 'navigation/navigation'
import appTheme, {
  dashboardIconActiveColor,
  dashboardIconInactiveColor,
  dividerColor
} from 'theme/default'

class UserMenu extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      heartsHover: false,
      heartsPopoverOpen: false,
      menuIconHover: false,
      menuOpen: false
    }
  }

  onHeartsHover (hovering) {
    this.setState({
      heartsHover: hovering
    })
  }

  onHeartsClick (event) {
    this.setState({
      heartsPopoverOpen: !this.state.open,
      heartsPopoverAnchorElem: event.currentTarget
    })
  }

  handleHeartsPopoverClose () {
    this.setState({
      heartsPopoverOpen: false
    })
  }

  onMenuClick (event) {
    this.setState({
      menuOpen: !this.state.open,
      menuPopoverAnchorElem: event.currentTarget
    })
  }

  handleMenuPopoverClose () {
    this.setState({
      menuOpen: false
    })
  }

  onMenuIconHover (hovering) {
    this.setState({
      menuIconHover: hovering
    })
  }

  onHover (hovering) {
    this.setState({
      hover: hovering
    })
  }

  render () {
    const { app, user } = this.props
    if (!user || !app) {
      return null
    }

    const userMenuStyle = {
      display: 'flex',
      alignItems: 'center'
    }

    // Hearts style
    const textStyle = Object.assign({}, {
      color: this.state.heartsHover
        ? dashboardIconActiveColor
        : dashboardIconInactiveColor,
      transition: 'color 300ms ease-in',
      fontSize: 18,
      fontWeight: 'normal',
      fontFamily: appTheme.fontFamily,
      userSelect: 'none',
      cursor: 'pointer'
    }, this.props.style)
    const heartsStyle = Object.assign({}, textStyle, {
      marginRight: 0
    })
    const heartIconStyle = {
      marginLeft: 3
    }

    // Hearts popover style
    const heartsPopoverStyle = {
      textAlign: 'center',
      width: 210
    }
    const heartsPopoverSectionStyle = {
    }
    const dividerStyle = {
      marginTop: 16,
      marginBottom: 12
    }
    const statTextStyle = {
      fontSize: 14,
      display: 'block',
      marginTop: 4,
      marginBottom: 4
    }
    const smallHeartIconStyle = {
      marginLeft: 2
    }
    const statNumberStyle = {
      fontSize: 24,
      display: 'block'
    }
    const popoverButtonStyle = {
      marginTop: 6,
      marginBottom: 0
    }

    // Popover section on how to earn Hearts.
    const rewardMethodContainerStyle = Object.assign({}, statTextStyle, {
      display: 'flex',
      textAlign: 'left',
      color: dividerColor,
      marginTop: 1,
      marginBottom: 1
    })
    const rewardAmountsSectionStyle = Object.assign({}, heartsPopoverSectionStyle, {
      paddingLeft: 12,
      paddingRight: 12
    })
    const rewardTextStyle = {
      textAlign: 'left',
      flex: 6
    }
    const rewardValueStyle = {
      flex: 3,
      textAlign: 'right'
    }

    // Menu style
    const menuPopoverStyle = {
      padding: 0,
      width: 200
    }
    const menuIconButtonStyle = {
      padding: 0,
      width: 40,
      height: 40
    }
    const menuIconStyle = {
      color: (
        this.state.menuIconHover
        ? dashboardIconActiveColor
        : dashboardIconInactiveColor
      ),
      transition: 'color 300ms ease-in',
      fontSize: 22
    }
    const menuStyle = {
      backgroundColor: 'transparent',
      fontFamily: appTheme.fontFamily
    }
    const menuItemStyle = {
      fontSize: 14,
      color: appTheme.palette.alternateTextColor
    }
    const menuItemIconColor = '#FFFFFF'
    const menuItemFontIconStyle = {
      color: menuItemIconColor,
      top: 2,
      left: 6,
      fontSize: 18,
      height: 18,
      width: 18
    }
    const menuItemSvgIconStyle = {
      color: menuItemIconColor,
      // top: 2,
      height: 22,
      width: 22
    }

    // TODO: add level bar
    return (

          {user.vcCurrent}

        <DashboardPopover
          open={this.state.heartsPopoverOpen}
          anchorEl={this.state.heartsPopoverAnchorElem}
          onRequestClose={this.handleHeartsPopoverClose.bind(this)}
          style={heartsPopoverStyle}
        >

              Level {user.level}

                {user.heartsUntilNextLevel}
                 until next level

            <Divider style={dividerStyle} />

                  {user.vcDonatedAllTime}
                 Donated

                <RaisedButton
                  label='Donate Hearts'
                  style={popoverButtonStyle}
                  primary
                  onClick={goToDonate}
                />

            <Divider style={dividerStyle} />

                {user.numUsersRecruited} Tabbers Recruited

                <RaisedButton
                  label='Invite A Friend'
                  labelPosition='before'
                  style={popoverButtonStyle}
                  primary
                  onClick={goToInviteFriends}
               />

            <Divider style={dividerStyle} />

                Open a tab
                1

                Recruit a friend
                {app.referralVcReward}

        </DashboardPopover>
        <IconButton
          style={menuIconButtonStyle}
          iconStyle={menuIconStyle}
          onMouseEnter={this.onMenuIconHover.bind(this, true)}
          onMouseLeave={this.onMenuIconHover.bind(this, false)}
          onClick={this.onMenuClick.bind(this)}
        >
          <MoreVertIcon />
        </IconButton>
        <DashboardPopover
          style={menuPopoverStyle}
          open={this.state.menuOpen}
          anchorEl={this.state.menuPopoverAnchorElem}
          onRequestClose={this.handleMenuPopoverClose.bind(this)}
        >
          <Menu
            style={menuStyle}
            menuItemStyle={menuItemStyle}
          >
            <MenuItem primaryText='Settings' onClick={goToSettings}
              leftIcon={
                <SettingsIcon color={menuItemIconColor} style={menuItemSvgIconStyle} />
              }
            />
            <MenuItem primaryText='Donate Hearts' onClick={goToDonate}
              leftIcon={

              }
            />
            <MenuItem primaryText='Invite Friends' onClick={goToInviteFriends}
              leftIcon={
                <PersonAddIcon color={menuItemIconColor} style={menuItemSvgIconStyle} />
              }
            />
            <MenuItem primaryText='Your Stats' onClick={goToStats}
              leftIcon={
                <ChartIcon color={menuItemIconColor} style={menuItemSvgIconStyle} />
              }
            />
          </Menu>
        </DashboardPopover>

    )
  }
}

UserMenu.propTypes = {
  app: PropTypes.shape({
    referralVcReward: PropTypes.number.isRequired
  }),
  user: PropTypes.shape({
    vcCurrent: PropTypes.number.isRequired,
    level: PropTypes.number.isRequired,
    heartsUntilNextLevel: PropTypes.number.isRequired,
    vcDonatedAllTime: PropTypes.number.isRequired
  }),
  style: PropTypes.object
}

UserMenu.defaultProps = {
  style: {}
}

export default UserMenu
<commit_msg>Replace font-awesome heart with material SVG icon in user dropdown.<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import DashboardPopover from '../Dashboard/DashboardPopover'
import RaisedButton from 'material-ui/RaisedButton'
import Divider from 'material-ui/Divider'
import Menu from 'material-ui/Menu'
import MenuItem from 'material-ui/MenuItem'
import IconButton from 'material-ui/IconButton'
import HeartIcon from 'material-ui/svg-icons/action/favorite'
import HeartBorderIcon from 'material-ui/svg-icons/action/favorite-border'
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert'
import SettingsIcon from 'material-ui/svg-icons/action/settings'
import PersonAddIcon from 'material-ui/svg-icons/social/person-add'
import ChartIcon from 'material-ui/svg-icons/editor/insert-chart'
import {
  goToInviteFriends,
  goToDonate,
  goToSettings,
  goToStats
} from 'navigation/navigation'
import appTheme, {
  dashboardIconActiveColor,
  dashboardIconInactiveColor,
  dividerColor
} from 'theme/default'

class UserMenu extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      heartsHover: false,
      heartsPopoverOpen: false,
      menuIconHover: false,
      menuOpen: false
    }
  }

  onHeartsHover (hovering) {
    this.setState({
      heartsHover: hovering
    })
  }

  onHeartsClick (event) {
    this.setState({
      heartsPopoverOpen: !this.state.open,
      heartsPopoverAnchorElem: event.currentTarget
    })
  }

  handleHeartsPopoverClose () {
    this.setState({
      heartsPopoverOpen: false
    })
  }

  onMenuClick (event) {
    this.setState({
      menuOpen: !this.state.open,
      menuPopoverAnchorElem: event.currentTarget
    })
  }

  handleMenuPopoverClose () {
    this.setState({
      menuOpen: false
    })
  }

  onMenuIconHover (hovering) {
    this.setState({
      menuIconHover: hovering
    })
  }

  onHover (hovering) {
    this.setState({
      hover: hovering
    })
  }

  render () {
    const { app, user } = this.props
    if (!user || !app) {
      return null
    }

    const userMenuStyle = {
      display: 'flex',
      alignItems: 'center'
    }

    // Hearts style
    const textStyle = Object.assign({}, {
      color: this.state.heartsHover
        ? dashboardIconActiveColor
        : dashboardIconInactiveColor,
      transition: 'color 300ms ease-in',
      fontSize: 18,
      fontWeight: 'normal',
      fontFamily: appTheme.fontFamily,
      userSelect: 'none',
      cursor: 'pointer'
    }, this.props.style)
    const heartsStyle = Object.assign({}, textStyle, {
      marginRight: 0,
      display: 'flex',
      alignItems: 'center'
    })

    // Hearts popover style
    const heartsPopoverStyle = {
      textAlign: 'center',
      width: 210
    }
    const heartsPopoverSectionStyle = {
    }
    const dividerStyle = {
      marginTop: 16,
      marginBottom: 12
    }
    const statTextStyle = {
      fontSize: 14,
      display: 'block',
      marginTop: 4,
      marginBottom: 4
    }
    const smallHeartIconStyle = {
      height: 16,
      marginLeft: -3
    }
    const statNumberStyle = {
      fontSize: 24,
      display: 'block'
    }
    const popoverButtonStyle = {
      marginTop: 6,
      marginBottom: 0
    }

    // Popover section on how to earn Hearts.
    const rewardMethodContainerStyle = Object.assign({}, statTextStyle, {
      display: 'flex',
      textAlign: 'left',
      color: dividerColor,
      marginTop: 1,
      marginBottom: 1
    })
    const rewardAmountsSectionStyle = Object.assign({}, heartsPopoverSectionStyle, {
      paddingLeft: 12,
      paddingRight: 12
    })
    const rewardTextStyle = {
      textAlign: 'left',
      flex: 6
    }
    const rewardValueStyle = {
      flex: 3,
      textAlign: 'right'
    }

    // Menu style
    const menuPopoverStyle = {
      padding: 0,
      width: 200
    }
    const menuIconButtonStyle = {
      padding: 0,
      width: 40,
      height: 40
    }
    const menuIconStyle = {
      color: (
        this.state.menuIconHover
        ? dashboardIconActiveColor
        : dashboardIconInactiveColor
      ),
      transition: 'color 300ms ease-in',
      fontSize: 22
    }
    const menuStyle = {
      backgroundColor: 'transparent',
      fontFamily: appTheme.fontFamily
    }
    const menuItemStyle = {
      fontSize: 14,
      color: appTheme.palette.alternateTextColor
    }
    const menuItemIconColor = '#FFFFFF'
    const menuItemSvgIconStyle = {
      color: menuItemIconColor,
      height: 22,
      width: 22
    }

    // TODO: add level bar
    return (

          {user.vcCurrent}
          <HeartBorderIcon
            style={{ marginLeft: 2, height: 24, width: 24, paddingBottom: 0 }}
            color={dashboardIconInactiveColor}
            hoverColor={dashboardIconActiveColor}
          />

        <DashboardPopover
          open={this.state.heartsPopoverOpen}
          anchorEl={this.state.heartsPopoverAnchorElem}
          onRequestClose={this.handleHeartsPopoverClose.bind(this)}
          style={heartsPopoverStyle}
        >

              Level {user.level}

                {user.heartsUntilNextLevel}
                <HeartBorderIcon
                  style={smallHeartIconStyle}
                  color={dashboardIconActiveColor}
                />
                 until next level

            <Divider style={dividerStyle} />

                  {user.vcDonatedAllTime}
                  <HeartBorderIcon
                    style={{ marginLeft: 2, height: 24, width: 24, paddingBottom: 1 }}
                    color={dashboardIconActiveColor}
                  />

                Donated

                <RaisedButton
                  label='Donate Hearts'
                  style={popoverButtonStyle}
                  primary
                  onClick={goToDonate}
                />

            <Divider style={dividerStyle} />

                {user.numUsersRecruited} Tabbers Recruited

                <RaisedButton
                  label='Invite A Friend'
                  labelPosition='before'
                  style={popoverButtonStyle}
                  primary
                  onClick={goToInviteFriends}
               />

            <Divider style={dividerStyle} />

                Open a tab

                  1
                  <HeartBorderIcon
                    style={Object.assign({}, smallHeartIconStyle, { marginRight: -4 })}
                    color={dividerColor}
                  />

                Recruit a friend

                  {app.referralVcReward}
                  <HeartBorderIcon
                    style={Object.assign({}, smallHeartIconStyle, { marginRight: -4 })}
                    color={dividerColor}
                  />

        </DashboardPopover>
        <IconButton
          style={menuIconButtonStyle}
          iconStyle={menuIconStyle}
          onMouseEnter={this.onMenuIconHover.bind(this, true)}
          onMouseLeave={this.onMenuIconHover.bind(this, false)}
          onClick={this.onMenuClick.bind(this)}
        >
          <MoreVertIcon />
        </IconButton>
        <DashboardPopover
          style={menuPopoverStyle}
          open={this.state.menuOpen}
          anchorEl={this.state.menuPopoverAnchorElem}
          onRequestClose={this.handleMenuPopoverClose.bind(this)}
        >
          <Menu
            style={menuStyle}
            menuItemStyle={menuItemStyle}
          >
            <MenuItem primaryText='Settings' onClick={goToSettings}
              leftIcon={
                <SettingsIcon color={menuItemIconColor} style={menuItemSvgIconStyle} />
              }
            />
            <MenuItem primaryText='Donate Hearts' onClick={goToDonate}
              leftIcon={
                <HeartIcon color={menuItemIconColor} style={menuItemSvgIconStyle} />
              }
            />
            <MenuItem primaryText='Invite Friends' onClick={goToInviteFriends}
              leftIcon={
                <PersonAddIcon color={menuItemIconColor} style={menuItemSvgIconStyle} />
              }
            />
            <MenuItem primaryText='Your Stats' onClick={goToStats}
              leftIcon={
                <ChartIcon color={menuItemIconColor} style={menuItemSvgIconStyle} />
              }
            />
          </Menu>
        </DashboardPopover>

    )
  }
}

UserMenu.propTypes = {
  app: PropTypes.shape({
    referralVcReward: PropTypes.number.isRequired
  }),
  user: PropTypes.shape({
    vcCurrent: PropTypes.number.isRequired,
    level: PropTypes.number.isRequired,
    heartsUntilNextLevel: PropTypes.number.isRequired,
    vcDonatedAllTime: PropTypes.number.isRequired
  }),
  style: PropTypes.object
}

UserMenu.defaultProps = {
  style: {}
}

export default UserMenu
<|endoftext|>


<commit_before>import React from 'react';
import MZBenchRouter from '../utils/MZBenchRouter';
import MZBenchActions from '../actions/MZBenchActions';
import moment from 'moment';
import 'moment-duration-format';
import BenchStore from '../stores/BenchStore';
import TagInput from 'react-categorized-tag-input';
import AuthStore from '../stores/AuthStore';
import InlineEdit from 'react-edit-inline';

class BenchSummary extends React.Component {
    constructor(props) {
        super(props);

        this.state = { tags: props.bench.tags};
    }

    _labelCssClass(status) {
        switch (status) {
            case "complete":
                return "label-success";
            case "failed":
                return "label-danger";
            case "zombie":
                return "label-warning";
            case "stopped":
                return "label-default";
        }
        return "label-info";
    }

    componentWillReceiveProps(newProps) {
        let newTags = newProps.bench.tags;
        let oldTags = this.state.tags;
        if (!newTags.every((e) => {oldTags.indexOf(e) > -1}) ||
            !oldTags.every((e) => {newTags.indexOf(e) > -1})) {
            this.setState({tags: newProps.bench.tags.slice()})
        }
    }

    render() {
        let bench = this.props.bench;

        let labelClass = this._labelCssClass(bench.status);

        var tagSuggestions = BenchStore.getAllTags();
        this.state.tags.reduce(
            (acc, t) => {
                var i = acc.indexOf(t);
                if (i > -1) acc.splice(i, 1);
                return acc;
            }, tagSuggestions);

        var tags = this.state.tags.slice().map((t) => {return {title: t, category: 'cat1'};});

        var canStop = AuthStore.isAnonymousServer() ||
                      (this.props.bench.author == AuthStore.userLogin());

        return (

Scenario

                                    #{bench.id + ' '}

                                        <InlineEdit className=""
                                                    validate={ (m) => { return true; } }
                                                    activeClassName="inline-change-input"
                                                    text={bench.name}
                                                    paramName="name"
                                                    change={this._updateBenchName.bind(this)}
                                                    />

Author

                                    {bench.author_name == "" ? bench.author : bench.author_name + ' (' + bench.author + ')'}

Cloud

{bench.cloud}, {bench.nodes} node(s)

                        {bench.exclusive != "" ?

Exclusive label

{bench.exclusive}

: null}

Duration

{moment.duration(this.props.duration).format("h [hrs], m [min], s [sec]")}

Date

{bench.start_time_client.format("lll")}

Status

<span className={`label ${labelClass}`}>{bench.status}

                        {bench.parent != "undefined" ?

Parent

<a href={`#/bench/${bench.parent}/overview`}>#{bench.parent}

 : null}

Tags

                                    <TagInput value={tags}
                                              categories={[{
                                                        id: 'cat1', type: 'tag',
                                                        title: 'existing tags',
                                                        items: tagSuggestions.slice(),
                                                        single: false
                                                      }]}
                                              addNew={true}
                                              transformTag={(tag) => {return tag.title;}}
                                              onChange={this._handleTagChange.bind(this)}
                                              placeholder="Add a tag"
                                              />

                                 Stop

                                     Restart

                                <button className="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">

                                </button>

Clone

        );
    }

    _updateBenchName(newName) {
        MZBenchActions.updateBenchName(this.props.bench.id, newName.name);
    }

    _handleTagChange(tags) {
        var new_tags = tags.map((t) => {return t.title;});
        var old_tags = this.state.tags;
        this.setState({tags: new_tags});
        new_tags.map((t) => {
            if (old_tags.indexOf(t) == -1) {
                MZBenchActions.addBenchTag(this.props.bench.id, t);
            }
        });

        old_tags.map((t) => {
            if (new_tags.indexOf(t) == -1) {
                MZBenchActions.removeBenchTag(this.props.bench.id, t);
            }
        });
    }

    _onCloneBench(event) {
        event.preventDefault();
        MZBenchActions.cloneBench(parseInt($(event.target).data("id")));
        MZBenchRouter.navigate("#/new", {});
    }

    _onClick(event) {
        event.preventDefault();

        let anchor = $(event.target).closest('a');
        let action_message = 'Benchmark ' + anchor.data('msg');
        if (!anchor.attr('disabled')) {
            $.ajax({url: anchor.attr('href'),
                    success: () => {$.notify({message: action_message}, {type: 'success', delay: 3000});},
                    error: () => {$.notify({message: 'Request failed'}, {type: 'danger', delay: 3000});},
                    beforeSend: (xhr) => { AuthStore.addCSRFToken(xhr) }
                });
        }
    }
};

BenchSummary.propTypes = {
    bench: React.PropTypes.object.isRequired
};

export default BenchSummary;
<commit_msg>Display req error reason if possible<commit_after>import React from 'react';
import MZBenchRouter from '../utils/MZBenchRouter';
import MZBenchActions from '../actions/MZBenchActions';
import moment from 'moment';
import 'moment-duration-format';
import BenchStore from '../stores/BenchStore';
import TagInput from 'react-categorized-tag-input';
import AuthStore from '../stores/AuthStore';
import InlineEdit from 'react-edit-inline';

class BenchSummary extends React.Component {
    constructor(props) {
        super(props);

        this.state = { tags: props.bench.tags};
    }

    _labelCssClass(status) {
        switch (status) {
            case "complete":
                return "label-success";
            case "failed":
                return "label-danger";
            case "zombie":
                return "label-warning";
            case "stopped":
                return "label-default";
        }
        return "label-info";
    }

    componentWillReceiveProps(newProps) {
        let newTags = newProps.bench.tags;
        let oldTags = this.state.tags;
        if (!newTags.every((e) => {oldTags.indexOf(e) > -1}) ||
            !oldTags.every((e) => {newTags.indexOf(e) > -1})) {
            this.setState({tags: newProps.bench.tags.slice()})
        }
    }

    render() {
        let bench = this.props.bench;

        let labelClass = this._labelCssClass(bench.status);

        var tagSuggestions = BenchStore.getAllTags();
        this.state.tags.reduce(
            (acc, t) => {
                var i = acc.indexOf(t);
                if (i > -1) acc.splice(i, 1);
                return acc;
            }, tagSuggestions);

        var tags = this.state.tags.slice().map((t) => {return {title: t, category: 'cat1'};});

        var canStop = AuthStore.isAnonymousServer() ||
                      (this.props.bench.author == AuthStore.userLogin());

        return (

Scenario

                                    #{bench.id + ' '}

                                        <InlineEdit className=""
                                                    validate={ (m) => { return true; } }
                                                    activeClassName="inline-change-input"
                                                    text={bench.name}
                                                    paramName="name"
                                                    change={this._updateBenchName.bind(this)}
                                                    />

Author

                                    {bench.author_name == "" ? bench.author : bench.author_name + ' (' + bench.author + ')'}

Cloud

{bench.cloud}, {bench.nodes} node(s)

                        {bench.exclusive != "" ?

Exclusive label

{bench.exclusive}

: null}

Duration

{moment.duration(this.props.duration).format("h [hrs], m [min], s [sec]")}

Date

{bench.start_time_client.format("lll")}

Status

<span className={`label ${labelClass}`}>{bench.status}

                        {bench.parent != "undefined" ?

Parent

<a href={`#/bench/${bench.parent}/overview`}>#{bench.parent}

 : null}

Tags

                                    <TagInput value={tags}
                                              categories={[{
                                                        id: 'cat1', type: 'tag',
                                                        title: 'existing tags',
                                                        items: tagSuggestions.slice(),
                                                        single: false
                                                      }]}
                                              addNew={true}
                                              transformTag={(tag) => {return tag.title;}}
                                              onChange={this._handleTagChange.bind(this)}
                                              placeholder="Add a tag"
                                              />

                                 Stop

                                     Restart

                                <button className="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">

                                </button>

Clone

        );
    }

    _updateBenchName(newName) {
        MZBenchActions.updateBenchName(this.props.bench.id, newName.name);
    }

    _handleTagChange(tags) {
        var new_tags = tags.map((t) => {return t.title;});
        var old_tags = this.state.tags;
        this.setState({tags: new_tags});
        new_tags.map((t) => {
            if (old_tags.indexOf(t) == -1) {
                MZBenchActions.addBenchTag(this.props.bench.id, t);
            }
        });

        old_tags.map((t) => {
            if (new_tags.indexOf(t) == -1) {
                MZBenchActions.removeBenchTag(this.props.bench.id, t);
            }
        });
    }

    _onCloneBench(event) {
        event.preventDefault();
        MZBenchActions.cloneBench(parseInt($(event.target).data("id")));
        MZBenchRouter.navigate("#/new", {});
    }

    _onClick(event) {
        event.preventDefault();

        let anchor = $(event.target).closest('a');
        let action_message = 'Benchmark ' + anchor.data('msg');
        if (!anchor.attr('disabled')) {
            $.ajax({url: anchor.attr('href'),
                    success: () => {$.notify({message: action_message}, {type: 'success', delay: 3000});},
                    error: (res) => {
                        if (res.statusText) {
                            $.notify({message: res.statusText}, {type: 'danger', delay: 3000});
                        } else {
                            $.notify({message: 'Request failed'}, {type: 'danger', delay: 3000});
                        }
                    },
                    beforeSend: (xhr) => { AuthStore.addCSRFToken(xhr) }
                });
        }
    }
};

BenchSummary.propTypes = {
    bench: React.PropTypes.object.isRequired
};

export default BenchSummary;
<|endoftext|>


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

/* eslint-env browser */

import 'whatwg-fetch/fetch'; // Imports `fetch` polyfill
import firebase from 'firebase/app';
import 'firebase/messaging';

const SUBSCRIBE_ENDPOINT = '/api/notifications/subscribe';
const UNSUBSCRIBE_ENDPOINT = '/api/notifications/unsubscribe';
const TOPICS_ENDPOINT = '/api/notifications/topics';

const ERROR_PERMISSION_BLOCKED = 'messaging/permission-blocked';
const ERROR_NOTIFICATIONS_BLOCKED = 'messaging/notifications-blocked';
// const ERROR_PERMISSION_DEFAULT = 'messaging/permission-default';

export default class Messaging {
  constructor(messagingSenderId) {
    const config = {
      messagingSenderId: messagingSenderId
    };
    firebase.initializeApp(config);
  }

  /**
   * Fetches from url, adding token to the request body
   * as a JSON
   *
   * @param url - the url to fetch from
   * @param token - the token to be sent to the server
   */
  _postWithToken(url, token) {
    return fetch(url, {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({token: token})
    });
  }

  _checkBlockedNotification(err) {
    return err.code === ERROR_PERMISSION_BLOCKED ||
            err.code === ERROR_NOTIFICATIONS_BLOCKED;
  }
  /**
   * Enables Notifications to a topic.
   * Will ask user permission, if needed and then subscribe
   * to a topic on the server.
   *
   * @param topic - The topic to subscribe to
   * @returns a Promise
   */
  subscribe(topic) {
    console.log('Subscribing to: ' + topic);
    const messaging = firebase.messaging();
    return messaging.requestPermission()
      .then(() => {
        return messaging.getToken();
      })
      .then(token => {
        console.log(token);
        const url = SUBSCRIBE_ENDPOINT + '/' + topic;
        return this._postWithToken(url, token);
      })
      .catch(err => {
        if (this._checkBlockedNotification(err)) {
          err.blocked = true;
        }
        return Promise.reject(err);
      });
  }

  /**
   * Disables Notifications from a topic.
   *
   * @param topic - The topic to unsubscribe from.
   * @returns Promise
   */
  unsubscribe(topic) {
    console.log('Unsubscribing from: ' + topic);
    const messaging = firebase.messaging();
    return messaging.getToken()
      .then(token => {
        const url = UNSUBSCRIBE_ENDPOINT + '/' + topic;
        return this._postWithToken(url, token);
      }).catch(err => {
        if (this._checkBlockedNotification(err)) {
          err.blocked = true;
        }
        return Promise.reject(err);
      });
  }

  /**
   * Gets all topics user has subscribed for
   *
   * @returns Promise<Array<String>>
   */
  getSubscriptions() {
    const messaging = firebase.messaging();
    return messaging.getToken()
      .then(token => {
        if (!token) {
          return Promise.resolve([]);
        }
        const url = TOPICS_ENDPOINT + '?token=' + token;
        return fetch(url, {
          headers: {
            Accept: 'application/json'
          }
        });
      })
      .then(response => {
        return response.json();
      })
      .then(json => {
        return json.subscriptions;
      })
      .catch(err => {
        console.error('Error fetching subscriptions: ', err);
        return Promise.resolve([]);
      });
  }

  /**
   * Checks if user is subscribed to a topic.
   * @param topic - The topic to check for subscriptions.
   * @returns Promise<Boolean> - true if subscribed, false if not.
   */
  isSubscribed(topic) {
    return this.getSubscriptions()
      .then(subscriptions => {
        return subscriptions.indexOf(topic) >= 0;
      });
  }

  isNotificationBlocked() {
    const messaging = firebase.messaging();
    return messaging.getToken()
      .then(_ => {
        return false;
      })
      .catch(err => {
        if (err.code === ERROR_NOTIFICATIONS_BLOCKED) {
          return true;
        }
        return Promise.reject(err);
      });
  }
}
<commit_msg>Fix getSubscription() error<commit_after>/**
 * Copyright 2015-2016, Google, Inc.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* eslint-env browser */

import 'whatwg-fetch/fetch'; // Imports `fetch` polyfill
import firebase from 'firebase/app';
import 'firebase/messaging';

const SUBSCRIBE_ENDPOINT = '/api/notifications/subscribe';
const UNSUBSCRIBE_ENDPOINT = '/api/notifications/unsubscribe';
const TOPICS_ENDPOINT = '/api/notifications/topics';

const ERROR_PERMISSION_BLOCKED = 'messaging/permission-blocked';
const ERROR_NOTIFICATIONS_BLOCKED = 'messaging/notifications-blocked';
// const ERROR_PERMISSION_DEFAULT = 'messaging/permission-default';

export default class Messaging {
  constructor(messagingSenderId) {
    const config = {
      messagingSenderId: messagingSenderId
    };
    firebase.initializeApp(config);
  }

  /**
   * Fetches from url, adding token to the request body
   * as a JSON
   *
   * @param url - the url to fetch from
   * @param token - the token to be sent to the server
   */
  _postWithToken(url, token) {
    return fetch(url, {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({token: token})
    });
  }

  _checkBlockedNotification(err) {
    return err.code === ERROR_PERMISSION_BLOCKED ||
            err.code === ERROR_NOTIFICATIONS_BLOCKED;
  }
  /**
   * Enables Notifications to a topic.
   * Will ask user permission, if needed and then subscribe
   * to a topic on the server.
   *
   * @param topic - The topic to subscribe to
   * @returns a Promise
   */
  subscribe(topic) {
    console.log('Subscribing to: ' + topic);
    const messaging = firebase.messaging();
    return messaging.requestPermission()
      .then(() => {
        return messaging.getToken();
      })
      .then(token => {
        console.log(token);
        const url = SUBSCRIBE_ENDPOINT + '/' + topic;
        return this._postWithToken(url, token);
      })
      .catch(err => {
        if (this._checkBlockedNotification(err)) {
          err.blocked = true;
        }
        return Promise.reject(err);
      });
  }

  /**
   * Disables Notifications from a topic.
   *
   * @param topic - The topic to unsubscribe from.
   * @returns Promise
   */
  unsubscribe(topic) {
    console.log('Unsubscribing from: ' + topic);
    const messaging = firebase.messaging();
    return messaging.getToken()
      .then(token => {
        const url = UNSUBSCRIBE_ENDPOINT + '/' + topic;
        return this._postWithToken(url, token);
      }).catch(err => {
        if (this._checkBlockedNotification(err)) {
          err.blocked = true;
        }
        return Promise.reject(err);
      });
  }

  /**
   * Gets all topics user has subscribed for
   *
   * @returns Promise<Array<String>>
   */
  getSubscriptions() {
    const messaging = firebase.messaging();
    return messaging.getToken()
      .then(token => {
        if (!token) {
          return Promise.resolve([]);
        }
        const url = TOPICS_ENDPOINT + '?token=' + token;
        return fetch(url, {headers: {Accept: 'application/json'}})
          .then(response => {
            if (response.status !== 200) {
              return [];
            }
            return response.json()
              .then(json => {
                return json.subscriptions;
              });
          });
      })
      .catch(err => {
        console.error('Error fetching subscriptions: ', err);
        return Promise.resolve([]);
      });
  }

  /**
   * Checks if user is subscribed to a topic.
   * @param topic - The topic to check for subscriptions.
   * @returns Promise<Boolean> - true if subscribed, false if not.
   */
  isSubscribed(topic) {
    return this.getSubscriptions()
      .then(subscriptions => {
        return subscriptions.indexOf(topic) >= 0;
      });
  }

  isNotificationBlocked() {
    const messaging = firebase.messaging();
    return messaging.getToken()
      .then(_ => {
        return false;
      })
      .catch(err => {
        if (err.code === ERROR_NOTIFICATIONS_BLOCKED) {
          return true;
        }
        return Promise.reject(err);
      });
  }
}
<|endoftext|>


<commit_before>const Sequelize = require('sequelize');
const { v4: uuidv4 } = require('uuid');

/**
 * @param {import('sequelize').QueryInterface} queryInterface
 * @param {import('../lib/config')} config
 * @param {import('../lib/logger')} appLog
 * @param {object} nedb - collection of nedb objects created in /lib/db.js
 * @param {object} sequelizeDb - sequelize instance
 */
// eslint-disable-next-line no-unused-vars
async function up(queryInterface, config, appLog, nedb, sequelizeDb) {
  /**
   * batches can now take the place of query_history,
   * as it acts as a log of queries that have been run.
   *
   * Existing query_history records are inserted into batches.
   * Then a view is created to translate batches and other tables
   * into a wide table, used for reporting.
   *
   * The query_history table is then dropped,
   * as it is no longer necessary.
   * ========================================================
   */

  let rows = await sequelizeDb.query(
    `
      SELECT
        connection_id,
        user_id,
        start_time,
        stop_time,
        query_run_time AS duration_ms,
        query_id,
        query_name AS name,
        query_text AS batch_text,
        created_at,
        row_count,
        incomplete
      FROM
        query_history
    `,
    {
      type: Sequelize.QueryTypes.SELECT,
    }
  );

  const batchRows = [];
  const statementRows = [];

  rows.forEach((row) => {
    const { incomplete, row_count, ...batch } = row;
    const batchId = uuidv4();
    batchRows.push({
      ...batch,
      id: batchId,
      selected_text: row.batch_text,
      status: 'finished',
      updated_at: row.created_at,
    });
    statementRows.push({
      id: uuidv4(),
      sequence: 1,
      batch_id: batchId,
      statement_text: row.batch_text,
      status: 'finished',
      start_time: row.start_time,
      stop_time: row.stop_time,
      duration_ms: row.duration_ms,
      incomplete,
      row_count,
      created_at: row.created_at,
      updated_at: row.created_at,
    });
  });

  if (batchRows.length) {
    await queryInterface.bulkInsert('batches', batchRows);
    await queryInterface.bulkInsert('statements', statementRows);
  }

  await sequelizeDb.query(
    `
      CREATE VIEW vw_query_history AS
        WITH statement_summary AS (
          SELECT
            batch_id,
            SUM(row_count) AS row_count,
            MAX(incomplete) AS incomplete
          FROM
            statements
          GROUP BY
            batch_id
        )
        SELECT
          b.id,
          b.query_id,
          b.name AS query_name,
          b.connection_id,
          c.name AS connection_name,
          b.status,
          b.start_time,
          b.stop_time,
          b.duration_ms,
          b.selected_text AS query_text,
          b.user_id,
          u.email AS user_email,
          ss.row_count,
          ss.incomplete
        FROM
          batches b
          LEFT JOIN users u ON b.user_id = u.id
          LEFT JOIN connections c ON b.connection_id = c.id
          LEFT JOIN statement_summary ss ON b.id = ss.batch_id
    `,
    {
      type: Sequelize.QueryTypes.RAW,
    }
  );

  await queryInterface.dropTable('query_history');
}

module.exports = {
  up,
};
<commit_msg>make view compatible with MSSQL<commit_after>const Sequelize = require('sequelize');
const { v4: uuidv4 } = require('uuid');

/**
 * @param {import('sequelize').QueryInterface} queryInterface
 * @param {import('../lib/config')} config
 * @param {import('../lib/logger')} appLog
 * @param {object} nedb - collection of nedb objects created in /lib/db.js
 * @param {object} sequelizeDb - sequelize instance
 */
// eslint-disable-next-line no-unused-vars
async function up(queryInterface, config, appLog, nedb, sequelizeDb) {
  /**
   * batches can now take the place of query_history,
   * as it acts as a log of queries that have been run.
   *
   * Existing query_history records are inserted into batches.
   * Then a view is created to translate batches and other tables
   * into a wide table, used for reporting.
   *
   * The query_history table is then dropped,
   * as it is no longer necessary.
   * ========================================================
   */

  let rows = await sequelizeDb.query(
    `
      SELECT
        connection_id,
        user_id,
        start_time,
        stop_time,
        query_run_time AS duration_ms,
        query_id,
        query_name AS name,
        query_text AS batch_text,
        created_at,
        row_count,
        incomplete
      FROM
        query_history
    `,
    {
      type: Sequelize.QueryTypes.SELECT,
    }
  );

  const batchRows = [];
  const statementRows = [];

  rows.forEach((row) => {
    const { incomplete, row_count, ...batch } = row;
    const batchId = uuidv4();
    batchRows.push({
      ...batch,
      id: batchId,
      selected_text: row.batch_text,
      status: 'finished',
      updated_at: row.created_at,
    });
    statementRows.push({
      id: uuidv4(),
      sequence: 1,
      batch_id: batchId,
      statement_text: row.batch_text,
      status: 'finished',
      start_time: row.start_time,
      stop_time: row.stop_time,
      duration_ms: row.duration_ms,
      incomplete,
      row_count,
      created_at: row.created_at,
      updated_at: row.created_at,
    });
  });

  if (batchRows.length) {
    await queryInterface.bulkInsert('batches', batchRows);
    await queryInterface.bulkInsert('statements', statementRows);
  }

  await sequelizeDb.query(
    `
      CREATE VIEW vw_query_history AS
        WITH statement_summary AS (
          SELECT
            batch_id,
            SUM(row_count) AS row_count,
            MAX(CONVERT(INT, incomplete)) AS incomplete
          FROM
            statements
          GROUP BY
            batch_id
        )
        SELECT
          b.id,
          b.query_id,
          b.name AS query_name,
          b.connection_id,
          c.name AS connection_name,
          b.status,
          b.start_time,
          b.stop_time,
          b.duration_ms,
          b.selected_text AS query_text,
          b.user_id,
          u.email AS user_email,
          ss.row_count,
          ss.incomplete
        FROM
          batches b
          LEFT JOIN users u ON b.user_id = u.id
          LEFT JOIN connections c ON b.connection_id = c.id
          LEFT JOIN statement_summary ss ON b.id = ss.batch_id
    `,
    {
      type: Sequelize.QueryTypes.RAW,
    }
  );

  await queryInterface.dropTable('query_history');
}

module.exports = {
  up,
};
<|endoftext|>


<commit_before>import EmberError from "ember-metal/error";

function visit(vertex, fn, visited, path) {
  var name = vertex.name;
  var vertices = vertex.incoming;
  var names = vertex.incomingNames;
  var len = names.length;
  var i;

  if (!visited) {
    visited = {};
  }
  if (!path) {
    path = [];
  }
  if (visited.hasOwnProperty(name)) {
    return;
  }
  path.push(name);
  visited[name] = true;
  for (i = 0; i < len; i++) {
    visit(vertices[names[i]], fn, visited, path);
  }
  fn(vertex, path);
  path.pop();
}

function DAG() {
  this.names = [];
  this.vertices = {};
}

DAG.prototype.add = function(name) {
  if (!name) { return; }
  if (this.vertices.hasOwnProperty(name)) {
    return this.vertices[name];
  }
  var vertex = {
    name: name, incoming: {}, incomingNames: [], hasOutgoing: false, value: null
  };
  this.vertices[name] = vertex;
  this.names.push(name);
  return vertex;
};

DAG.prototype.map = function(name, value) {
  this.add(name).value = value;
};

DAG.prototype.addEdge = function(fromName, toName) {
  if (!fromName || !toName || fromName === toName) {
    return;
  }
  var from = this.add(fromName);
  var to = this.add(toName);
  if (to.incoming.hasOwnProperty(fromName)) {
    return;
  }
  function checkCycle(vertex, path) {
    if (vertex.name === toName) {
      throw new EmberError("cycle detected: " + toName + " <- " + path.join(" <- "));
    }
  }
  visit(from, checkCycle);
  from.hasOutgoing = true;
  to.incoming[fromName] = from;
  to.incomingNames.push(fromName);
};

DAG.prototype.topsort = function(fn) {
  var visited = {};
  var vertices = this.vertices;
  var names = this.names;
  var len = names.length;
  var i, vertex;

  for (i = 0; i < len; i++) {
    vertex = vertices[names[i]];
    if (!vertex.hasOutgoing) {
      visit(vertex, fn, visited);
    }
  }
};

DAG.prototype.addEdges = function(name, value, before, after) {
  var i;
  this.map(name, value);
  if (before) {
    if (typeof before === 'string') {
      this.addEdge(name, before);
    } else {
      for (i = 0; i < before.length; i++) {
        this.addEdge(name, before[i]);
      }
    }
  }
  if (after) {
    if (typeof after === 'string') {
      this.addEdge(after, name);
    } else {
      for (i = 0; i < after.length; i++) {
        this.addEdge(after[i], name);
      }
    }
  }
};

export default DAG;
<commit_msg>[DOC release] Document Ember.DAG<commit_after>import EmberError from "ember-metal/error";

function visit(vertex, fn, visited, path) {
  var name = vertex.name;
  var vertices = vertex.incoming;
  var names = vertex.incomingNames;
  var len = names.length;
  var i;

  if (!visited) {
    visited = {};
  }
  if (!path) {
    path = [];
  }
  if (visited.hasOwnProperty(name)) {
    return;
  }
  path.push(name);
  visited[name] = true;
  for (i = 0; i < len; i++) {
    visit(vertices[names[i]], fn, visited, path);
  }
  fn(vertex, path);
  path.pop();
}

/**
 * DAG stands for Directed acyclic graph.
 *
 * It is used to build a graph of dependencies checking that there isn't circular
 * dependencies. p.e Registering initializers with a certain precedence order.
 *
 * @class DAG
 * @constructor
 */
function DAG() {
  this.names = [];
  this.vertices = {};
}

/**
 * Adds a vertex entry to the graph unless it is already added.
 *
 * @private
 * @method add
 * @param {String} name The name of the vertex to add
 */
DAG.prototype.add = function(name) {
  if (!name) { return; }
  if (this.vertices.hasOwnProperty(name)) {
    return this.vertices[name];
  }
  var vertex = {
    name: name, incoming: {}, incomingNames: [], hasOutgoing: false, value: null
  };
  this.vertices[name] = vertex;
  this.names.push(name);
  return vertex;
};

/**
 * Adds a vertex to the graph and sets its value.
 *
 * @private
 * @method map
 * @param {String} name The name of the vertex.
 * @param         value The value to put in the vertex.
 */
DAG.prototype.map = function(name, value) {
  this.add(name).value = value;
};

/**
 * Connects the vertices with the given names, adding them to the graph if
 * necesary, only if this does not produce is any circular dependency.
 *
 * @private
 * @method addEdge
 * @param {String} fromName The name the vertex where the edge starts.
 * @param {String} toName The name the vertex where the edge ends.
 */
DAG.prototype.addEdge = function(fromName, toName) {
  if (!fromName || !toName || fromName === toName) {
    return;
  }
  var from = this.add(fromName);
  var to = this.add(toName);
  if (to.incoming.hasOwnProperty(fromName)) {
    return;
  }
  function checkCycle(vertex, path) {
    if (vertex.name === toName) {
      throw new EmberError("cycle detected: " + toName + " <- " + path.join(" <- "));
    }
  }
  visit(from, checkCycle);
  from.hasOutgoing = true;
  to.incoming[fromName] = from;
  to.incomingNames.push(fromName);
};

/**
 * Visits all the vertex of the graph calling the given function with each one,
 * ensuring that the vertices are visited respecting their precedence.
 *
 * @method  topsort
 * @param {Function} fn The function to be invoked on each vertex.
 */
DAG.prototype.topsort = function(fn) {
  var visited = {};
  var vertices = this.vertices;
  var names = this.names;
  var len = names.length;
  var i, vertex;

  for (i = 0; i < len; i++) {
    vertex = vertices[names[i]];
    if (!vertex.hasOutgoing) {
      visit(vertex, fn, visited);
    }
  }
};

/**
 * Adds a vertex with the given name and value to the graph and joins it with the
 * vertices referenced in _before_ and _after_. If there isn't vertices with those
 * names, they are added too.
 *
 * If either _before_ or _after_ are falsy/empty, the added vertex will not have
 * an incoming/outgoing edge.
 *
 * @method addEdges
 * @param {String} name The name of the vertex to be added.
 * @param         value The value of that vertex.
 * @param        before An string or array of strings with the names of the vertices before
 *                      which this vertex must be visited.
 * @param         after An string or array of strings with the names of the vertex after
 *                      which this vertex must be visited.
 *
 */
DAG.prototype.addEdges = function(name, value, before, after) {
  var i;
  this.map(name, value);
  if (before) {
    if (typeof before === 'string') {
      this.addEdge(name, before);
    } else {
      for (i = 0; i < before.length; i++) {
        this.addEdge(name, before[i]);
      }
    }
  }
  if (after) {
    if (typeof after === 'string') {
      this.addEdge(after, name);
    } else {
      for (i = 0; i < after.length; i++) {
        this.addEdge(after[i], name);
      }
    }
  }
};

export default DAG;
<|endoftext|>


<commit_before>module.exports = function (grunt) {

    grunt.loadNpmTasks("grunt-browserify");
    grunt.loadNpmTasks("grunt-contrib-watch");

    grunt.initConfig({
        browserify: {
            dist: {
                files: {
                    "./App/Build/bundle.js": ["./App/**/*.es6.js"]
                },
                options: {
                    transform: [
                       ["babelify", {
                           loose: "all"
                       }]
                    ]
                }
            }
        },
        watch: {
            scripts: {
                files: ["Gruntfile.js", "./App/**/*.es6.js"],
                tasks: ["browserify"]
            }
        }
    });

    grunt.registerTask("default", ["build", "watch"]);
    grunt.registerTask("build", ["browserify"]);
};<commit_msg>Add bindings for vs extension. if you open projcet, it should auto run watch command<commit_after>/// <binding ProjectOpened='watch' />
module.exports = function (grunt) {

    grunt.loadNpmTasks("grunt-browserify");
    grunt.loadNpmTasks("grunt-contrib-watch");

    grunt.initConfig({
        browserify: {
            dist: {
                files: {
                    "./App/Build/bundle.js": ["./App/**/*.es6.js"]
                },
                options: {
                    transform: [
                       ["babelify", {
                           loose: "all"
                       }]
                    ]
                }
            }
        },
        watch: {
            scripts: {
                files: ["Gruntfile.js", "./App/**/*.es6.js"],
                tasks: ["browserify"]
            }
        }
    });

    grunt.registerTask("default", ["build", "watch"]);
    grunt.registerTask("build", ["browserify"]);
};<|endoftext|>


<commit_before>var should = require('should');
var _ = require('lodash');
var mapping = require('../../../lib/simulation/openfisca/mapping');

describe('Ressources', function () {

    describe('# Interruption / Prolongation', function () {

        var situation = {
            dateDeValeur: new Date('2015-03-01'),
            individus: [
                {
                    role: 'conjoint',
                    ressources: [
                        {
                            periode: '2015-02',
                            type: 'stage', // => indemnites_stage
                            montant: 200
                        },
                        {
                            periode: '2015-02',
                            type: 'revenusStageFormationPro', // => revenus_stage_formation_pro
                            montant: 300
                        }
                    ],
                    interruptedRessources: ['stage'],
                    specificSituations: [],
                }
            ]
        };

        var individu = mapping.mapIndividus(situation)[0];

        it('devrait copier les ressources encore perçues sur le mois courant', function () {
            individu.revenus_stage_formation_pro.should.have.ownProperty('2015-03');
            individu.revenus_stage_formation_pro['2015-03'].should.equal(300);
        });

        it('ne devrait pas copier les ressources dont la perception est interrompue sur le mois courant', function () {
            individu.indemnites_stage.should.not.have.ownProperty('2015-03');
        });

    });

    describe('# Estimation des revenus N-2', function () {
        // Le demandeur touche en réalité 1200 d'ARE par mois depuis 6 mois. Il déclare 7200 de revenus sur l'année glissante.
        // Mes-Aides ne connait que le montant sur l'année glissante et sur chacun des trois derniers mois. Il répartit de manière uniforme les revenus sur les mois M-12 à M-4.
        var situation = {
            dateDeValeur: new Date('2017-04-12'),
            individus: [
                {
                    role: 'demandeur',
                    ressources: [
                        {
                            periode: '2017-03',
                            type: 'allocationsChomage',
                            montant: 1200
                        },
                        {
                            periode: '2017-02',
                            type: 'allocationsChomage',
                            montant: 1200
                        },
                        {
                            periode: '2017-01',
                            type: 'allocationsChomage',
                            montant: 1200
                        },
                        {
                            periode: '2016-12',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                        {
                            periode: '2016-11',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                        {
                            periode: '2016-10',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                        {
                            periode: '2016-09',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                        {
                            periode: '2016-08',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                        {
                            periode: '2016-07',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                        {
                            periode: '2016-06',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                        {
                            periode: '2016-05',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                        {
                            periode: '2016-04',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                    ],
                    specificSituations: [],
                    interruptedRessources: [],
                }
            ]
        };

        var situationWithYearMoins2Captured = _.assign({}, situation, {ressourcesYearMoins2Captured: true});

        it('devrait supposer que les revenus N-2 sont égaux aux revenus de l’année glissante si les revenus N-2 n’ont pas été renseignés', function () {
            var individu = mapping.mapIndividus(situation)[0];
            // Les revenus estimés pour N-2 sont étalés sur 12 mois
            var expectedMappingResult = {
                '2017-04': 1200,  // prolongation
                '2017-03': 1200,
                '2017-02': 1200,
                '2017-01': 1200,
                '2016-12': 400,
                '2016-11': 400,
                '2016-10': 400,
                '2016-09': 400,
                '2016-08': 400,
                '2016-07': 400,
                '2016-06': 400,
                '2016-05': 400,
                '2016-04': 400,
                '2015-12': 600,
                '2015-11': 600,
                '2015-10': 600,
                '2015-09': 600,
                '2015-08': 600,
                '2015-07': 600,
                '2015-06': 600,
                '2015-05': 600,
                '2015-04': 600,
                '2015-03': 600,
                '2015-02': 600,
                '2015-01': 600,
            }
            should.deepEqual(individu.chomage_net, expectedMappingResult);
        });

        it('ne devrait pas supposer que les revenus N-2 sont égaux aux revenus de l’année glissante si les revenus N-2 ont été renseignés', function () {
            var individu = mapping.mapIndividus(situationWithYearMoins2Captured)[0];
            // Les revenus estimés pour N-2 sont étalés sur 12 mois
            var expectedMappingResult = {
                '2017-04': 1200,  // prolongation
                '2017-03': 1200,
                '2017-02': 1200,
                '2017-01': 1200,
                '2016-12': 400,
                '2016-11': 400,
                '2016-10': 400,
                '2016-09': 400,
                '2016-08': 400,
                '2016-07': 400,
                '2016-06': 400,
                '2016-05': 400,
                '2016-04': 400,
            }
            should.deepEqual(individu.chomage_net, expectedMappingResult);
        });
    });

    describe('# Mapping foyer fiscal', function() {
        it('devrait sommer et inverser les pensions alimentaires versées du demandeur et du conjoint', function() {
            var situation = {
                dateDeValeur: new Date('2015-03-01'),
                individus: [
                    {
                        role: 'demandeur',
                        ressources: [
                            {
                                periode: '2013',
                                type: 'rncPensionsAlimentairesVersees',
                                montant: 3000
                            }
                        ],
                        specificSituations: [],
                    },
                    {
                        role: 'conjoint',
                        ressources: [
                            {
                                periode: '2013',
                                type: 'rncPensionsAlimentairesVersees',
                                montant: 3000
                            }
                        ],
                        specificSituations: [],
                    }
                ],
                ressourcesYearMoins2Captured: true
            };

            var foyerFiscal = mapping.mapFoyerFiscal(situation);

            foyerFiscal.pensions_alimentaires_versees.should.have.ownProperty('2013');
            foyerFiscal.pensions_alimentaires_versees['2013'].should.equal(-6000);
        });
    });

});
<commit_msg>fixup! Test YM2 estimation<commit_after>var should = require('should');
var _ = require('lodash');
var mapping = require('../../../lib/simulation/openfisca/mapping');

describe('Ressources', function () {

    describe('# Interruption / Prolongation', function () {

        var situation = {
            dateDeValeur: new Date('2015-03-01'),
            individus: [
                {
                    role: 'conjoint',
                    ressources: [
                        {
                            periode: '2015-02',
                            type: 'stage', // => indemnites_stage
                            montant: 200
                        },
                        {
                            periode: '2015-02',
                            type: 'revenusStageFormationPro', // => revenus_stage_formation_pro
                            montant: 300
                        }
                    ],
                    interruptedRessources: ['stage'],
                    specificSituations: [],
                }
            ]
        };

        var individu = mapping.mapIndividus(situation)[0];

        it('devrait copier les ressources encore perçues sur le mois courant', function () {
            individu.revenus_stage_formation_pro.should.have.ownProperty('2015-03');
            individu.revenus_stage_formation_pro['2015-03'].should.equal(300);
        });

        it('ne devrait pas copier les ressources dont la perception est interrompue sur le mois courant', function () {
            individu.indemnites_stage.should.not.have.ownProperty('2015-03');
        });

    });

    describe('# Estimation des revenus N-2', function () {
        // Le demandeur touche en réalité 1200 d'ARE par mois depuis 6 mois. Il déclare 7200 de revenus sur l'année glissante.
        // Mes-Aides ne connait que le montant sur l'année glissante et sur chacun des trois derniers mois. Il répartit de manière uniforme les revenus sur les mois M-12 à M-4.
        var situation = {
            dateDeValeur: new Date('2017-04-12'),
            individus: [
                {
                    role: 'demandeur',
                    ressources: [
                        {
                            periode: '2017-03',
                            type: 'allocationsChomage',
                            montant: 1200
                        },
                        {
                            periode: '2017-02',
                            type: 'allocationsChomage',
                            montant: 1200
                        },
                        {
                            periode: '2017-01',
                            type: 'allocationsChomage',
                            montant: 1200
                        },
                        {
                            periode: '2016-12',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                        {
                            periode: '2016-11',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                        {
                            periode: '2016-10',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                        {
                            periode: '2016-09',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                        {
                            periode: '2016-08',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                        {
                            periode: '2016-07',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                        {
                            periode: '2016-06',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                        {
                            periode: '2016-05',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                        {
                            periode: '2016-04',
                            type: 'allocationsChomage',
                            montant: 400
                        },
                    ],
                    specificSituations: [],
                    interruptedRessources: [],
                }
            ]
        };

        var situationWithYearMoins2Captured = _.assign({}, situation, {ressourcesYearMoins2Captured: true});

        it('devrait supposer que les revenus N-2 sont égaux aux revenus de l’année glissante si les revenus N-2 n’ont pas été renseignés', function () {
            var individu = mapping.mapIndividus(situation)[0];
            // Les revenus estimés pour N-2 sont étalés sur 12 mois
            var expectedMappingResult = {
                '2017-04': 1200,  // prolongation
                '2017-03': 1200,
                '2017-02': 1200,
                '2017-01': 1200,
                '2016-12': 400,
                '2016-11': 400,
                '2016-10': 400,
                '2016-09': 400,
                '2016-08': 400,
                '2016-07': 400,
                '2016-06': 400,
                '2016-05': 400,
                '2016-04': 400,
                '2015-12': 600,
                '2015-11': 600,
                '2015-10': 600,
                '2015-09': 600,
                '2015-08': 600,
                '2015-07': 600,
                '2015-06': 600,
                '2015-05': 600,
                '2015-04': 600,
                '2015-03': 600,
                '2015-02': 600,
                '2015-01': 600,
            }
            should.deepEqual(individu.chomage_net, expectedMappingResult);
        });

        it('ne devrait pas supposer que les revenus N-2 sont égaux aux revenus de l’année glissante si les revenus N-2 ont été renseignés', function () {
            var individu = mapping.mapIndividus(situationWithYearMoins2Captured)[0];
            var expectedMappingResult = {
                '2017-04': 1200,  // prolongation
                '2017-03': 1200,
                '2017-02': 1200,
                '2017-01': 1200,
                '2016-12': 400,
                '2016-11': 400,
                '2016-10': 400,
                '2016-09': 400,
                '2016-08': 400,
                '2016-07': 400,
                '2016-06': 400,
                '2016-05': 400,
                '2016-04': 400,
            }
            should.deepEqual(individu.chomage_net, expectedMappingResult);
        });
    });

    describe('# Mapping foyer fiscal', function() {
        it('devrait sommer et inverser les pensions alimentaires versées du demandeur et du conjoint', function() {
            var situation = {
                dateDeValeur: new Date('2015-03-01'),
                individus: [
                    {
                        role: 'demandeur',
                        ressources: [
                            {
                                periode: '2013',
                                type: 'rncPensionsAlimentairesVersees',
                                montant: 3000
                            }
                        ],
                        specificSituations: [],
                    },
                    {
                        role: 'conjoint',
                        ressources: [
                            {
                                periode: '2013',
                                type: 'rncPensionsAlimentairesVersees',
                                montant: 3000
                            }
                        ],
                        specificSituations: [],
                    }
                ],
                ressourcesYearMoins2Captured: true
            };

            var foyerFiscal = mapping.mapFoyerFiscal(situation);

            foyerFiscal.pensions_alimentaires_versees.should.have.ownProperty('2013');
            foyerFiscal.pensions_alimentaires_versees['2013'].should.equal(-6000);
        });
    });

});
<|endoftext|>