mirror of
https://github.com/vkaelin/LeagueStats.git
synced 2026-03-25 12:57:28 +00:00
feat: add ranks back in match-details
This commit is contained in:
parent
973355c201
commit
6f0beec0d2
17 changed files with 291 additions and 31 deletions
|
|
@ -5,6 +5,7 @@
|
||||||
:data="allyTeam"
|
:data="allyTeam"
|
||||||
:all-players="[...allyTeam.players, ...enemyTeam.players]"
|
:all-players="[...allyTeam.players, ...enemyTeam.players]"
|
||||||
:ally-team="true"
|
:ally-team="true"
|
||||||
|
:ranks-loaded="data.ranksLoaded"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="flex items-start justify-between px-3 py-2">
|
<div class="flex items-start justify-between px-3 py-2">
|
||||||
|
|
@ -23,6 +24,7 @@
|
||||||
:data="enemyTeam"
|
:data="enemyTeam"
|
||||||
:all-players="[...allyTeam.players, ...enemyTeam.players]"
|
:all-players="[...allyTeam.players, ...enemyTeam.players]"
|
||||||
:ally-team="false"
|
:ally-team="false"
|
||||||
|
:ranks-loaded="data.ranksLoaded"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="data.status === 'loading' && detailsOpen">
|
<div v-else-if="data.status === 'loading' && detailsOpen">
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,7 @@
|
||||||
{{ player.rank.shortName }}
|
{{ player.rank.shortName }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="player.rank === undefined">
|
<div v-else-if="!ranksLoaded">
|
||||||
<DotsLoader width="30px" dot-width="10px" />
|
<DotsLoader width="30px" dot-width="10px" />
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="w-5 h-5">
|
<div v-else class="w-5 h-5">
|
||||||
|
|
@ -350,6 +350,10 @@ export default {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
ranksLoaded: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
||||||
|
|
@ -14,16 +14,39 @@ export const mutations = {
|
||||||
state.matches.push({ matchId, status: 'loading' })
|
state.matches.push({ matchId, status: 'loading' })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
MATCH_FOUND(state, matchDetails) {
|
MATCH_FOUND(state, {matchDetails, ranksLoaded }) {
|
||||||
matchDetails.status = 'loaded'
|
matchDetails.status = 'loaded'
|
||||||
|
matchDetails.ranksLoaded = ranksLoaded
|
||||||
|
|
||||||
|
// Set SoloQ as rank for now
|
||||||
|
if(ranksLoaded) {
|
||||||
|
for (const player of matchDetails.blueTeam.players) {
|
||||||
|
player.rank = player.rank && player.rank[420]
|
||||||
|
}
|
||||||
|
for (const player of matchDetails.redTeam.players) {
|
||||||
|
player.rank = player.rank && player.rank[420]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const index = state.matches.findIndex(m => m.gameId === matchDetails.gameId)
|
const index = state.matches.findIndex(m => m.gameId === matchDetails.gameId)
|
||||||
Vue.set(state.matches, index, matchDetails)
|
Vue.set(state.matches, index, matchDetails)
|
||||||
},
|
},
|
||||||
MATCH_RANKS_FOUND(state, { gameId, blueTeam, redTeam }) {
|
MATCH_RANKS_FOUND(state, { gameId, ranksByPlayer }) {
|
||||||
const match = state.matches.find(m => m.gameId === gameId)
|
const match = state.matches.find(m => m.gameId === gameId)
|
||||||
match.blueTeam.players = blueTeam
|
|
||||||
match.redTeam.players = redTeam
|
for (const player of match.blueTeam.players) {
|
||||||
|
const ranks = ranksByPlayer[player.id]
|
||||||
|
if(!ranks) continue
|
||||||
|
Vue.set(player, 'rank', ranks[420])
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const player of match.redTeam.players) {
|
||||||
|
const ranks = ranksByPlayer[player.id]
|
||||||
|
if(!ranks) continue
|
||||||
|
Vue.set(player, 'rank', ranks[420])
|
||||||
|
}
|
||||||
|
|
||||||
|
match.ranksLoaded = true
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,17 +58,17 @@ export const actions = {
|
||||||
const resp = await axios(({ url: 'match/details', data: { matchId }, method: 'POST' })).catch(() => { })
|
const resp = await axios(({ url: 'match/details', data: { matchId }, method: 'POST' })).catch(() => { })
|
||||||
console.log('--- DETAILS INFOS ---')
|
console.log('--- DETAILS INFOS ---')
|
||||||
console.log(resp.data)
|
console.log(resp.data)
|
||||||
commit('MATCH_FOUND', resp.data.matchDetails)
|
const {matchDetails, ranksLoaded} = resp.data
|
||||||
|
commit('MATCH_FOUND', {matchDetails, ranksLoaded })
|
||||||
|
|
||||||
// TODO: add ranks back when it's done on the API
|
// If the ranks of the players are not yet known
|
||||||
// // If the ranks of the players are not yet known
|
if (!ranksLoaded) {
|
||||||
// if (resp.data.matchDetails.blueTeam.players[0].rank === undefined) {
|
const ranks = await axios(({ url: 'match/details/ranks', data: { matchId }, method: 'POST' })).catch(() => { })
|
||||||
// const ranks = await axios(({ url: 'match/details/ranks', data: { gameId, region }, method: 'POST' })).catch(() => { })
|
if (!ranks) return
|
||||||
// if (!ranks) return
|
console.log('--- RANK OF MATCH DETAILS ---')
|
||||||
// console.log('--- RANK OF MATCH DETAILS ---')
|
console.log(ranks.data)
|
||||||
// console.log(ranks.data)
|
commit('MATCH_RANKS_FOUND', { matchId, ranksByPlayer: ranks.data })
|
||||||
// commit('MATCH_RANKS_FOUND', { gameId, ...ranks.data })
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
import Match from 'App/Models/Match'
|
import Match from 'App/Models/Match'
|
||||||
|
import MatchPlayerRankParser from 'App/Parsers/MatchPlayerRankParser'
|
||||||
import DetailedMatchSerializer from 'App/Serializers/DetailedMatchSerializer'
|
import DetailedMatchSerializer from 'App/Serializers/DetailedMatchSerializer'
|
||||||
|
import MatchPlayerRankSerializer from 'App/Serializers/MatchPlayerRankSerializer'
|
||||||
import MatchService from 'App/Services/MatchService'
|
import MatchService from 'App/Services/MatchService'
|
||||||
import StatsService from 'App/Services/StatsService'
|
import StatsService from 'App/Services/StatsService'
|
||||||
import DetailedMatchValidator from 'App/Validators/DetailedMatchValidator'
|
import DetailedMatchValidator from 'App/Validators/DetailedMatchValidator'
|
||||||
|
|
@ -8,7 +10,7 @@ import MatchesIndexValidator from 'App/Validators/MatchesIndexValidator'
|
||||||
|
|
||||||
export default class MatchesController {
|
export default class MatchesController {
|
||||||
/**
|
/**
|
||||||
* POST - Return data from matches searched by gameIds
|
* POST - Return data from matches searched by matchIds
|
||||||
* @param ctx
|
* @param ctx
|
||||||
*/
|
*/
|
||||||
public async index({ request, response }: HttpContextContract) {
|
public async index({ request, response }: HttpContextContract) {
|
||||||
|
|
@ -33,15 +35,33 @@ export default class MatchesController {
|
||||||
const match = await Match.query()
|
const match = await Match.query()
|
||||||
.where('id', matchId)
|
.where('id', matchId)
|
||||||
.preload('teams')
|
.preload('teams')
|
||||||
.preload('players')
|
.preload('players', (playersQuery) => {
|
||||||
|
playersQuery.preload('ranks')
|
||||||
|
})
|
||||||
.firstOrFail()
|
.firstOrFail()
|
||||||
|
|
||||||
const matchDetails = DetailedMatchSerializer.serializeOneMatch(match)
|
const { match: matchDetails, ranksLoaded } = DetailedMatchSerializer.serializeOneMatch(match)
|
||||||
|
|
||||||
console.timeEnd('MatchDetails')
|
console.timeEnd('MatchDetails')
|
||||||
|
|
||||||
return response.json({
|
return response.json({
|
||||||
matchDetails,
|
matchDetails,
|
||||||
|
ranksLoaded,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST - Return ranks of players for a specific game
|
||||||
|
* @param ctx
|
||||||
|
*/
|
||||||
|
public async showRanks({ request, response }: HttpContextContract) {
|
||||||
|
console.time('Ranks')
|
||||||
|
const { matchId } = await request.validate(DetailedMatchValidator)
|
||||||
|
const match = await Match.query().where('id', matchId).preload('players').firstOrFail()
|
||||||
|
const parsedRanks = await MatchPlayerRankParser.parse(match)
|
||||||
|
const serializedRanks = MatchPlayerRankSerializer.serialize(parsedRanks)
|
||||||
|
|
||||||
|
console.timeEnd('Ranks')
|
||||||
|
return response.json(serializedRanks)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ export default class SummonersController {
|
||||||
finalJSON.current = currentGame
|
finalJSON.current = currentGame
|
||||||
|
|
||||||
// RANKED STATS
|
// RANKED STATS
|
||||||
finalJSON.ranked = await SummonerService.getRanked(account, region)
|
finalJSON.ranked = await SummonerService.getRanked(account.id, region)
|
||||||
|
|
||||||
// RECENT ACTIVITY
|
// RECENT ACTIVITY
|
||||||
finalJSON.recentActivity = await StatsService.getRecentActivity(account.puuid)
|
finalJSON.recentActivity = await StatsService.getRecentActivity(account.puuid)
|
||||||
|
|
@ -78,7 +78,6 @@ export default class SummonersController {
|
||||||
|
|
||||||
finalJSON.matchesDetails = await MatchService.getMatches(region, matchIds, puuid)
|
finalJSON.matchesDetails = await MatchService.getMatches(region, matchIds, puuid)
|
||||||
|
|
||||||
// TODO: STATS
|
|
||||||
console.time('STATS')
|
console.time('STATS')
|
||||||
finalJSON.stats = await StatsService.getSummonerStats(puuid, season)
|
finalJSON.stats = await StatsService.getSummonerStats(puuid, season)
|
||||||
console.timeEnd('STATS')
|
console.timeEnd('STATS')
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { BaseModel, BelongsTo, belongsTo, column } from '@ioc:Adonis/Lucid/Orm'
|
import { BaseModel, BelongsTo, belongsTo, column, HasMany, hasMany } from '@ioc:Adonis/Lucid/Orm'
|
||||||
import Match from './Match'
|
import Match from './Match'
|
||||||
|
import MatchPlayerRank from './MatchPlayerRank'
|
||||||
import Summoner from './Summoner'
|
import Summoner from './Summoner'
|
||||||
|
|
||||||
export default class MatchPlayer extends BaseModel {
|
export default class MatchPlayer extends BaseModel {
|
||||||
|
|
@ -12,6 +13,12 @@ export default class MatchPlayer extends BaseModel {
|
||||||
@belongsTo(() => Match)
|
@belongsTo(() => Match)
|
||||||
public match: BelongsTo<typeof Match>
|
public match: BelongsTo<typeof Match>
|
||||||
|
|
||||||
|
@hasMany(() => MatchPlayerRank, {
|
||||||
|
localKey: 'id',
|
||||||
|
foreignKey: 'playerId',
|
||||||
|
})
|
||||||
|
public ranks: HasMany<typeof MatchPlayerRank>
|
||||||
|
|
||||||
@column()
|
@column()
|
||||||
public participantId: number
|
public participantId: number
|
||||||
|
|
||||||
|
|
|
||||||
38
server-v2/app/Models/MatchPlayerRank.ts
Normal file
38
server-v2/app/Models/MatchPlayerRank.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { DateTime } from 'luxon'
|
||||||
|
import { BaseModel, BelongsTo, belongsTo, column } from '@ioc:Adonis/Lucid/Orm'
|
||||||
|
import MatchPlayer from './MatchPlayer'
|
||||||
|
|
||||||
|
export default class MatchPlayerRank extends BaseModel {
|
||||||
|
@column({ isPrimary: true })
|
||||||
|
public id: number
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public playerId: number
|
||||||
|
|
||||||
|
@belongsTo(() => MatchPlayer, {
|
||||||
|
localKey: 'id',
|
||||||
|
foreignKey: 'playerId',
|
||||||
|
})
|
||||||
|
public player: BelongsTo<typeof MatchPlayer>
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public gamemode: number
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public tier: string
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public rank: number
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public lp: number
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public wins: number
|
||||||
|
|
||||||
|
@column()
|
||||||
|
public losses: number
|
||||||
|
|
||||||
|
@column.dateTime({ autoCreate: true })
|
||||||
|
public createdAt: DateTime
|
||||||
|
}
|
||||||
43
server-v2/app/Parsers/MatchPlayerRankParser.ts
Normal file
43
server-v2/app/Parsers/MatchPlayerRankParser.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import Database from '@ioc:Adonis/Lucid/Database'
|
||||||
|
import { notEmpty } from 'App/helpers'
|
||||||
|
import Match from 'App/Models/Match'
|
||||||
|
import MatchPlayer from 'App/Models/MatchPlayer'
|
||||||
|
import SummonerService from 'App/Services/SummonerService'
|
||||||
|
import { PlayerRankParsed } from './ParsedType'
|
||||||
|
|
||||||
|
class MatchPlayerRankParser {
|
||||||
|
public async parse(match: Match): Promise<PlayerRankParsed[]> {
|
||||||
|
const requests = match.players.map((p) => SummonerService.getRanked(p.summonerId, match.region))
|
||||||
|
const ranks = await Promise.all(requests)
|
||||||
|
|
||||||
|
const parsedRanks = ranks
|
||||||
|
.map((rank) => {
|
||||||
|
return Object.entries(rank).map(([queue, data]) => {
|
||||||
|
let player: MatchPlayer | undefined
|
||||||
|
if (!data || !(player = match.players.find((p) => p.summonerId === data.summonerId))) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const rank: PlayerRankParsed = {
|
||||||
|
player_id: player.id,
|
||||||
|
gamemode: queue === 'soloQ' ? 420 : 440,
|
||||||
|
tier: data.tier,
|
||||||
|
rank: SummonerService.leaguesNumbers[data.rank],
|
||||||
|
lp: data.leaguePoints,
|
||||||
|
wins: data.wins,
|
||||||
|
losses: data.losses,
|
||||||
|
}
|
||||||
|
return rank
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.flat()
|
||||||
|
.filter(notEmpty)
|
||||||
|
|
||||||
|
// Store ranks in DB
|
||||||
|
await Database.table('match_player_ranks').multiInsert(parsedRanks)
|
||||||
|
|
||||||
|
return parsedRanks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new MatchPlayerRankParser()
|
||||||
|
|
@ -15,3 +15,13 @@ export enum TeamPosition {
|
||||||
BOTTOM,
|
BOTTOM,
|
||||||
UTILITY,
|
UTILITY,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PlayerRankParsed {
|
||||||
|
player_id: number
|
||||||
|
gamemode: number
|
||||||
|
tier: string
|
||||||
|
rank: number
|
||||||
|
lp: number
|
||||||
|
wins: number
|
||||||
|
losses: number
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,11 +75,19 @@ class DetailedMatchSerializer extends MatchSerializer {
|
||||||
).toFixed(1) + '%',
|
).toFixed(1) + '%',
|
||||||
dmgTaken: +((player.damageTaken * 100) / teamStats.dmgTaken).toFixed(1) + '%',
|
dmgTaken: +((player.damageTaken * 100) / teamStats.dmgTaken).toFixed(1) + '%',
|
||||||
}
|
}
|
||||||
|
const rank = player.ranks.length
|
||||||
|
? player.ranks.reduce((acc, rank) => {
|
||||||
|
acc[rank.gamemode] = this.getPlayerRank(rank)
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
: undefined
|
||||||
return {
|
return {
|
||||||
...this.getPlayerBase(player),
|
...this.getPlayerBase(player),
|
||||||
...this.getRuneIcons(player.perksSelected, player.perksSecondaryStyle),
|
...this.getRuneIcons(player.perksSelected, player.perksSecondaryStyle),
|
||||||
|
id: player.id,
|
||||||
stats,
|
stats,
|
||||||
percentStats,
|
percentStats,
|
||||||
|
rank,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.sort(sortTeamByRole)
|
.sort(sortTeamByRole)
|
||||||
|
|
@ -106,18 +114,24 @@ class DetailedMatchSerializer extends MatchSerializer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public serializeOneMatch(match: Match): SerializedDetailedMatch {
|
public serializeOneMatch(match: Match): { match: SerializedDetailedMatch; ranksLoaded: boolean } {
|
||||||
const blueTeam = match.teams.find((team) => team.color === 100)!
|
const blueTeam = match.teams.find((team) => team.color === 100)!
|
||||||
const redTeam = match.teams.find((team) => team.color === 200)!
|
const redTeam = match.teams.find((team) => team.color === 200)!
|
||||||
|
|
||||||
const bluePlayers: MatchPlayer[] = []
|
const bluePlayers: MatchPlayer[] = []
|
||||||
const redPlayers: MatchPlayer[] = []
|
const redPlayers: MatchPlayer[] = []
|
||||||
|
|
||||||
|
let ranksLoaded = false
|
||||||
|
|
||||||
for (const p of match.players) {
|
for (const p of match.players) {
|
||||||
p.team === 100 ? bluePlayers.push(p) : redPlayers.push(p)
|
p.team === 100 ? bluePlayers.push(p) : redPlayers.push(p)
|
||||||
|
|
||||||
|
if (p.ranks.length) {
|
||||||
|
ranksLoaded = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const serializedMatch = {
|
||||||
blueTeam: this.getTeamDetailed(blueTeam, bluePlayers, match.gameDuration),
|
blueTeam: this.getTeamDetailed(blueTeam, bluePlayers, match.gameDuration),
|
||||||
date: match.date,
|
date: match.date,
|
||||||
matchId: match.id,
|
matchId: match.id,
|
||||||
|
|
@ -128,6 +142,11 @@ class DetailedMatchSerializer extends MatchSerializer {
|
||||||
season: match.season,
|
season: match.season,
|
||||||
time: match.gameDuration,
|
time: match.gameDuration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
match: serializedMatch,
|
||||||
|
ranksLoaded,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
20
server-v2/app/Serializers/MatchPlayerRankSerializer.ts
Normal file
20
server-v2/app/Serializers/MatchPlayerRankSerializer.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { PlayerRankParsed } from 'App/Parsers/ParsedType'
|
||||||
|
import MatchSerializer from './MatchSerializer'
|
||||||
|
import { SerializedPlayerRanksList } from './SerializedTypes'
|
||||||
|
|
||||||
|
class MatchPlayerRankSerializer extends MatchSerializer {
|
||||||
|
public serialize(ranks: PlayerRankParsed[]): SerializedPlayerRanksList {
|
||||||
|
const result = ranks.reduce((acc, rank) => {
|
||||||
|
if (!acc[rank.player_id]) {
|
||||||
|
acc[rank.player_id] = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[rank.player_id][rank.gamemode] = this.getPlayerRank(rank)
|
||||||
|
return acc
|
||||||
|
}, {} as SerializedPlayerRanksList)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new MatchPlayerRankSerializer()
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import MatchPlayer from 'App/Models/MatchPlayer'
|
import MatchPlayer from 'App/Models/MatchPlayer'
|
||||||
import { TeamPosition } from 'App/Parsers/ParsedType'
|
import { PlayerRankParsed, TeamPosition } from 'App/Parsers/ParsedType'
|
||||||
import CDragonService from 'App/Services/CDragonService'
|
import CDragonService from 'App/Services/CDragonService'
|
||||||
|
import SummonerService from 'App/Services/SummonerService'
|
||||||
import {
|
import {
|
||||||
SerializedBasePlayer,
|
SerializedBasePlayer,
|
||||||
SerializedMatchChampion,
|
SerializedMatchChampion,
|
||||||
|
|
@ -99,4 +100,15 @@ export default abstract class MatchSerializer {
|
||||||
summonerSpell2: this.getSummonerSpell(player.summoner2Id),
|
summonerSpell2: this.getSummonerSpell(player.summoner2Id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getPlayerRank(rank: PlayerRankParsed) {
|
||||||
|
return {
|
||||||
|
tier: rank.tier,
|
||||||
|
rank: rank.rank,
|
||||||
|
lp: rank.lp,
|
||||||
|
wins: rank.wins,
|
||||||
|
losses: rank.losses,
|
||||||
|
shortName: SummonerService.getRankedShortName(rank),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ export interface SerializedDetailedMatchBan {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SerializedDetailedMatchPlayer extends SerializedBasePlayer {
|
export interface SerializedDetailedMatchPlayer extends SerializedBasePlayer {
|
||||||
|
id: number
|
||||||
stats: SerializedDetailedMatchStats
|
stats: SerializedDetailedMatchStats
|
||||||
percentStats: SerializedDetailedMatchPercentStats
|
percentStats: SerializedDetailedMatchPercentStats
|
||||||
primaryRune: string | null
|
primaryRune: string | null
|
||||||
|
|
@ -160,3 +161,20 @@ export interface SerializedDetailedMatchPercentStats {
|
||||||
minions: number
|
minions: number
|
||||||
vision: number
|
vision: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SerializedPlayerRanksList {
|
||||||
|
[summonerId: string]: SerializedPlayerRanks
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SerializedPlayerRanks {
|
||||||
|
[gamemode: number]: SerializedPlayerRank
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SerializedPlayerRank {
|
||||||
|
tier: string
|
||||||
|
rank: number
|
||||||
|
lp: number
|
||||||
|
wins: number
|
||||||
|
losses: number
|
||||||
|
shortName: number | string
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ import Jax from './Jax'
|
||||||
import { SummonerDTO } from 'App/Services/Jax/src/Endpoints/SummonerEndpoint'
|
import { SummonerDTO } from 'App/Services/Jax/src/Endpoints/SummonerEndpoint'
|
||||||
import { LeagueEntryDTO } from './Jax/src/Endpoints/LeagueEndpoint'
|
import { LeagueEntryDTO } from './Jax/src/Endpoints/LeagueEndpoint'
|
||||||
import Summoner from 'App/Models/Summoner'
|
import Summoner from 'App/Models/Summoner'
|
||||||
|
import { PlayerRankParsed } from 'App/Parsers/ParsedType'
|
||||||
|
import MatchPlayerRank from 'App/Models/MatchPlayerRank'
|
||||||
|
|
||||||
export interface LeagueEntriesByQueue {
|
export interface LeagueEntriesByQueue {
|
||||||
soloQ?: LeagueEntryByQueue
|
soloQ?: LeagueEntryByQueue
|
||||||
|
|
@ -15,8 +17,16 @@ export interface LeagueEntryByQueue extends LeagueEntryDTO {
|
||||||
}
|
}
|
||||||
|
|
||||||
class SummonerService {
|
class SummonerService {
|
||||||
private uniqueLeagues = ['CHALLENGER', 'GRANDMASTER', 'MASTER']
|
private readonly uniqueLeagues = ['CHALLENGER', 'GRANDMASTER', 'MASTER']
|
||||||
private leaguesNumbers = { I: 1, II: 2, III: 3, IV: 4 }
|
public readonly leaguesNumbers = { I: 1, II: 2, III: 3, IV: 4 }
|
||||||
|
|
||||||
|
public getRankedShortName(rank: PlayerRankParsed | MatchPlayerRank) {
|
||||||
|
return this.uniqueLeagues.includes(rank.tier) ? rank.lp : rank.tier[0] + rank.rank
|
||||||
|
}
|
||||||
|
|
||||||
|
public getWinrate(wins: number, losses: number) {
|
||||||
|
return +((wins * 100) / (wins + losses)).toFixed(1) + '%'
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper to transform League Data from the Riot API
|
* Helper to transform League Data from the Riot API
|
||||||
|
|
@ -29,7 +39,7 @@ class SummonerService {
|
||||||
const fullRank = this.uniqueLeagues.includes(league.tier)
|
const fullRank = this.uniqueLeagues.includes(league.tier)
|
||||||
? league.tier
|
? league.tier
|
||||||
: `${league.tier} ${league.rank}`
|
: `${league.tier} ${league.rank}`
|
||||||
const winrate = +((league.wins * 100) / (league.wins + league.losses)).toFixed(1) + '%'
|
const winrate = this.getWinrate(league.wins, league.losses)
|
||||||
const shortName = this.uniqueLeagues.includes(league.tier)
|
const shortName = this.uniqueLeagues.includes(league.tier)
|
||||||
? league.leaguePoints
|
? league.leaguePoints
|
||||||
: league.tier[0] + this.leaguesNumbers[league.rank]
|
: league.tier[0] + this.leaguesNumbers[league.rank]
|
||||||
|
|
@ -70,8 +80,8 @@ class SummonerService {
|
||||||
* @param account
|
* @param account
|
||||||
* @param region
|
* @param region
|
||||||
*/
|
*/
|
||||||
public async getRanked(account: SummonerDTO, region: string): Promise<LeagueEntriesByQueue> {
|
public async getRanked(summonerId: string, region: string): Promise<LeagueEntriesByQueue> {
|
||||||
const ranked = await Jax.League.summonerID(account.id, region)
|
const ranked = await Jax.League.summonerID(summonerId, region)
|
||||||
const result: LeagueEntriesByQueue = {}
|
const result: LeagueEntriesByQueue = {}
|
||||||
|
|
||||||
if (ranked && ranked.length) {
|
if (ranked && ranked.length) {
|
||||||
|
|
@ -80,7 +90,6 @@ class SummonerService {
|
||||||
result.flex5v5 =
|
result.flex5v5 =
|
||||||
this.getleagueData(ranked.find((e) => e.queueType === 'RANKED_FLEX_SR')) || undefined
|
this.getleagueData(ranked.find((e) => e.queueType === 'RANKED_FLEX_SR')) || undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,3 +110,10 @@ export function sortTeamByRole<T extends SortableByRole>(a: T, b: T) {
|
||||||
const sortingArr = ['TOP', 'JUNGLE', 'MIDDLE', 'BOTTOM', 'UTILITY']
|
const sortingArr = ['TOP', 'JUNGLE', 'MIDDLE', 'BOTTOM', 'UTILITY']
|
||||||
return sortingArr.indexOf(a.role) - sortingArr.indexOf(b.role)
|
return sortingArr.indexOf(a.role) - sortingArr.indexOf(b.role)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/46700791/9188650
|
||||||
|
export function notEmpty<TValue>(value: TValue | null | undefined): value is TValue {
|
||||||
|
if (value === null || value === undefined) return false
|
||||||
|
const testDummy: TValue = value
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import BaseSchema from '@ioc:Adonis/Lucid/Schema'
|
||||||
|
|
||||||
|
export default class MatchPlayerRanks extends BaseSchema {
|
||||||
|
protected tableName = 'match_player_ranks'
|
||||||
|
|
||||||
|
public async up() {
|
||||||
|
this.schema.createTable(this.tableName, (table) => {
|
||||||
|
table.increments('id')
|
||||||
|
|
||||||
|
table.integer('player_id').unsigned().notNullable()
|
||||||
|
|
||||||
|
table.integer('gamemode').notNullable()
|
||||||
|
table.string('tier', 11).notNullable()
|
||||||
|
table.integer('rank').notNullable()
|
||||||
|
table.integer('lp').notNullable()
|
||||||
|
table.integer('wins').notNullable()
|
||||||
|
table.integer('losses').notNullable()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
|
||||||
|
*/
|
||||||
|
table.timestamp('created_at', { useTz: true })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down() {
|
||||||
|
this.schema.dropTable(this.tableName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,6 +37,6 @@ Route.post('/summoner/records', 'SummonersController.records')
|
||||||
|
|
||||||
Route.post('/match', 'MatchesController.index')
|
Route.post('/match', 'MatchesController.index')
|
||||||
Route.post('/match/details', 'MatchesController.show')
|
Route.post('/match/details', 'MatchesController.show')
|
||||||
// Route.post('/match/details/ranks', 'MatchesController.showRanks')
|
Route.post('/match/details/ranks', 'MatchesController.showRanks')
|
||||||
|
|
||||||
Route.get('/cdragon/runes', 'CDragonController.runes')
|
Route.get('/cdragon/runes', 'CDragonController.runes')
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue