@firestone-hs/bgs-global-stats 1.0.21 → 1.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -89,7 +89,8 @@ const buildHeroesForMmr = (rows, lastPatch, tribesStr) => {
89
89
  ? rows.filter(row => !!row.tribes).filter(row => row.tribes === tribesStr)
90
90
  : rows;
91
91
  const allTimeHeroes = buildHeroStats(rowsWithTribes, 'all-time', tribesStr);
92
- const lastPatchHeroes = buildHeroStats(rowsWithTribes.filter(row => row.buildNumber >= lastPatch.number && row.creationDate > new Date(lastPatch.date)), 'last-patch', tribesStr);
92
+ const lastPatchHeroes = buildHeroStats(rowsWithTribes.filter(row => row.buildNumber >= lastPatch.number ||
93
+ row.creationDate > new Date(new Date(lastPatch.date).getTime() + 24 * 60 * 60 * 1000)), 'last-patch', tribesStr);
93
94
  const threeDaysHeroes = buildHeroStats(rowsWithTribes.filter(row => row.creationDate >= new Date(new Date().getTime() - 3 * 24 * 60 * 60 * 1000)), 'past-three', tribesStr);
94
95
  const sevenDaysHeroes = buildHeroStats(rowsWithTribes.filter(row => row.creationDate >= new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000)), 'past-seven', tribesStr);
95
96
  return [...allTimeHeroes, ...lastPatchHeroes, ...threeDaysHeroes, ...sevenDaysHeroes];
@@ -1 +1 @@
1
- {"version":3,"file":"build-battlegrounds-hero-stats-new.js","sourceRoot":"/","sources":["build-battlegrounds-hero-stats-new.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/no-use-before-define */\r\nimport { AllCardsService, CardIds, Race } from '@firestone-hs/reference-data';\r\nimport { ServerlessMysql } from 'serverless-mysql';\r\nimport { gzipSync } from 'zlib';\r\nimport { BgsGlobalHeroStat2, BgsGlobalStats2, MmrPercentile } from './bgs-global-stats';\r\nimport { getConnection as getConnectionStats } from './db/rds';\r\nimport { S3 } from './db/s3';\r\nimport { formatDate, groupByFunction, http } from './utils/util-functions';\r\n\r\nconst s3 = new S3();\r\nconst allCards = new AllCardsService();\r\n\r\n// This example demonstrates a NodeJS 8.10 async handler[1], however of course you could use\r\n// the more traditional callback-style handler.\r\n// [1]: https://aws.amazon.com/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/\r\nexport default async (event): Promise<any> => {\r\n\tawait handleNewStats();\r\n};\r\n\r\nexport const handleNewStats = async () => {\r\n\tawait allCards.initializeCardsDb('20211104');\r\n\tconst lastPatch = await getLastBattlegroundsPatch();\r\n\tconst mysql = await getConnectionStats();\r\n\r\n\tconst rows: readonly InternalBgsRow[] = await loadRows(mysql, lastPatch);\r\n\tconst mmrPercentiles: readonly MmrPercentile[] = buildMmrPercentiles(rows);\r\n\r\n\t// A basic approach could be to generate all files for all tribe combinations. That way,\r\n\t// the app will only query static files with all the info for the specific tribe combination\r\n\tconst allTribes = extractAllTribes(rows);\r\n\tconsole.log('all tribes', allTribes);\r\n\r\n\tconst tribePermutations = [null, ...combine(allTribes, 5)];\r\n\t// const tribePermutations = [null];\r\n\tconsole.log('tribe permutations, should be 56, because 8 tribes', tribePermutations.length);\r\n\tconsole.log('tribe permutations', tribePermutations);\r\n\tfor (const tribes of tribePermutations) {\r\n\t\tconsole.log('handling tribes', tribes);\r\n\t\tconst tribesStr = !!tribes?.length ? tribes.join(',') : null;\r\n\t\tconst statsForTribes: BgsGlobalStats2 = {\r\n\t\t\tlastUpdateDate: formatDate(new Date()),\r\n\t\t\tmmrPercentiles: mmrPercentiles,\r\n\t\t\theroStats: buildHeroes(rows, lastPatch, mmrPercentiles, tribesStr),\r\n\t\t\tallTribes: allTribes,\r\n\t\t};\r\n\t\tconst stringResults = JSON.stringify(statsForTribes);\r\n\t\tconst gzippedResults = gzipSync(stringResults);\r\n\t\tawait s3.writeFile(\r\n\t\t\tgzippedResults,\r\n\t\t\t'static.zerotoheroes.com',\r\n\t\t\t`api/bgs/bgs-global-stats-${!!tribes?.length ? tribes.join('-') : 'all-tribes'}.gz.json`,\r\n\t\t\t'application/json',\r\n\t\t\t'gzip',\r\n\t\t);\r\n\t}\r\n\r\n\tawait mysql.end();\r\n\r\n\treturn { statusCode: 200, body: null };\r\n};\r\n\r\n// https://stackoverflow.com/a/47204248/548701\r\nconst combine = <T>(input: readonly T[], chooseN: number): T[][] => {\r\n\tconst finalResult: T[][] = [];\r\n\r\n\tconst intermediateResult = [];\r\n\tintermediateResult.length = chooseN;\r\n\tconst combineInternal = <T>(input: readonly T[], chooseN: number, start = 0): void => {\r\n\t\tif (chooseN === 0) {\r\n\t\t\tfinalResult.push([...intermediateResult].sort());\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tfor (let i = start; i <= input.length - chooseN; i++) {\r\n\t\t\tintermediateResult[intermediateResult.length - chooseN] = input[i];\r\n\t\t\tcombineInternal(input, chooseN - 1, i + 1);\r\n\t\t}\r\n\t};\r\n\tcombineInternal(input, chooseN, 0);\r\n\r\n\treturn finalResult;\r\n};\r\n\r\nconst extractAllTribes = (rows: readonly InternalBgsRow[]): readonly Race[] => {\r\n\treturn [\r\n\t\t...new Set(\r\n\t\t\trows\r\n\t\t\t\t.map(row => row.tribes)\r\n\t\t\t\t.filter(tribes => !!tribes?.length)\r\n\t\t\t\t.map(tribes => tribes.split(',').map(strTribe => parseInt(strTribe) as Race))\r\n\t\t\t\t.reduce((a, b) => [...new Set(a.concat(b))], []),\r\n\t\t),\r\n\t];\r\n};\r\n\r\nconst buildHeroes = (\r\n\trows: readonly InternalBgsRow[],\r\n\tlastPatch: PatchInfo,\r\n\tmmrPercentiles: readonly MmrPercentile[],\r\n\ttribesStr: string,\r\n): readonly BgsGlobalHeroStat2[] => {\r\n\treturn mmrPercentiles\r\n\t\t.map(\r\n\t\t\tmmrPercentile =>\r\n\t\t\t\t[mmrPercentile, rows.filter(row => row.rating >= mmrPercentile.mmr)] as [\r\n\t\t\t\t\tMmrPercentile,\r\n\t\t\t\t\treadonly InternalBgsRow[],\r\n\t\t\t\t],\r\n\t\t)\r\n\t\t.map(([mmr, rows]) => {\r\n\t\t\t// console.log('building heroes for mme', mmr.percentile);\r\n\t\t\treturn buildHeroesForMmr(rows, lastPatch, tribesStr).map(stat => ({\r\n\t\t\t\t...stat,\r\n\t\t\t\tmmrPercentile: mmr.percentile,\r\n\t\t\t}));\r\n\t\t})\r\n\t\t.map(info => {\r\n\t\t\t// console.log('reducing');\r\n\t\t\treturn info;\r\n\t\t})\r\n\t\t.reduce((a, b) => [...a, ...b], []);\r\n};\r\n\r\nconst buildHeroesForMmr = (\r\n\trows: readonly InternalBgsRow[],\r\n\tlastPatch: PatchInfo,\r\n\ttribesStr: string,\r\n): readonly BgsGlobalHeroStat2[] => {\r\n\tconst rowsWithTribes = !!tribesStr\r\n\t\t? rows.filter(row => !!row.tribes).filter(row => row.tribes === tribesStr)\r\n\t\t: rows;\r\n\tconst allTimeHeroes = buildHeroStats(rowsWithTribes, 'all-time', tribesStr);\r\n\tconst lastPatchHeroes = buildHeroStats(\r\n\t\trowsWithTribes.filter(\r\n\t\t\trow => row.buildNumber >= lastPatch.number && row.creationDate > new Date(lastPatch.date),\r\n\t\t),\r\n\t\t'last-patch',\r\n\t\ttribesStr,\r\n\t);\r\n\tconst threeDaysHeroes = buildHeroStats(\r\n\t\trowsWithTribes.filter(row => row.creationDate >= new Date(new Date().getTime() - 3 * 24 * 60 * 60 * 1000)),\r\n\t\t'past-three',\r\n\t\ttribesStr,\r\n\t);\r\n\tconst sevenDaysHeroes = buildHeroStats(\r\n\t\trowsWithTribes.filter(row => row.creationDate >= new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000)),\r\n\t\t'past-seven',\r\n\t\ttribesStr,\r\n\t);\r\n\treturn [...allTimeHeroes, ...lastPatchHeroes, ...threeDaysHeroes, ...sevenDaysHeroes];\r\n};\r\n\r\nconst buildHeroStats = (\r\n\trows: readonly InternalBgsRow[],\r\n\tperiod: string,\r\n\ttribesStr: string,\r\n): readonly BgsGlobalHeroStat2[] => {\r\n\tconst grouped: { [groupingKey: string]: readonly InternalBgsRow[] } = !!tribesStr\r\n\t\t? groupByFunction((row: InternalBgsRow) => `${row.heroCardId}-${row.tribes}-${row.darkmoonPrizes}`)(rows)\r\n\t\t: groupByFunction((row: InternalBgsRow) => `${row.heroCardId}-${row.darkmoonPrizes}`)(rows);\r\n\r\n\treturn Object.values(grouped).map(groupedRows => {\r\n\t\tconst ref = groupedRows[0];\r\n\r\n\t\tconst placementDistribution = buildPlacementDistribution(groupedRows);\r\n\t\tconst combatWinrate = buildCombatWinrate(groupedRows);\r\n\t\tconst warbandStats = buildWarbandStats(groupedRows);\r\n\r\n\t\treturn {\r\n\t\t\tdate: period,\r\n\t\t\tcardId: ref.heroCardId,\r\n\t\t\ttotalMatches: groupedRows.length,\r\n\t\t\tplacementDistribution: placementDistribution,\r\n\t\t\tcombatWinrate: combatWinrate,\r\n\t\t\twarbandStats: warbandStats,\r\n\t\t} as BgsGlobalHeroStat2;\r\n\t});\r\n};\r\n\r\nconst buildWarbandStats = (\r\n\trows: readonly InternalBgsRow[],\r\n): readonly { turn: number; dataPoints: number; totalStats: number }[] => {\r\n\tconst data: { [turn: string]: { dataPoints: number; totalStats: number } } = {};\r\n\tfor (const row of rows) {\r\n\t\tif (!row.warbandStats?.length) {\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tconst parsed: readonly { turn: number; totalStats: number }[] = JSON.parse(row.warbandStats);\r\n\t\tif (!parsed?.length) {\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tfor (const turnInfo of parsed) {\r\n\t\t\tif (turnInfo.turn === 0 || turnInfo.totalStats == null) {\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tconst existingInfo = data['' + turnInfo.turn] ?? { dataPoints: 0, totalStats: 0 };\r\n\t\t\texistingInfo.dataPoints = existingInfo.dataPoints + 1;\r\n\t\t\texistingInfo.totalStats = existingInfo.totalStats + Math.round(turnInfo.totalStats);\r\n\t\t\tdata['' + turnInfo.turn] = existingInfo;\r\n\t\t}\r\n\t}\r\n\r\n\tconst result: { turn: number; dataPoints: number; totalStats: number }[] = Object.keys(data).map(turn => ({\r\n\t\tturn: +turn,\r\n\t\tdataPoints: data[turn].dataPoints,\r\n\t\ttotalStats: data[turn].totalStats,\r\n\t}));\r\n\treturn result;\r\n};\r\n\r\nconst buildCombatWinrate = (\r\n\trows: readonly InternalBgsRow[],\r\n): readonly { turn: number; dataPoints: number; totalWinrate: number }[] => {\r\n\tconst ref = rows[0];\r\n\tconst debug = ref.heroCardId === 'BG21_HERO_000';\r\n\r\n\tconst data: { [turn: string]: { dataPoints: number; totalWinrate: number } } = {};\r\n\tfor (const row of rows) {\r\n\t\t// console.log('building combatWinrate', row);\r\n\t\tif (!row.combatWinrate?.length) {\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tlet parsed: readonly { turn: number; winrate: number }[] = null;\r\n\t\ttry {\r\n\t\t\tparsed = JSON.parse(row.combatWinrate);\r\n\t\t\t// console.log('parsed', parsed);\r\n\t\t\tif (!parsed?.length) {\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t} catch (e) {\r\n\t\t\tconsole.error('Could not parse combat winrate', row.id, e);\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tif (debug) {\r\n\t\t\t// console.log('handling combat winrate', parsed);\r\n\t\t}\r\n\r\n\t\tfor (const turnInfo of parsed) {\r\n\t\t\tif (turnInfo.turn === 0 || turnInfo.winrate == null) {\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tif (debug) {\r\n\t\t\t\t// console.log('\\t turnInfo', turnInfo);\r\n\t\t\t}\r\n\t\t\tconst existingInfo = data['' + turnInfo.turn] ?? { dataPoints: 0, totalWinrate: 0 };\r\n\t\t\tif (debug) {\r\n\t\t\t\t// console.log('\\t existingInfo', existingInfo);\r\n\t\t\t}\r\n\t\t\texistingInfo.dataPoints = existingInfo.dataPoints + 1;\r\n\t\t\texistingInfo.totalWinrate = existingInfo.totalWinrate + Math.round(turnInfo.winrate);\r\n\t\t\tif (debug) {\r\n\t\t\t\t// console.log('\\t existingInfo after', existingInfo);\r\n\t\t\t}\r\n\t\t\tdata['' + turnInfo.turn] = existingInfo;\r\n\t\t\tif (debug) {\r\n\t\t\t\t// console.log('\\t data', data);\r\n\t\t\t}\r\n\t\t}\r\n\t\t// console.log('existingInfo', existingInfo);\r\n\t}\r\n\r\n\tif (debug) {\r\n\t\t// console.log('\\t data', data);\r\n\t}\r\n\tconst result: { turn: number; dataPoints: number; totalWinrate: number }[] = Object.keys(data).map(turn => ({\r\n\t\tturn: +turn,\r\n\t\tdataPoints: data[turn].dataPoints,\r\n\t\ttotalWinrate: data[turn].totalWinrate,\r\n\t}));\r\n\tif (debug) {\r\n\t\t// console.log('\\t result', result);\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst buildPlacementDistribution = (\r\n\trows: readonly InternalBgsRow[],\r\n): readonly { rank: number; totalMatches: number }[] => {\r\n\tconst placementDistribution: { rank: number; totalMatches: number }[] = [];\r\n\tconst groupedByPlacement: { [placement: string]: readonly InternalBgsRow[] } = groupByFunction(\r\n\t\t(res: InternalBgsRow) => '' + res.rank,\r\n\t)(rows);\r\n\tObject.keys(groupedByPlacement).forEach(placement =>\r\n\t\tplacementDistribution.push({ rank: +placement, totalMatches: groupedByPlacement[placement].length }),\r\n\t);\r\n\treturn placementDistribution;\r\n};\r\n\r\nconst loadRows = async (mysql: ServerlessMysql, patch: PatchInfo): Promise<readonly InternalBgsRow[]> => {\r\n\tconsole.log('loading rows', patch);\r\n\tconst query = `\r\n\t\tSELECT * FROM bgs_run_stats\r\n\t\tWHERE creationDate > DATE_SUB(NOW(), INTERVAL 30 DAY)\r\n\t`;\r\n\tconsole.log('running query', query);\r\n\tconst rows: readonly InternalBgsRow[] = await mysql.query(query);\r\n\tconsole.log('rows', rows?.length, rows[0]);\r\n\treturn rows\r\n\t\t.filter(row => row.heroCardId.startsWith('TB_BaconShop_') || row.heroCardId.startsWith('BG'))\r\n\t\t.filter(row => row.heroCardId !== CardIds.ArannaStarseeker_ArannaUnleashedTokenBattlegrounds);\r\n};\r\n\r\nconst getLastBattlegroundsPatch = async (): Promise<PatchInfo> => {\r\n\tconst patchInfo = await http(`https://static.zerotoheroes.com/hearthstone/data/patches.json`);\r\n\tconst structuredPatch = JSON.parse(patchInfo);\r\n\tconst patchNumber = structuredPatch.currentBattlegroundsMetaPatch;\r\n\treturn structuredPatch.patches.find(patch => patch.number === patchNumber);\r\n};\r\n\r\nconst buildMmrPercentiles = (rows: readonly InternalBgsRow[]): readonly MmrPercentile[] => {\r\n\tconst sortedMmrs = rows.map(row => row.rating).sort((a, b) => a - b);\r\n\tconst median = sortedMmrs[Math.floor(sortedMmrs.length / 2)];\r\n\tconst top25 = sortedMmrs[Math.floor((sortedMmrs.length / 4) * 3)];\r\n\tconst top10 = sortedMmrs[Math.floor((sortedMmrs.length / 10) * 9)];\r\n\t// const top1 = sortedMmrs[Math.floor((sortedMmrs.length / 100) * 99)];\r\n\t// console.debug('percentiles', median, top25, top10, top1);\r\n\treturn [\r\n\t\t{\r\n\t\t\tpercentile: 100,\r\n\t\t\tmmr: 0,\r\n\t\t},\r\n\t\t{\r\n\t\t\tpercentile: 50,\r\n\t\t\tmmr: median,\r\n\t\t},\r\n\t\t{\r\n\t\t\tpercentile: 25,\r\n\t\t\tmmr: top25,\r\n\t\t},\r\n\t\t{\r\n\t\t\tpercentile: 10,\r\n\t\t\tmmr: top10,\r\n\t\t},\r\n\t\t// {\r\n\t\t// \tpercentile: 1,\r\n\t\t// \tmmr: top1,\r\n\t\t// },\r\n\t];\r\n};\r\n\r\ninterface InternalBgsRow {\r\n\treadonly id: number;\r\n\treadonly creationDate: Date;\r\n\treadonly buildNumber: number;\r\n\treadonly rating: number;\r\n\treadonly heroCardId: string;\r\n\treadonly rank: number;\r\n\treadonly reviewId: string;\r\n\treadonly tribes: string;\r\n\treadonly darkmoonPrizes: boolean;\r\n\treadonly combatWinrate: string;\r\n\treadonly warbandStats: string;\r\n}\r\n\r\ninterface PatchInfo {\r\n\treadonly number: number;\r\n\treadonly version: string;\r\n\treadonly name: string;\r\n\treadonly date: string;\r\n}\r\n"]}
1
+ {"version":3,"file":"build-battlegrounds-hero-stats-new.js","sourceRoot":"/","sources":["build-battlegrounds-hero-stats-new.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/no-use-before-define */\r\nimport { AllCardsService, CardIds, Race } from '@firestone-hs/reference-data';\r\nimport { ServerlessMysql } from 'serverless-mysql';\r\nimport { gzipSync } from 'zlib';\r\nimport { BgsGlobalHeroStat2, BgsGlobalStats2, MmrPercentile } from './bgs-global-stats';\r\nimport { getConnection as getConnectionStats } from './db/rds';\r\nimport { S3 } from './db/s3';\r\nimport { formatDate, groupByFunction, http } from './utils/util-functions';\r\n\r\nconst s3 = new S3();\r\nconst allCards = new AllCardsService();\r\n\r\n// This example demonstrates a NodeJS 8.10 async handler[1], however of course you could use\r\n// the more traditional callback-style handler.\r\n// [1]: https://aws.amazon.com/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/\r\nexport default async (event): Promise<any> => {\r\n\tawait handleNewStats();\r\n};\r\n\r\nexport const handleNewStats = async () => {\r\n\tawait allCards.initializeCardsDb('20211104');\r\n\tconst lastPatch = await getLastBattlegroundsPatch();\r\n\tconst mysql = await getConnectionStats();\r\n\r\n\tconst rows: readonly InternalBgsRow[] = await loadRows(mysql, lastPatch);\r\n\tconst mmrPercentiles: readonly MmrPercentile[] = buildMmrPercentiles(rows);\r\n\r\n\t// A basic approach could be to generate all files for all tribe combinations. That way,\r\n\t// the app will only query static files with all the info for the specific tribe combination\r\n\tconst allTribes = extractAllTribes(rows);\r\n\tconsole.log('all tribes', allTribes);\r\n\r\n\tconst tribePermutations = [null, ...combine(allTribes, 5)];\r\n\t// const tribePermutations = [null];\r\n\tconsole.log('tribe permutations, should be 56, because 8 tribes', tribePermutations.length);\r\n\tconsole.log('tribe permutations', tribePermutations);\r\n\tfor (const tribes of tribePermutations) {\r\n\t\tconsole.log('handling tribes', tribes);\r\n\t\tconst tribesStr = !!tribes?.length ? tribes.join(',') : null;\r\n\t\tconst statsForTribes: BgsGlobalStats2 = {\r\n\t\t\tlastUpdateDate: formatDate(new Date()),\r\n\t\t\tmmrPercentiles: mmrPercentiles,\r\n\t\t\theroStats: buildHeroes(rows, lastPatch, mmrPercentiles, tribesStr),\r\n\t\t\tallTribes: allTribes,\r\n\t\t};\r\n\t\tconst stringResults = JSON.stringify(statsForTribes);\r\n\t\tconst gzippedResults = gzipSync(stringResults);\r\n\t\tawait s3.writeFile(\r\n\t\t\tgzippedResults,\r\n\t\t\t'static.zerotoheroes.com',\r\n\t\t\t`api/bgs/bgs-global-stats-${!!tribes?.length ? tribes.join('-') : 'all-tribes'}.gz.json`,\r\n\t\t\t'application/json',\r\n\t\t\t'gzip',\r\n\t\t);\r\n\t}\r\n\r\n\tawait mysql.end();\r\n\r\n\treturn { statusCode: 200, body: null };\r\n};\r\n\r\n// https://stackoverflow.com/a/47204248/548701\r\nconst combine = <T>(input: readonly T[], chooseN: number): T[][] => {\r\n\tconst finalResult: T[][] = [];\r\n\r\n\tconst intermediateResult = [];\r\n\tintermediateResult.length = chooseN;\r\n\tconst combineInternal = <T>(input: readonly T[], chooseN: number, start = 0): void => {\r\n\t\tif (chooseN === 0) {\r\n\t\t\tfinalResult.push([...intermediateResult].sort());\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tfor (let i = start; i <= input.length - chooseN; i++) {\r\n\t\t\tintermediateResult[intermediateResult.length - chooseN] = input[i];\r\n\t\t\tcombineInternal(input, chooseN - 1, i + 1);\r\n\t\t}\r\n\t};\r\n\tcombineInternal(input, chooseN, 0);\r\n\r\n\treturn finalResult;\r\n};\r\n\r\nconst extractAllTribes = (rows: readonly InternalBgsRow[]): readonly Race[] => {\r\n\treturn [\r\n\t\t...new Set(\r\n\t\t\trows\r\n\t\t\t\t.map(row => row.tribes)\r\n\t\t\t\t.filter(tribes => !!tribes?.length)\r\n\t\t\t\t.map(tribes => tribes.split(',').map(strTribe => parseInt(strTribe) as Race))\r\n\t\t\t\t.reduce((a, b) => [...new Set(a.concat(b))], []),\r\n\t\t),\r\n\t];\r\n};\r\n\r\nconst buildHeroes = (\r\n\trows: readonly InternalBgsRow[],\r\n\tlastPatch: PatchInfo,\r\n\tmmrPercentiles: readonly MmrPercentile[],\r\n\ttribesStr: string,\r\n): readonly BgsGlobalHeroStat2[] => {\r\n\treturn mmrPercentiles\r\n\t\t.map(\r\n\t\t\tmmrPercentile =>\r\n\t\t\t\t[mmrPercentile, rows.filter(row => row.rating >= mmrPercentile.mmr)] as [\r\n\t\t\t\t\tMmrPercentile,\r\n\t\t\t\t\treadonly InternalBgsRow[],\r\n\t\t\t\t],\r\n\t\t)\r\n\t\t.map(([mmr, rows]) => {\r\n\t\t\t// console.log('building heroes for mme', mmr.percentile);\r\n\t\t\treturn buildHeroesForMmr(rows, lastPatch, tribesStr).map(stat => ({\r\n\t\t\t\t...stat,\r\n\t\t\t\tmmrPercentile: mmr.percentile,\r\n\t\t\t}));\r\n\t\t})\r\n\t\t.map(info => {\r\n\t\t\t// console.log('reducing');\r\n\t\t\treturn info;\r\n\t\t})\r\n\t\t.reduce((a, b) => [...a, ...b], []);\r\n};\r\n\r\nconst buildHeroesForMmr = (\r\n\trows: readonly InternalBgsRow[],\r\n\tlastPatch: PatchInfo,\r\n\ttribesStr: string,\r\n): readonly BgsGlobalHeroStat2[] => {\r\n\tconst rowsWithTribes = !!tribesStr\r\n\t\t? rows.filter(row => !!row.tribes).filter(row => row.tribes === tribesStr)\r\n\t\t: rows;\r\n\tconst allTimeHeroes = buildHeroStats(rowsWithTribes, 'all-time', tribesStr);\r\n\tconst lastPatchHeroes = buildHeroStats(\r\n\t\trowsWithTribes.filter(\r\n\t\t\trow =>\r\n\t\t\t\trow.buildNumber >= lastPatch.number ||\r\n\t\t\t\trow.creationDate > new Date(new Date(lastPatch.date).getTime() + 24 * 60 * 60 * 1000),\r\n\t\t),\r\n\t\t'last-patch',\r\n\t\ttribesStr,\r\n\t);\r\n\tconst threeDaysHeroes = buildHeroStats(\r\n\t\trowsWithTribes.filter(row => row.creationDate >= new Date(new Date().getTime() - 3 * 24 * 60 * 60 * 1000)),\r\n\t\t'past-three',\r\n\t\ttribesStr,\r\n\t);\r\n\tconst sevenDaysHeroes = buildHeroStats(\r\n\t\trowsWithTribes.filter(row => row.creationDate >= new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000)),\r\n\t\t'past-seven',\r\n\t\ttribesStr,\r\n\t);\r\n\treturn [...allTimeHeroes, ...lastPatchHeroes, ...threeDaysHeroes, ...sevenDaysHeroes];\r\n};\r\n\r\nconst buildHeroStats = (\r\n\trows: readonly InternalBgsRow[],\r\n\tperiod: string,\r\n\ttribesStr: string,\r\n): readonly BgsGlobalHeroStat2[] => {\r\n\tconst grouped: { [groupingKey: string]: readonly InternalBgsRow[] } = !!tribesStr\r\n\t\t? groupByFunction((row: InternalBgsRow) => `${row.heroCardId}-${row.tribes}-${row.darkmoonPrizes}`)(rows)\r\n\t\t: groupByFunction((row: InternalBgsRow) => `${row.heroCardId}-${row.darkmoonPrizes}`)(rows);\r\n\r\n\treturn Object.values(grouped).map(groupedRows => {\r\n\t\tconst ref = groupedRows[0];\r\n\r\n\t\tconst placementDistribution = buildPlacementDistribution(groupedRows);\r\n\t\tconst combatWinrate = buildCombatWinrate(groupedRows);\r\n\t\tconst warbandStats = buildWarbandStats(groupedRows);\r\n\r\n\t\treturn {\r\n\t\t\tdate: period,\r\n\t\t\tcardId: ref.heroCardId,\r\n\t\t\ttotalMatches: groupedRows.length,\r\n\t\t\tplacementDistribution: placementDistribution,\r\n\t\t\tcombatWinrate: combatWinrate,\r\n\t\t\twarbandStats: warbandStats,\r\n\t\t} as BgsGlobalHeroStat2;\r\n\t});\r\n};\r\n\r\nconst buildWarbandStats = (\r\n\trows: readonly InternalBgsRow[],\r\n): readonly { turn: number; dataPoints: number; totalStats: number }[] => {\r\n\tconst data: { [turn: string]: { dataPoints: number; totalStats: number } } = {};\r\n\tfor (const row of rows) {\r\n\t\tif (!row.warbandStats?.length) {\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tconst parsed: readonly { turn: number; totalStats: number }[] = JSON.parse(row.warbandStats);\r\n\t\tif (!parsed?.length) {\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tfor (const turnInfo of parsed) {\r\n\t\t\tif (turnInfo.turn === 0 || turnInfo.totalStats == null) {\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tconst existingInfo = data['' + turnInfo.turn] ?? { dataPoints: 0, totalStats: 0 };\r\n\t\t\texistingInfo.dataPoints = existingInfo.dataPoints + 1;\r\n\t\t\texistingInfo.totalStats = existingInfo.totalStats + Math.round(turnInfo.totalStats);\r\n\t\t\tdata['' + turnInfo.turn] = existingInfo;\r\n\t\t}\r\n\t}\r\n\r\n\tconst result: { turn: number; dataPoints: number; totalStats: number }[] = Object.keys(data).map(turn => ({\r\n\t\tturn: +turn,\r\n\t\tdataPoints: data[turn].dataPoints,\r\n\t\ttotalStats: data[turn].totalStats,\r\n\t}));\r\n\treturn result;\r\n};\r\n\r\nconst buildCombatWinrate = (\r\n\trows: readonly InternalBgsRow[],\r\n): readonly { turn: number; dataPoints: number; totalWinrate: number }[] => {\r\n\tconst ref = rows[0];\r\n\tconst debug = ref.heroCardId === 'BG21_HERO_000';\r\n\r\n\tconst data: { [turn: string]: { dataPoints: number; totalWinrate: number } } = {};\r\n\tfor (const row of rows) {\r\n\t\t// console.log('building combatWinrate', row);\r\n\t\tif (!row.combatWinrate?.length) {\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tlet parsed: readonly { turn: number; winrate: number }[] = null;\r\n\t\ttry {\r\n\t\t\tparsed = JSON.parse(row.combatWinrate);\r\n\t\t\t// console.log('parsed', parsed);\r\n\t\t\tif (!parsed?.length) {\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t} catch (e) {\r\n\t\t\tconsole.error('Could not parse combat winrate', row.id, e);\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tif (debug) {\r\n\t\t\t// console.log('handling combat winrate', parsed);\r\n\t\t}\r\n\r\n\t\tfor (const turnInfo of parsed) {\r\n\t\t\tif (turnInfo.turn === 0 || turnInfo.winrate == null) {\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\tif (debug) {\r\n\t\t\t\t// console.log('\\t turnInfo', turnInfo);\r\n\t\t\t}\r\n\t\t\tconst existingInfo = data['' + turnInfo.turn] ?? { dataPoints: 0, totalWinrate: 0 };\r\n\t\t\tif (debug) {\r\n\t\t\t\t// console.log('\\t existingInfo', existingInfo);\r\n\t\t\t}\r\n\t\t\texistingInfo.dataPoints = existingInfo.dataPoints + 1;\r\n\t\t\texistingInfo.totalWinrate = existingInfo.totalWinrate + Math.round(turnInfo.winrate);\r\n\t\t\tif (debug) {\r\n\t\t\t\t// console.log('\\t existingInfo after', existingInfo);\r\n\t\t\t}\r\n\t\t\tdata['' + turnInfo.turn] = existingInfo;\r\n\t\t\tif (debug) {\r\n\t\t\t\t// console.log('\\t data', data);\r\n\t\t\t}\r\n\t\t}\r\n\t\t// console.log('existingInfo', existingInfo);\r\n\t}\r\n\r\n\tif (debug) {\r\n\t\t// console.log('\\t data', data);\r\n\t}\r\n\tconst result: { turn: number; dataPoints: number; totalWinrate: number }[] = Object.keys(data).map(turn => ({\r\n\t\tturn: +turn,\r\n\t\tdataPoints: data[turn].dataPoints,\r\n\t\ttotalWinrate: data[turn].totalWinrate,\r\n\t}));\r\n\tif (debug) {\r\n\t\t// console.log('\\t result', result);\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst buildPlacementDistribution = (\r\n\trows: readonly InternalBgsRow[],\r\n): readonly { rank: number; totalMatches: number }[] => {\r\n\tconst placementDistribution: { rank: number; totalMatches: number }[] = [];\r\n\tconst groupedByPlacement: { [placement: string]: readonly InternalBgsRow[] } = groupByFunction(\r\n\t\t(res: InternalBgsRow) => '' + res.rank,\r\n\t)(rows);\r\n\tObject.keys(groupedByPlacement).forEach(placement =>\r\n\t\tplacementDistribution.push({ rank: +placement, totalMatches: groupedByPlacement[placement].length }),\r\n\t);\r\n\treturn placementDistribution;\r\n};\r\n\r\nconst loadRows = async (mysql: ServerlessMysql, patch: PatchInfo): Promise<readonly InternalBgsRow[]> => {\r\n\tconsole.log('loading rows', patch);\r\n\tconst query = `\r\n\t\tSELECT * FROM bgs_run_stats\r\n\t\tWHERE creationDate > DATE_SUB(NOW(), INTERVAL 30 DAY)\r\n\t`;\r\n\tconsole.log('running query', query);\r\n\tconst rows: readonly InternalBgsRow[] = await mysql.query(query);\r\n\tconsole.log('rows', rows?.length, rows[0]);\r\n\treturn rows\r\n\t\t.filter(row => row.heroCardId.startsWith('TB_BaconShop_') || row.heroCardId.startsWith('BG'))\r\n\t\t.filter(row => row.heroCardId !== CardIds.ArannaStarseeker_ArannaUnleashedTokenBattlegrounds);\r\n};\r\n\r\nconst getLastBattlegroundsPatch = async (): Promise<PatchInfo> => {\r\n\tconst patchInfo = await http(`https://static.zerotoheroes.com/hearthstone/data/patches.json`);\r\n\tconst structuredPatch = JSON.parse(patchInfo);\r\n\tconst patchNumber = structuredPatch.currentBattlegroundsMetaPatch;\r\n\treturn structuredPatch.patches.find(patch => patch.number === patchNumber);\r\n};\r\n\r\nconst buildMmrPercentiles = (rows: readonly InternalBgsRow[]): readonly MmrPercentile[] => {\r\n\tconst sortedMmrs = rows.map(row => row.rating).sort((a, b) => a - b);\r\n\tconst median = sortedMmrs[Math.floor(sortedMmrs.length / 2)];\r\n\tconst top25 = sortedMmrs[Math.floor((sortedMmrs.length / 4) * 3)];\r\n\tconst top10 = sortedMmrs[Math.floor((sortedMmrs.length / 10) * 9)];\r\n\t// const top1 = sortedMmrs[Math.floor((sortedMmrs.length / 100) * 99)];\r\n\t// console.debug('percentiles', median, top25, top10, top1);\r\n\treturn [\r\n\t\t{\r\n\t\t\tpercentile: 100,\r\n\t\t\tmmr: 0,\r\n\t\t},\r\n\t\t{\r\n\t\t\tpercentile: 50,\r\n\t\t\tmmr: median,\r\n\t\t},\r\n\t\t{\r\n\t\t\tpercentile: 25,\r\n\t\t\tmmr: top25,\r\n\t\t},\r\n\t\t{\r\n\t\t\tpercentile: 10,\r\n\t\t\tmmr: top10,\r\n\t\t},\r\n\t\t// {\r\n\t\t// \tpercentile: 1,\r\n\t\t// \tmmr: top1,\r\n\t\t// },\r\n\t];\r\n};\r\n\r\ninterface InternalBgsRow {\r\n\treadonly id: number;\r\n\treadonly creationDate: Date;\r\n\treadonly buildNumber: number;\r\n\treadonly rating: number;\r\n\treadonly heroCardId: string;\r\n\treadonly rank: number;\r\n\treadonly reviewId: string;\r\n\treadonly tribes: string;\r\n\treadonly darkmoonPrizes: boolean;\r\n\treadonly combatWinrate: string;\r\n\treadonly warbandStats: string;\r\n}\r\n\r\ninterface PatchInfo {\r\n\treadonly number: number;\r\n\treadonly version: string;\r\n\treadonly name: string;\r\n\treadonly date: string;\r\n}\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firestone-hs/bgs-global-stats",
3
- "version": "1.0.21",
3
+ "version": "1.0.25",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "lint": "eslint --color --fix --ext .ts .",
@@ -25,15 +25,15 @@
25
25
  ],
26
26
  "dependencies": {
27
27
  "@firestone-hs/hs-replay-xml-parser": "0.0.78",
28
- "@firestone-hs/reference-data": "^0.1.175",
28
+ "@firestone-hs/reference-data": "^0.1.197",
29
29
  "@types/elementtree": "^0.1.0",
30
- "aws-sdk": "^2.524.0",
30
+ "aws-sdk": "^2.1040.0",
31
31
  "elementtree": "^0.1.7",
32
32
  "immutable": "3.8.2",
33
33
  "jszip": "^3.7.1",
34
34
  "lodash-es": "4.17.15",
35
35
  "mysql": "^2.17.1",
36
- "node-fetch": "^2.6.1",
36
+ "node-fetch": "^2.6.7",
37
37
  "sax": "1.2.4",
38
38
  "serverless-mysql": "^1.5.1",
39
39
  "tslib": "^1.9.0"
@@ -1,2 +0,0 @@
1
- declare const _default: (event: any) => Promise<any>;
2
- export default _default;
@@ -1,163 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- const reference_data_1 = require("@firestone-hs/reference-data");
13
- const zlib_1 = require("zlib");
14
- const build_battlegrounds_hero_stats_new_1 = require("./build-battlegrounds-hero-stats-new");
15
- const rds_1 = require("./db/rds");
16
- const rds_bgs_1 = require("./db/rds-bgs");
17
- const s3_1 = require("./db/s3");
18
- const retrieve_bgs_global_stats_1 = require("./retrieve-bgs-global-stats");
19
- const util_functions_1 = require("./utils/util-functions");
20
- const s3 = new s3_1.S3();
21
- const allCards = new reference_data_1.AllCardsService();
22
- exports.default = (event) => __awaiter(void 0, void 0, void 0, function* () {
23
- yield allCards.initializeCardsDb();
24
- const lastBattlegroundsPatch = yield getLastBattlegroundsPatch();
25
- const mysql = yield rds_1.getConnection();
26
- const mysqlBgs = yield rds_bgs_1.getConnection();
27
- yield updateAggregatedStats(mysqlBgs, mysql, lastBattlegroundsPatch, allCards);
28
- yield updateLastPeriodStats(mysqlBgs, mysql, lastBattlegroundsPatch, allCards);
29
- const stats = yield retrieve_bgs_global_stats_1.loadStats(mysql, mysqlBgs);
30
- const stringResults = JSON.stringify(stats);
31
- const gzippedResults = zlib_1.gzipSync(stringResults);
32
- yield s3.writeFile(gzippedResults, 'static.zerotoheroes.com', 'api/bgs-global-stats.json', 'application/json', 'gzip');
33
- yield mysqlBgs.end();
34
- yield mysql.end();
35
- yield build_battlegrounds_hero_stats_new_1.handleNewStats();
36
- return { statusCode: 200, body: null };
37
- });
38
- const getLastBattlegroundsPatch = () => __awaiter(void 0, void 0, void 0, function* () {
39
- const patchInfo = yield util_functions_1.http(`https://static.zerotoheroes.com/hearthstone/data/patches.json`);
40
- const structuredPatch = JSON.parse(patchInfo);
41
- return structuredPatch.currentBattlegroundsMetaPatch;
42
- });
43
- const updateAggregatedStats = (mysqlBgs, mysqlStats, buildNumber, allCards) => __awaiter(void 0, void 0, void 0, function* () {
44
- const now = Date.now();
45
- const earliestStartDate = new Date(now - 10 * 24 * 60 * 60 * 1000).toISOString();
46
- yield updateStats(mysqlBgs, mysqlStats, earliestStartDate, buildNumber, false, allCards);
47
- });
48
- const updateLastPeriodStats = (mysqlBgs, mysqlStats, buildNumber, allCards) => __awaiter(void 0, void 0, void 0, function* () {
49
- const now = Date.now();
50
- const earliestStartDate = new Date(now - 24 * 60 * 60 * 1000).toISOString();
51
- yield updateStats(mysqlBgs, mysqlStats, earliestStartDate, buildNumber, true, allCards);
52
- });
53
- const updateStats = (mysqlBgs, mysqlStats, creationDate, buildNumber, insertCreationDate, allCards) => __awaiter(void 0, void 0, void 0, function* () {
54
- const allHeroesQuery = `
55
- SELECT distinct playerCardId
56
- FROM replay_summary
57
- WHERE gameMode = 'battlegrounds'
58
- AND (playerCardId like 'TB_BaconShop_Hero%' OR playerCardId like 'BG%')
59
- AND buildNumber >= ${buildNumber}
60
- ${creationDate ? "AND creationDate > '" + creationDate + "'" : ''}
61
- `;
62
- const allHeroesResult = yield mysqlStats.query(allHeroesQuery);
63
- const allHeroes = allHeroesResult
64
- .filter(playerCardId => playerCardId !== 'TB_BaconShop_HERO_59t')
65
- .map(result => util_functions_1.normalizeHeroCardId(result.playerCardId, allCards));
66
- const heroStatsQuery = `
67
- SELECT playerCardId, additionalResult, count(*) as count, max(creationDate) as lastPlayedDate
68
- FROM replay_summary
69
- WHERE gameMode = 'battlegrounds'
70
- AND (playerCardId like 'TB_BaconShop_Hero%' OR playerCardId like 'BG%')
71
- AND playerRank >= 4000
72
- AND buildNumber >= ${buildNumber}
73
- ${creationDate ? "AND creationDate > '" + creationDate + "'" : ''}
74
- GROUP BY playerCardId, additionalResult
75
- `;
76
- const heroStatsResults = (yield mysqlStats.query(heroStatsQuery))
77
- .filter(result => result.playerCardId !== 'TB_BaconShop_HERO_59t')
78
- .map(result => (Object.assign(Object.assign({}, result), { playerCardId: util_functions_1.normalizeHeroCardId(result.playerCardId, allCards), additionalResult: parseInt(result.additionalResult) })))
79
- .filter(result => result.additionalResult > 0);
80
- const stats = allHeroes.map(heroCardId => buildHeroInfo(heroCardId, heroStatsResults));
81
- const now = new Date().toISOString();
82
- if (insertCreationDate) {
83
- const values = stats
84
- .map(stat => `('${stat.id}', ${insertCreationDate ? "'" + now + "'" : null}, ${stat.popularity},
85
- ${stat.averagePosition}, ${stat.top4}, ${stat.top1}, '${stat.tier}', ${stat.totalGames})`)
86
- .join(',');
87
- const insertQuery = `
88
- INSERT INTO bgs_hero_stats
89
- (heroCardId, date, popularity, averagePosition, top4, top1, tier, totalGames)
90
- VALUES ${values}
91
- `;
92
- const updateResult = yield mysqlBgs.query(insertQuery);
93
- }
94
- else {
95
- for (const stat of stats) {
96
- const updateQuery = `
97
- UPDATE bgs_hero_stats
98
- SET
99
- popularity = ${stat.popularity},
100
- averagePosition = ${stat.averagePosition},
101
- top4 = ${stat.top4},
102
- top1 = ${stat.top1},
103
- tier = '${stat.tier}',
104
- totalGames = ${stat.totalGames}
105
- WHERE heroCardId = '${stat.id}' AND date is NULL
106
- `;
107
- const updateResult = yield mysqlBgs.query(updateQuery);
108
- if (updateResult.affectedRows === 0) {
109
- const insertQuery = `
110
- INSERT INTO bgs_hero_stats
111
- (heroCardId, date, popularity, averagePosition, top4, top1, tier, totalGames)
112
- VALUES ('${stat.id}', NULL, ${stat.popularity}, ${stat.averagePosition}, ${stat.top4}, ${stat.top1}, '${stat.tier}', ${stat.totalGames})
113
- `;
114
- const insertResult = yield mysqlBgs.query(insertQuery);
115
- }
116
- }
117
- }
118
- return { statusCode: 200, body: null };
119
- });
120
- const buildHeroInfo = (heroCardId, heroStatsResults) => {
121
- const total = heroStatsResults.map(result => result.count).reduce((a, b) => a + b, 0);
122
- const heroStatsForHero = heroStatsResults.filter(result => result.playerCardId === heroCardId);
123
- const totalGamesPlayedForHero = heroStatsForHero.map(result => result.count).reduce((a, b) => a + b, 0) || 0;
124
- const averagePosition = (heroStatsForHero.map(result => result.additionalResult * result.count).reduce((a, b) => a + b, 0) || 0) /
125
- totalGamesPlayedForHero;
126
- const top4Percentage = (100 *
127
- (heroStatsForHero
128
- .filter(result => result.additionalResult <= 4)
129
- .map(result => result.count)
130
- .reduce((a, b) => a + b, 0) || 0)) /
131
- totalGamesPlayedForHero;
132
- const top1Percentage = (100 *
133
- (heroStatsForHero
134
- .filter(result => result.additionalResult === 1)
135
- .map(result => result.count)
136
- .reduce((a, b) => a + b, 0) || 0)) /
137
- totalGamesPlayedForHero;
138
- return {
139
- id: heroCardId,
140
- popularity: (100 * totalGamesPlayedForHero) / total,
141
- averagePosition: averagePosition,
142
- top4: top4Percentage,
143
- top1: top1Percentage,
144
- tier: getTier(averagePosition),
145
- totalGames: totalGamesPlayedForHero,
146
- };
147
- };
148
- const getTier = (averagePosition) => {
149
- if (averagePosition < 3.7) {
150
- return 'S';
151
- }
152
- else if (averagePosition < 4.1) {
153
- return 'A';
154
- }
155
- else if (averagePosition < 4.4) {
156
- return 'B';
157
- }
158
- else if (averagePosition < 4.7) {
159
- return 'C';
160
- }
161
- return 'D';
162
- };
163
- //# sourceMappingURL=build-battlegrounds-hero-stats.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"build-battlegrounds-hero-stats.js","sourceRoot":"/","sources":["build-battlegrounds-hero-stats.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/no-use-before-define */\r\nimport { AllCardsService } from '@firestone-hs/reference-data';\r\nimport { ServerlessMysql } from 'serverless-mysql';\r\nimport { gzipSync } from 'zlib';\r\nimport { BgsGlobalHeroStat, BgsHeroTier } from './bgs-global-stats';\r\nimport { handleNewStats } from './build-battlegrounds-hero-stats-new';\r\nimport { getConnection as getConnectionStats } from './db/rds';\r\nimport { getConnection as getConnectionBgs } from './db/rds-bgs';\r\nimport { S3 } from './db/s3';\r\nimport { loadStats } from './retrieve-bgs-global-stats';\r\nimport { http, normalizeHeroCardId } from './utils/util-functions';\r\n\r\nconst s3 = new S3();\r\nconst allCards = new AllCardsService();\r\n\r\n// This example demonstrates a NodeJS 8.10 async handler[1], however of course you could use\r\n// the more traditional callback-style handler.\r\n// [1]: https://aws.amazon.com/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/\r\nexport default async (event): Promise<any> => {\r\n\tawait allCards.initializeCardsDb();\r\n\tconst lastBattlegroundsPatch = await getLastBattlegroundsPatch();\r\n\r\n\tconst mysql = await getConnectionStats();\r\n\tconst mysqlBgs = await getConnectionBgs();\r\n\r\n\tawait updateAggregatedStats(mysqlBgs, mysql, lastBattlegroundsPatch, allCards);\r\n\tawait updateLastPeriodStats(mysqlBgs, mysql, lastBattlegroundsPatch, allCards);\r\n\r\n\tconst stats = await loadStats(mysql, mysqlBgs);\r\n\tconst stringResults = JSON.stringify(stats);\r\n\tconst gzippedResults = gzipSync(stringResults);\r\n\tawait s3.writeFile(\r\n\t\tgzippedResults,\r\n\t\t'static.zerotoheroes.com',\r\n\t\t'api/bgs-global-stats.json',\r\n\t\t'application/json',\r\n\t\t'gzip',\r\n\t);\r\n\r\n\tawait mysqlBgs.end();\r\n\tawait mysql.end();\r\n\r\n\tawait handleNewStats();\r\n\r\n\treturn { statusCode: 200, body: null };\r\n};\r\n\r\nconst getLastBattlegroundsPatch = async (): Promise<number> => {\r\n\tconst patchInfo = await http(`https://static.zerotoheroes.com/hearthstone/data/patches.json`);\r\n\tconst structuredPatch = JSON.parse(patchInfo);\r\n\treturn structuredPatch.currentBattlegroundsMetaPatch;\r\n};\r\n\r\nconst updateAggregatedStats = async (\r\n\tmysqlBgs: ServerlessMysql,\r\n\tmysqlStats: ServerlessMysql,\r\n\tbuildNumber: number,\r\n\tallCards: AllCardsService,\r\n) => {\r\n\t// This won't be fully accurate, as not all update will be installed simulatenously, but it's good enough\r\n\tconst now = Date.now();\r\n\tconst earliestStartDate = new Date(now - 10 * 24 * 60 * 60 * 1000).toISOString();\r\n\tawait updateStats(mysqlBgs, mysqlStats, earliestStartDate, buildNumber, false, allCards);\r\n};\r\n\r\n// TODO: remove this lastperiod stuff, add a new column with the last update Date, and do a query on that last update date\r\nconst updateLastPeriodStats = async (\r\n\tmysqlBgs: ServerlessMysql,\r\n\tmysqlStats: ServerlessMysql,\r\n\tbuildNumber: number,\r\n\tallCards: AllCardsService,\r\n) => {\r\n\t// Get all the reviews from the last day\r\n\tconst now = Date.now();\r\n\tconst earliestStartDate = new Date(now - 24 * 60 * 60 * 1000).toISOString();\r\n\tawait updateStats(mysqlBgs, mysqlStats, earliestStartDate, buildNumber, true, allCards);\r\n};\r\n\r\nconst updateStats = async (\r\n\tmysqlBgs: ServerlessMysql,\r\n\tmysqlStats: ServerlessMysql,\r\n\tcreationDate: string,\r\n\tbuildNumber: number,\r\n\tinsertCreationDate: boolean,\r\n\tallCards: AllCardsService,\r\n) => {\r\n\tconst allHeroesQuery = `\r\n\t\tSELECT distinct playerCardId\r\n\t\tFROM replay_summary\r\n\t\tWHERE gameMode = 'battlegrounds'\r\n\t\tAND (playerCardId like 'TB_BaconShop_Hero%' OR playerCardId like 'BG%')\r\n\t\tAND buildNumber >= ${buildNumber}\r\n\t\t${creationDate ? \"AND creationDate > '\" + creationDate + \"'\" : ''}\r\n\t`;\r\n\tconst allHeroesResult: readonly any[] = await mysqlStats.query(allHeroesQuery);\r\n\tconst allHeroes: readonly string[] = allHeroesResult\r\n\t\t.filter(playerCardId => playerCardId !== 'TB_BaconShop_HERO_59t')\r\n\t\t.map(result => normalizeHeroCardId(result.playerCardId, allCards));\r\n\r\n\tconst heroStatsQuery = `\r\n\t\tSELECT playerCardId, additionalResult, count(*) as count, max(creationDate) as lastPlayedDate\r\n\t\tFROM replay_summary\r\n\t\tWHERE gameMode = 'battlegrounds'\r\n\t\tAND (playerCardId like 'TB_BaconShop_Hero%' OR playerCardId like 'BG%')\r\n\t\tAND playerRank >= 4000\r\n\t\tAND buildNumber >= ${buildNumber}\r\n\t\t${creationDate ? \"AND creationDate > '\" + creationDate + \"'\" : ''}\r\n\t\tGROUP BY playerCardId, additionalResult\r\n\t`;\r\n\tconst heroStatsResults: readonly any[] = ((await mysqlStats.query(heroStatsQuery)) as any[])\r\n\t\t.filter(result => result.playerCardId !== 'TB_BaconShop_HERO_59t')\r\n\t\t.map(result => ({\r\n\t\t\t...result,\r\n\t\t\tplayerCardId: normalizeHeroCardId(result.playerCardId, allCards),\r\n\t\t\tadditionalResult: parseInt(result.additionalResult),\r\n\t\t}))\r\n\t\t.filter(result => result.additionalResult > 0);\r\n\r\n\tconst stats: BgsGlobalHeroStat[] = allHeroes.map(heroCardId => buildHeroInfo(heroCardId, heroStatsResults));\r\n\r\n\tconst now = new Date().toISOString();\r\n\tif (insertCreationDate) {\r\n\t\tconst values = stats\r\n\t\t\t.map(\r\n\t\t\t\tstat =>\r\n\t\t\t\t\t`('${stat.id}', ${insertCreationDate ? \"'\" + now + \"'\" : null}, ${stat.popularity}, \r\n\t\t\t\t\t${stat.averagePosition}, ${stat.top4}, ${stat.top1}, '${stat.tier}', ${stat.totalGames})`,\r\n\t\t\t)\r\n\t\t\t.join(',');\r\n\t\tconst insertQuery = `\r\n\t\t\t\tINSERT INTO bgs_hero_stats\r\n\t\t\t\t(heroCardId, date, popularity, averagePosition, top4, top1, tier, totalGames)\r\n\t\t\t\tVALUES ${values}\r\n\t\t\t`;\r\n\t\tconst updateResult = await mysqlBgs.query(insertQuery);\r\n\t}\r\n\t// Here the assumption is that we have run the INSERT once, and now we just update the data\r\n\t// NULL date means aggregated data from the latest period. Maybe at one point we'll need\r\n\t// to support multiple aggregated data, but for now this is enough\r\n\telse {\r\n\t\tfor (const stat of stats) {\r\n\t\t\tconst updateQuery = `\r\n\t\t\t\t\tUPDATE bgs_hero_stats\r\n\t\t\t\t\tSET \r\n\t\t\t\t\t\tpopularity = ${stat.popularity}, \r\n\t\t\t\t\t\taveragePosition = ${stat.averagePosition},\r\n\t\t\t\t\t\ttop4 = ${stat.top4},\r\n\t\t\t\t\t\ttop1 = ${stat.top1},\r\n\t\t\t\t\t\ttier = '${stat.tier}',\r\n\t\t\t\t\t\ttotalGames = ${stat.totalGames}\r\n\t\t\t\t\tWHERE heroCardId = '${stat.id}' AND date is NULL\r\n\t\t\t\t`;\r\n\t\t\tconst updateResult: any = await mysqlBgs.query(updateQuery);\r\n\t\t\t// Non-existing data\r\n\t\t\tif (updateResult.affectedRows === 0) {\r\n\t\t\t\tconst insertQuery = `\r\n\t\t\t\t\tINSERT INTO bgs_hero_stats\r\n\t\t\t\t\t(heroCardId, date, popularity, averagePosition, top4, top1, tier, totalGames)\r\n\t\t\t\t\tVALUES ('${stat.id}', NULL, ${stat.popularity}, ${stat.averagePosition}, ${stat.top4}, ${stat.top1}, '${stat.tier}', ${stat.totalGames})\r\n\t\t\t\t`;\r\n\t\t\t\tconst insertResult = await mysqlBgs.query(insertQuery);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\treturn { statusCode: 200, body: null };\r\n};\r\n\r\nconst buildHeroInfo = (heroCardId: string, heroStatsResults: readonly any[]): BgsGlobalHeroStat => {\r\n\tconst total = heroStatsResults.map(result => result.count).reduce((a, b) => a + b, 0);\r\n\tconst heroStatsForHero = heroStatsResults.filter(result => result.playerCardId === heroCardId);\r\n\tconst totalGamesPlayedForHero = heroStatsForHero.map(result => result.count).reduce((a, b) => a + b, 0) || 0;\r\n\tconst averagePosition =\r\n\t\t(heroStatsForHero.map(result => result.additionalResult * result.count).reduce((a, b) => a + b, 0) || 0) /\r\n\t\ttotalGamesPlayedForHero;\r\n\tconst top4Percentage =\r\n\t\t(100 *\r\n\t\t\t(heroStatsForHero\r\n\t\t\t\t.filter(result => result.additionalResult <= 4)\r\n\t\t\t\t.map(result => result.count)\r\n\t\t\t\t.reduce((a, b) => a + b, 0) || 0)) /\r\n\t\ttotalGamesPlayedForHero;\r\n\tconst top1Percentage =\r\n\t\t(100 *\r\n\t\t\t(heroStatsForHero\r\n\t\t\t\t.filter(result => result.additionalResult === 1)\r\n\t\t\t\t.map(result => result.count)\r\n\t\t\t\t.reduce((a, b) => a + b, 0) || 0)) /\r\n\t\ttotalGamesPlayedForHero;\r\n\treturn {\r\n\t\tid: heroCardId,\r\n\t\tpopularity: (100 * totalGamesPlayedForHero) / total,\r\n\t\taveragePosition: averagePosition,\r\n\t\ttop4: top4Percentage,\r\n\t\ttop1: top1Percentage,\r\n\t\ttier: getTier(averagePosition),\r\n\t\ttotalGames: totalGamesPlayedForHero,\r\n\t} as BgsGlobalHeroStat;\r\n};\r\n\r\nconst getTier = (averagePosition: number): BgsHeroTier => {\r\n\tif (averagePosition < 3.7) {\r\n\t\treturn 'S';\r\n\t} else if (averagePosition < 4.1) {\r\n\t\treturn 'A';\r\n\t} else if (averagePosition < 4.4) {\r\n\t\treturn 'B';\r\n\t} else if (averagePosition < 4.7) {\r\n\t\treturn 'C';\r\n\t}\r\n\treturn 'D';\r\n};\r\n"]}