refactor: transform matches data to store in db only useful data

This commit is contained in:
Valentin Kaelin 2019-09-29 18:06:54 +02:00
parent 68d15872ce
commit 4f13d8400e
11 changed files with 270 additions and 150 deletions

View file

@ -1,4 +1,4 @@
import { timeDifference, secToTime, getRankImg } from '@/helpers/functions.js' import { timeDifference, getRankImg } from '@/helpers/functions.js'
import { maps, gameModes } from '@/data/data.js' import { maps, gameModes } from '@/data/data.js'
import summonersJSON from '@/data/summoner.json' import summonersJSON from '@/data/summoner.json'
@ -7,7 +7,7 @@ import summonersJSON from '@/data/summoner.json'
* @param {Object} RiotData : all data from the Riot API * @param {Object} RiotData : all data from the Riot API
* @param {Object} championsInfos : champions data from the Riot API * @param {Object} championsInfos : champions data from the Riot API
*/ */
export function createSummonerData(RiotData, championsInfos, runesInfos) { export function createSummonerData(RiotData) {
console.log('--- ALL INFOS ---') console.log('--- ALL INFOS ---')
console.log(RiotData) console.log(RiotData)
@ -25,129 +25,25 @@ export function createSummonerData(RiotData, championsInfos, runesInfos) {
soloQ.lp = soloQStats.leaguePoints soloQ.lp = soloQStats.leaguePoints
} }
const matchesInfos = [] for (const match of matches) {
// Loop on all matches for (let i = 0; i < match.items.length; i++) {
for (let i = 0; i < matches.length; i++) { match.items[i] = getItemLink(match.items[i])
const currentMatch = matches[i]
const participantId = currentMatch.participantIdentities.find((p) => p.player.currentAccountId === userStats.accountId).participantId
const player = currentMatch.participants[participantId - 1]
const teamId = player.teamId
let win = currentMatch.teams.find((t) => t.teamId === teamId).win
let status = win === 'Win' ? 'Victory' : 'Defeat'
// Match less than 5min
if (currentMatch.gameDuration < 300) {
win = 'Remake'
status = 'Remake'
} }
const map = maps[currentMatch.mapId] match.firstSum = getSummonerLink(match.firstSum)
let mode = gameModes[currentMatch.queueId] match.secondSum = getSummonerLink(match.secondSum)
if (!mode)
mode = 'Undefined gamemode'
const champion = (({ id, name }) => ({ id, name }))(Object.entries(championsInfos).find(([, champion]) => Number(champion.key) === player.championId)[1])
const role = getRoleName(player.timeline)
const timeAgo = timeDifference(currentMatch.gameCreation) match.date = timeDifference(match.date)
const time = secToTime(currentMatch.gameDuration)
const kills = player.stats.kills
const deaths = player.stats.deaths
const assists = player.stats.assists
let kda
if (kills + assists !== 0 && deaths === 0) {
kda = '∞'
} else {
kda = +(deaths === 0 ? 0 : ((kills + assists) / deaths)).toFixed(2)
}
const level = player.stats.champLevel
const damage = +(player.stats.totalDamageDealtToChampions / 1000).toFixed(1) + 'k'
const primaryRuneCategory = runesInfos.find(r => r.id === player.stats.perkPrimaryStyle) match.map = maps[match.map]
let primaryRune match.gamemode = gameModes[match.gamemode]
for (const subCat of primaryRuneCategory.slots) { if (!match.gamemode) match.gamemode = 'Undefined gamemode'
primaryRune = subCat.runes.find(r => r.id === player.stats.perk0)
if (primaryRune) {
break
}
}
primaryRune = `https://ddragon.leagueoflegends.com/cdn/img/${primaryRune.icon}`
let secondaryRune = runesInfos.find(r => r.id === player.stats.perkSubStyle)
secondaryRune = `https://ddragon.leagueoflegends.com/cdn/img/${secondaryRune.icon}`
const totalKills = currentMatch.participants.reduce((prev, current) => {
if (current.teamId !== teamId) {
return prev
}
return prev + current.stats.kills
}, 0)
const kp = +((kills + assists) * 100 / totalKills).toFixed(1) + '%'
const items = []
for (let i = 0; i < 6; i++) {
const currentItem = 'item' + i
items.push(getItemLink(player.stats[currentItem]))
}
const gold = +(player.stats.goldEarned / 1000).toFixed(1) + 'k'
const minions = player.stats.totalMinionsKilled + player.stats.neutralMinionsKilled
const firstSum = player.spell1Id
const secondSum = player.spell2Id
const allyTeam = []
const enemyTeam = []
for (let summoner of currentMatch.participantIdentities) {
const allData = currentMatch.participants[summoner.participantId - 1]
const playerInfos = {
name: summoner.player.summonerName,
role: getRoleName(allData.timeline),
champion: (({ id, name }) => ({ id, name }))(Object.entries(championsInfos).find(([, champion]) => Number(champion.key) === allData.championId)[1])
}
if (allData.teamId === teamId) {
allyTeam.push(playerInfos)
} else {
enemyTeam.push(playerInfos)
}
}
allyTeam.sort(sortTeamByRole)
enemyTeam.sort(sortTeamByRole)
matchesInfos.push({
result: win,
status,
map,
gamemode: mode,
champion,
role,
primaryRune,
secondaryRune,
date: timeAgo,
time,
kills,
deaths,
assists,
kda,
level,
damage,
kp,
items,
gold,
minions,
firstSum: getSummonerLink(firstSum),
secondSum: getSummonerLink(secondSum),
allyTeam,
enemyTeam
})
} // end loop matches } // end loop matches
console.log('matches infos just below')
console.log(matchesInfos)
return { return {
accountId: userStats.accountId, accountId: userStats.accountId,
allMatches: RiotData.allMatches, allMatches: RiotData.allMatches,
matches: matchesInfos, matches: RiotData.matchesDetails,
profileIconId: userStats.profileIconId, profileIconId: userStats.profileIconId,
name: userStats.name, name: userStats.name,
level: userStats.summonerLevel, level: userStats.summonerLevel,
@ -162,19 +58,7 @@ function getItemLink(id) {
return `url('https://ddragon.leagueoflegends.com/cdn/${process.env.VUE_APP_PATCH}/img/item/${id}.png')` return `url('https://ddragon.leagueoflegends.com/cdn/${process.env.VUE_APP_PATCH}/img/item/${id}.png')`
} }
function getRoleName(timeline) {
if (timeline.lane === 'BOTTOM' && timeline.role.includes('SUPPORT')) {
return 'SUPPORT'
}
return timeline.lane
}
function getSummonerLink(id) { function getSummonerLink(id) {
const spellName = Object.entries(summonersJSON.data).find(([, spell]) => Number(spell.key) === id)[0] const spellName = Object.entries(summonersJSON.data).find(([, spell]) => Number(spell.key) === id)[0]
return `https://ddragon.leagueoflegends.com/cdn/${process.env.VUE_APP_PATCH}/img/spell/${spellName}.png` return `https://ddragon.leagueoflegends.com/cdn/${process.env.VUE_APP_PATCH}/img/spell/${spellName}.png`
} }
function sortTeamByRole(a, b) {
const sortingArr = ['TOP', 'JUNGLE', 'MIDDLE', 'BOTTOM', 'SUPPORT']
return sortingArr.indexOf(a.role) - sortingArr.indexOf(b.role)
}

View file

@ -28,7 +28,7 @@ export const actions = {
try { try {
const resp = await axios(({ url: 'api', data: { summoner, region }, method: 'POST' })) const resp = await axios(({ url: 'api', data: { summoner, region }, method: 'POST' }))
if (resp.data) { if (resp.data) {
const infos = createSummonerData(resp.data, rootState.ddragon.champions, rootState.ddragon.runes) const infos = createSummonerData(resp.data)
commit('SUMMONER_FOUND', infos) commit('SUMMONER_FOUND', infos)
} else { } else {
commit('SUMMONER_NOT_FOUND') commit('SUMMONER_NOT_FOUND')

View file

@ -132,7 +132,6 @@ export default {
...mapState({ ...mapState({
summonerInfos: state => state.summoner.infos summonerInfos: state => state.summoner.infos
}), }),
...mapGetters('ddragon', ['areChampionsLoaded']),
...mapGetters('summoner', ['summonerFound', 'summonerNotFound', 'summonerLoading']) ...mapGetters('summoner', ['summonerFound', 'summonerNotFound', 'summonerLoading'])
}, },
@ -148,18 +147,12 @@ export default {
}, },
methods: { methods: {
async apiCall() { apiCall() {
if (!this.areChampionsLoaded)
await this.getChampions()
await this.getRunes()
this.summonerRequest({ summoner: this.summoner, region: this.region }) this.summonerRequest({ summoner: this.summoner, region: this.region })
}, },
redirect(summoner, region) { redirect(summoner, region) {
this.$router.push(`/summoner/${region}/${summoner}`) this.$router.push(`/summoner/${region}/${summoner}`)
}, },
...mapActions('ddragon', ['getChampions', 'getRunes']),
...mapActions('summoner', ['summonerRequest']) ...mapActions('summoner', ['summonerRequest'])
} }
} }

View file

@ -2,26 +2,51 @@
const Match = use('App/Models/Match') const Match = use('App/Models/Match')
const Jax = use('Jax') const Jax = use('Jax')
const MatchTransformer = use('App/Transformers/MatchTransformer')
class SummonerController { class SummonerController {
/** /**
* POST Endpoint : get summoner data * POST Endpoint : get summoner data
*/ */
async api({ request, response }) { async api({ request, response, transform }) {
const summoner = request.input('summoner') const summoner = request.input('summoner')
const region = request.input('region') const region = request.input('region')
const data = await this.getSummonerData(summoner, region) const data = await this.getSummonerData(summoner, region, transform)
response.json(data) response.json(data)
} }
/**
* Get the matchlist of all matches made less than 4 months ago by the Summoner
* @param today
* @param accountId
* @param beginIndex
* @param allMatches
*/
async getMatchList(today, accountId, beginIndex, allMatches) {
const { matches } = await Jax.Matchlist.accountID(accountId, beginIndex)
allMatches = [...allMatches, ...matches]
const lastMatch = matches[matches.length - 1].timestamp
const diff = today - lastMatch
console.log(diff)
// More matches to get from Riot API if they are younger than 4 months
if (matches.length === 100 && diff < 10368000000) {
return this.getMatchList(today, accountId, (beginIndex + 100), allMatches)
} else {
console.log('return all matches bro')
return allMatches
}
}
/** /**
* Get all the data about the searched Summoner * Get all the data about the searched Summoner
* @param summoner * @param summoner
* @param region * @param region
*/ */
async getSummonerData(summoner, region) { async getSummonerData(summoner, region, transform) {
console.time('all') console.time('all')
console.log(summoner, region) console.log(summoner, region)
@ -47,13 +72,15 @@ class SummonerController {
finalJSON.soloQ = soloQ.length ? soloQ[0] : null; finalJSON.soloQ = soloQ.length ? soloQ[0] : null;
console.time('getMatches') console.time('getMatches')
const { matches } = await Jax.Matchlist.accountID(account.accountId) const today = Date.now()
const matches = await this.getMatchList(today, account.accountId, 0, [])
const gameIds = matches.slice(0, 10).map(({ gameId }) => gameId) const gameIds = matches.slice(0, 10).map(({ gameId }) => gameId)
let matchesDetails = [] let matchesDetails = []
const matchesToGetFromRiot = [] const matchesToGetFromRiot = []
for (let i = 0; i < gameIds.length; ++i) { for (let i = 0; i < gameIds.length; ++i) {
const matchSaved = await Match.where({ gameId: gameIds[i] }).first() // const matchSaved = await Match.where({ gameId: gameIds[i] }).first()
const matchSaved = await Match.where({ gameId: gameIds[i], puuid: account.puuid }).first()
if (matchSaved) { if (matchSaved) {
console.log('match in mongodb') console.log('match in mongodb')
matchesDetails.push(matchSaved) matchesDetails.push(matchSaved)
@ -64,7 +91,22 @@ class SummonerController {
} }
const requests = matchesToGetFromRiot.map(Jax.Match.get) const requests = matchesToGetFromRiot.map(Jax.Match.get)
const matchesFromApi = await Promise.all(requests) let matchesFromApi = await Promise.all(requests)
/* If we have to same some matches in the db */
if (matchesFromApi.length !== 0) {
const champions = await Jax.DDragon.Champion.list()
const runes = await Jax.DDragon.Rune.list()
const ctx = {
account,
champions: champions.data,
runes
}
matchesFromApi = await transform.collection(matchesFromApi)
.transformWith(MatchTransformer)
.withContext(ctx)
.toJSON()
matchesDetails = [...matchesDetails, ...matchesFromApi] matchesDetails = [...matchesDetails, ...matchesFromApi]
/* Save all matches in db */ /* Save all matches in db */
@ -72,9 +114,10 @@ class SummonerController {
await Match.create(match) await Match.create(match)
console.log('match saved') console.log('match saved')
} }
}
/* Sort 10 matches */ /* Sort 10 matches */
matchesDetails.sort((a, b) => (a.gameCreation < b.gameCreation) ? 1 : -1) matchesDetails.sort((a, b) => (a.gameCreation < b.gameCreation) ? -1 : 1)
finalJSON.matchesDetails = matchesDetails finalJSON.matchesDetails = matchesDetails
finalJSON.allMatches = matches finalJSON.allMatches = matches

View file

@ -0,0 +1,25 @@
class MatchHelper {
getRoleName(timeline) {
if (timeline.lane === 'BOTTOM' && timeline.role.includes('SUPPORT')) {
return 'SUPPORT'
}
return timeline.lane
}
/**
* Return time in a formatted way
* @param sec : time in seconds to convert
*/
secToTime(sec) {
const min = Math.floor(sec / 60)
const newSec = sec - min * 60
return min + 'm' + (newSec < 10 ? '0' + newSec : newSec) + 's'
}
sortTeamByRole(a, b) {
const sortingArr = ['TOP', 'JUNGLE', 'MIDDLE', 'BOTTOM', 'SUPPORT']
return sortingArr.indexOf(a.role) - sortingArr.indexOf(b.role)
}
}
module.exports = new MatchHelper()

View file

@ -0,0 +1,138 @@
'use strict'
const BumblebeeTransformer = use('Bumblebee/Transformer')
const MatchHelper = use('App/Helpers/Match')
const Logger = use('Logger')
/**
* MatchTransformer class
*
* @class MatchTransformer
* @constructor
*/
class MatchTransformer extends BumblebeeTransformer {
/**
* This method is used to transform the data.
*/
transform(match, { account, champions, runes }) {
// console.log(champions)
// Logger.transport('file').info(champions)
const participantId = match.participantIdentities.find((p) => p.player.currentAccountId === account.accountId).participantId
const player = match.participants[participantId - 1]
const teamId = player.teamId
let win = match.teams.find((t) => t.teamId === teamId).win
let status = win === 'Win' ? 'Victory' : 'Defeat'
// Match less than 5min
if (match.gameDuration < 300) {
win = 'Remake'
status = 'Remake'
}
const map = match.mapId
const mode = match.queueId
const champion = (({ id, name }) => ({ id, name }))(Object.entries(champions).find(([, champion]) => Number(champion.key) === player.championId)[1])
const role = MatchHelper.getRoleName(player.timeline)
const gameCreation = match.gameCreation
const time = MatchHelper.secToTime(match.gameDuration)
const kills = player.stats.kills
const deaths = player.stats.deaths
const assists = player.stats.assists
let kda
if (kills + assists !== 0 && deaths === 0) {
kda = '∞'
} else {
kda = +(deaths === 0 ? 0 : ((kills + assists) / deaths)).toFixed(2)
}
const level = player.stats.champLevel
const damage = +(player.stats.totalDamageDealtToChampions / 1000).toFixed(1) + 'k'
const totalKills = match.participants.reduce((prev, current) => {
if (current.teamId !== teamId) {
return prev
}
return prev + current.stats.kills
}, 0)
const kp = +((kills + assists) * 100 / totalKills).toFixed(1) + '%'
const primaryRuneCategory = runes.find(r => r.id === player.stats.perkPrimaryStyle)
let primaryRune
for (const subCat of primaryRuneCategory.slots) {
primaryRune = subCat.runes.find(r => r.id === player.stats.perk0)
if (primaryRune) {
break
}
}
primaryRune = `https://ddragon.leagueoflegends.com/cdn/img/${primaryRune.icon}`
let secondaryRune = runes.find(r => r.id === player.stats.perkSubStyle)
secondaryRune = `https://ddragon.leagueoflegends.com/cdn/img/${secondaryRune.icon}`
const items = []
for (let i = 0; i < 6; i++) {
const currentItem = 'item' + i
items.push(player.stats[currentItem])
}
const gold = +(player.stats.goldEarned / 1000).toFixed(1) + 'k'
const minions = player.stats.totalMinionsKilled + player.stats.neutralMinionsKilled
const firstSum = player.spell1Id
const secondSum = player.spell2Id
const allyTeam = []
const enemyTeam = []
for (let summoner of match.participantIdentities) {
const allData = match.participants[summoner.participantId - 1]
const playerInfos = {
name: summoner.player.summonerName,
role: MatchHelper.getRoleName(allData.timeline),
champion: (({ id, name }) => ({ id, name }))(Object.entries(champions).find(([, champion]) => Number(champion.key) === allData.championId)[1])
}
if (allData.teamId === teamId) {
allyTeam.push(playerInfos)
} else {
enemyTeam.push(playerInfos)
}
}
allyTeam.sort(MatchHelper.sortTeamByRole)
enemyTeam.sort(MatchHelper.sortTeamByRole)
return {
puuid: account.puuid,
gameId: match.gameId,
result: win,
status,
map,
gamemode: mode,
champion,
role,
primaryRune,
secondaryRune,
date: gameCreation,
time,
kills,
deaths,
assists,
kda,
level,
damage,
kp,
items,
gold,
minions,
firstSum,
secondSum,
allyTeam,
enemyTeam
}
}
}
module.exports = MatchTransformer

View file

@ -0,0 +1,28 @@
'use strict'
module.exports = {
/*
* When enabled, Bulblebee will automatically parse the ?include=
* parameter and include all requested resources
*/
parseRequest: false,
/*
* Nested includes will be resolved up to this limit any further nested
* resources are going to be ignored
*/
includeRecursionLimit: 10,
/*
* The serializer will be used to transform the data into its final
* representation.
* Currently supported: 'plain', 'data'
*/
serializer: 'plain',
/*
* When a transformer is reffered to by its name only, Bumblebee will try to
* resolve the transformer using this namespace as prefix.
*/
namespace: 'App/Transformers'
}

View file

@ -7,6 +7,7 @@ class MatchSchema extends Schema {
up () { up () {
this.create('matches', (collection) => { this.create('matches', (collection) => {
collection.index('gameId', {gameId: 1}) collection.index('gameId', {gameId: 1})
collection.index('puuid', {puuid: 1})
}) })
} }

View file

@ -216,6 +216,11 @@
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.0.0.tgz", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.0.0.tgz",
"integrity": "sha512-7Bv1We7ZGuU79zZbb6rRqcpxo3OY+zrdtloZWoyD8fmGX+FeXRjE+iuGkZjSXLVovLzrsvMGMy0EkwA0E0umxg==" "integrity": "sha512-7Bv1We7ZGuU79zZbb6rRqcpxo3OY+zrdtloZWoyD8fmGX+FeXRjE+iuGkZjSXLVovLzrsvMGMy0EkwA0E0umxg=="
}, },
"adonis-bumblebee": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/adonis-bumblebee/-/adonis-bumblebee-2.0.0.tgz",
"integrity": "sha512-HUvzuoU3Xb4je23Pjec1SLTA8r8GWUoE6BVCl9y32IfrqKD3o1XXWwWcHs5SK1zFgb4OLjB6NDWQmvg8MKs1tg=="
},
"ajv": { "ajv": {
"version": "6.10.2", "version": "6.10.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",

View file

@ -24,6 +24,7 @@
"@adonisjs/framework": "^5.0.9", "@adonisjs/framework": "^5.0.9",
"@adonisjs/ignitor": "^2.0.8", "@adonisjs/ignitor": "^2.0.8",
"@adonisjs/lucid": "^6.1.3", "@adonisjs/lucid": "^6.1.3",
"adonis-bumblebee": "^2.0.0",
"got": "^9.6.0", "got": "^9.6.0",
"lucid-mongo": "^3.1.6", "lucid-mongo": "^3.1.6",
"mongodb-core": "^3.2.7", "mongodb-core": "^3.2.7",

View file

@ -17,6 +17,7 @@ const providers = [
'@adonisjs/bodyparser/providers/BodyParserProvider', '@adonisjs/bodyparser/providers/BodyParserProvider',
'@adonisjs/cors/providers/CorsProvider', '@adonisjs/cors/providers/CorsProvider',
'lucid-mongo/providers/LucidMongoProvider', 'lucid-mongo/providers/LucidMongoProvider',
'adonis-bumblebee/providers/BumblebeeProvider',
join(__dirname, '../providers/Jax/JaxProvider') join(__dirname, '../providers/Jax/JaxProvider')
] ]
@ -31,7 +32,8 @@ const providers = [
| |
*/ */
const aceProviders = [ const aceProviders = [
'lucid-mongo/providers/MigrationsProvider' 'lucid-mongo/providers/MigrationsProvider',
'adonis-bumblebee/providers/CommandsProvider'
] ]
/* /*