<commit_before>import config from 'src/config'
import {STAT_DESCRIPTORS} from 'src/common/models/stat'
import {connect} from 'src/db'
import {replace as replacePlayer} from 'src/server/db/player'
import {replace as replaceModerator} from 'src/server/db/moderator'
import {addUserToTeam} from 'src/server/services/gitHubService'
import {getLatestCycleForChapter} from 'src/server/db/cycle'
import {addPlayerIdsToPool, getPoolsForCycleWithPlayerCount} from 'src/server/db/pool'
import {GOAL_SELECTION} from 'src/common/models/cycle'
import {LEVELS, computePlayerLevel} from 'src/server/util/stats'

const r = connect()

const {
  ELO,
  ESTIMATION_ACCURACY,
  LEVEL,
} = STAT_DESCRIPTORS

// we want new players to start on level 1, but not act as
// if they have a higher estimation accuracy than current
// level 1 players
const newPlayerEstimationAccuracy = LEVELS[1].requirements[ESTIMATION_ACCURACY] + 0.01
const DEFAULT_PLAYER_STATS = {
  stats: {
    [ELO]: {rating: 1000},
    weightedAverages: {
      [ESTIMATION_ACCURACY]: newPlayerEstimationAccuracy,
    },
  },
}
DEFAULT_PLAYER_STATS.stats[LEVEL] = computePlayerLevel(DEFAULT_PLAYER_STATS)

const upsertToDatabase = {
  // we use .replace() instead of .insert() in case we get duplicates in the queue
  moderator: gameUser => replaceModerator(gameUser, {returnChanges: 'always'}),
  player: gameUser => replacePlayer({...gameUser, ...DEFAULT_PLAYER_STATS}, {returnChanges: 'always'}),
}

export function start() {
  const jobService = require('src/server/services/jobService')
  jobService.processJobs('userCreated', processUserCreated)
}

export async function processUserCreated(user) {
  const gameUser = await addUserToDatabase(user)
  await addUserToChapterGitHubTeam(user, gameUser)
  await notifyCRMSystemOfPlayerSignUp(user)

  const cycle = await getLatestCycleForChapter(gameUser.chapterId)
  if (cycle.state === GOAL_SELECTION) {
    await addNewPlayerToPool(gameUser, cycle)
  }
}

async function addNewPlayerToPool(gameUser, cycle) {
  const poolsWithCount = await getPoolsForCycleWithPlayerCount(cycle.id)
    .filter({level: gameUser.stats.level})

  poolsWithCount.sort((previousPool, currentPool) => previousPool.count - currentPool.count)
  await addPlayerIdsToPool(poolsWithCount[0].id, [gameUser.id])
}

async function addUserToDatabase(user) {
  if (!user.inviteCode) {
    throw new Error(`user with id ${user.id} has no inviteCode, unable to determine chapter assignment`)
  }
  const chapters = await r.table('chapters').getAll(user.inviteCode, {index: 'inviteCodes'}).run()
  if (chapters.length === 0) {
    throw new Error(`no chapter found for inviteCode ${user.inviteCode} on user with id ${user.id}`)
  }
  const chapter = chapters[0]
  const now = r.now()
  const gameUser = {
    id: user.id,
    chapterId: chapter.id,
    createdAt: now,
    updatedAt: now,
  }
  const gameRoles = ['player', 'moderator']
  const dbInsertPromises = []
  gameRoles.forEach(role => {
    if (_userHasRole(user, role)) {
      dbInsertPromises.push(upsertToDatabase[role](gameUser))
    }
  })
  const upsertedUsers = await Promise.all(dbInsertPromises)
    .then(results => results.map(result => result.changes[0].new_val))
    .catch(err => {
      throw new Error(`Unable to insert game user(s): ${err}`)
    })
  return upsertedUsers[0]
}

async function addUserToChapterGitHubTeam(user, gameUser) {<commit_msg>add in baseline stats for new users<commit_after>import config from 'src/config'
import {STAT_DESCRIPTORS, PRO_PLAYER_STATS_BASELINE} from 'src/common/models/stat'
import {connect} from 'src/db'
import {replace as replacePlayer} from 'src/server/db/player'
import {replace as replaceModerator} from 'src/server/db/moderator'
import {addUserToTeam} from 'src/server/services/gitHubService'
import {getLatestCycleForChapter} from 'src/server/db/cycle'
import {addPlayerIdsToPool, getPoolsForCycleWithPlayerCount} from 'src/server/db/pool'
import {GOAL_SELECTION} from 'src/common/models/cycle'
import {LEVELS, computePlayerLevel} from 'src/server/util/stats'

const r = connect()

const {
  ELO,
  ESTIMATION_ACCURACY,
  LEVEL,
} = STAT_DESCRIPTORS

// we want new players to start on level 1, but not act as
// if they have a higher estimation accuracy than current
// level 1 players
const newPlayerEstimationAccuracy = LEVELS[1].requirements[ESTIMATION_ACCURACY] + 0.01
const DEFAULT_PLAYER_STATS = {
  [ELO]: {rating: 1000},
  weightedAverages: {
    [ESTIMATION_ACCURACY]: newPlayerEstimationAccuracy,
  },
}
DEFAULT_PLAYER_STATS.stats[LEVEL] = computePlayerLevel(DEFAULT_PLAYER_STATS)

const upsertToDatabase = {
  // we use .replace() instead of .insert() in case we get duplicates in the queue
  moderator: gameUser => replaceModerator(gameUser, {returnChanges: 'always'}),
  player: (gameUser, idmUser) => {
    const statsBaseline = _userHasRole(idmUser, 'sep') ? PRO_PLAYER_STATS_BASELINE : {}
    return replacePlayer({
      ...gameUser,
      stats: {
        ...DEFAULT_PLAYER_STATS,
        ...statsBaseline,
      },
      statsBaseline,
    }, {returnChanges: 'always'})
  },
}

export function start() {
  const jobService = require('src/server/services/jobService')
  jobService.processJobs('userCreated', processUserCreated)
}

export async function processUserCreated(user) {
  const gameUser = await addUserToDatabase(user)
  await addUserToChapterGitHubTeam(user, gameUser)
  await notifyCRMSystemOfPlayerSignUp(user)

  const cycle = await getLatestCycleForChapter(gameUser.chapterId)
  if (cycle.state === GOAL_SELECTION) {
    await addNewPlayerToPool(gameUser, cycle)
  }
}

async function addNewPlayerToPool(gameUser, cycle) {
  const poolsWithCount = await getPoolsForCycleWithPlayerCount(cycle.id)
    .filter({level: gameUser.stats.level})

  poolsWithCount.sort((previousPool, currentPool) => previousPool.count - currentPool.count)
  await addPlayerIdsToPool(poolsWithCount[0].id, [gameUser.id])
}

async function addUserToDatabase(user) {
  if (!user.inviteCode) {
    throw new Error(`user with id ${user.id} has no inviteCode, unable to determine chapter assignment`)
  }
  const chapters = await r.table('chapters').getAll(user.inviteCode, {index: 'inviteCodes'}).run()
  if (chapters.length === 0) {
    throw new Error(`no chapter found for inviteCode ${user.inviteCode} on user with id ${user.id}`)
  }
  const chapter = chapters[0]
  const now = r.now()
  const gameUser = {
    id: user.id,
    chapterId: chapter.id,
    createdAt: now,
    updatedAt: now,
  }
  const gameRoles = ['player', 'moderator']
  const dbInsertPromises = []
  gameRoles.forEach(role => {
    if (_userHasRole(user, role)) {
      dbInsertPromises.push(upsertToDatabase[role](gameUser, user))
    }
  })
  const upsertedUsers = await Promise.all(dbInsertPromises)
    .then(results => results.map(result => result.changes[0].new_val))
    .catch(err => {
      throw new Error(`Unable to insert game user(s): ${err}`)
    })
  return upsertedUsers[0]
}

async function addUserToChapterGitHubTeam(user, gameUser) {
{'fraction_non_alphanumeric': 0.11661683041435283, 'fraction_numerical': 0.004271678769756515, 'mean_word_length': 4.8387364921030755, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5975991', 'n_tokens_mistral': 2317, 'n_tokens_neox': 2277, 'n_words': 536}