@firestone-hs/bgs-global-stats 1.0.41 → 1.0.43
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.
- package/dist/duos/aggregate-hourly/config.js.map +1 -0
- package/dist/duos/aggregate-hourly/heroes/_build-aggregated-stats.js.map +1 -0
- package/dist/{aggregate-hourly → duos/aggregate-hourly}/heroes/s3-saver.d.ts +1 -1
- package/dist/duos/aggregate-hourly/heroes/s3-saver.js.map +1 -0
- package/dist/{aggregate-hourly → duos/aggregate-hourly}/heroes/stats-merger.d.ts +1 -1
- package/dist/duos/aggregate-hourly/heroes/stats-merger.js +140 -0
- package/dist/duos/aggregate-hourly/heroes/stats-merger.js.map +1 -0
- package/dist/{aggregate-hourly → duos/aggregate-hourly}/hourly-utils.d.ts +1 -1
- package/dist/duos/aggregate-hourly/hourly-utils.js.map +1 -0
- package/dist/{aggregate-hourly → duos/aggregate-hourly}/percentiles.d.ts +2 -2
- package/dist/duos/aggregate-hourly/percentiles.js.map +1 -0
- package/dist/{aggregate-hourly → duos/aggregate-hourly}/s3-loader.d.ts +2 -2
- package/dist/duos/aggregate-hourly/s3-loader.js.map +1 -0
- package/dist/{hourly → duos/hourly}/_build-battlegrounds-hero-stats.d.ts +1 -1
- package/dist/{hourly → duos/hourly}/_build-battlegrounds-hero-stats.js +2 -31
- package/dist/duos/hourly/_build-battlegrounds-hero-stats.js.map +1 -0
- package/dist/{hourly → duos/hourly}/builders.d.ts +1 -1
- package/dist/duos/hourly/builders.js.map +1 -0
- package/dist/{hourly → duos/hourly}/hero-stats-buikder.d.ts +2 -2
- package/dist/{hourly → duos/hourly}/hero-stats-buikder.js +6 -3
- package/dist/duos/hourly/hero-stats-buikder.js.map +1 -0
- package/dist/{hourly → duos/hourly}/hero-stats.d.ts +1 -1
- package/dist/duos/hourly/hero-stats.js.map +1 -0
- package/dist/{hourly → duos/hourly}/rows.d.ts +1 -1
- package/dist/duos/hourly/rows.js +175 -0
- package/dist/duos/hourly/rows.js.map +1 -0
- package/dist/{hourly → duos/hourly}/utils.d.ts +2 -2
- package/dist/duos/hourly/utils.js.map +1 -0
- package/dist/models.d.ts +2 -0
- package/dist/models.js.map +1 -1
- package/dist/solo/aggregate-hourly/config.d.ts +2 -0
- package/dist/solo/aggregate-hourly/config.js +7 -0
- package/dist/solo/aggregate-hourly/config.js.map +1 -0
- package/dist/solo/aggregate-hourly/heroes/_build-aggregated-stats.js +69 -0
- package/dist/solo/aggregate-hourly/heroes/_build-aggregated-stats.js.map +1 -0
- package/dist/solo/aggregate-hourly/heroes/s3-saver.d.ts +2 -0
- package/dist/solo/aggregate-hourly/heroes/s3-saver.js +22 -0
- package/dist/solo/aggregate-hourly/heroes/s3-saver.js.map +1 -0
- package/dist/solo/aggregate-hourly/heroes/stats-merger.d.ts +3 -0
- package/dist/{aggregate-hourly → solo/aggregate-hourly}/heroes/stats-merger.js +9 -2
- package/dist/solo/aggregate-hourly/heroes/stats-merger.js.map +1 -0
- package/dist/solo/aggregate-hourly/hourly-utils.d.ts +4 -0
- package/dist/solo/aggregate-hourly/hourly-utils.js +33 -0
- package/dist/solo/aggregate-hourly/hourly-utils.js.map +1 -0
- package/dist/solo/aggregate-hourly/percentiles.d.ts +3 -0
- package/dist/solo/aggregate-hourly/percentiles.js +8 -0
- package/dist/solo/aggregate-hourly/percentiles.js.map +1 -0
- package/dist/solo/aggregate-hourly/quests/_build-aggregated-stats.d.ts +5 -0
- package/dist/solo/aggregate-hourly/quests/_build-aggregated-stats.js.map +1 -0
- package/dist/{aggregate-hourly → solo/aggregate-hourly}/quests/quest-stats-merger.d.ts +2 -2
- package/dist/solo/aggregate-hourly/quests/quest-stats-merger.js.map +1 -0
- package/dist/{aggregate-hourly → solo/aggregate-hourly}/quests/reward-stats-merger.d.ts +2 -2
- package/dist/solo/aggregate-hourly/quests/reward-stats-merger.js.map +1 -0
- package/dist/{aggregate-hourly → solo/aggregate-hourly}/quests/s3-saver.d.ts +2 -2
- package/dist/solo/aggregate-hourly/quests/s3-saver.js.map +1 -0
- package/dist/solo/aggregate-hourly/s3-loader.d.ts +6 -0
- package/dist/solo/aggregate-hourly/s3-loader.js +21 -0
- package/dist/solo/aggregate-hourly/s3-loader.js.map +1 -0
- package/dist/solo/hourly/_build-battlegrounds-hero-stats.d.ts +11 -0
- package/dist/solo/hourly/_build-battlegrounds-hero-stats.js +144 -0
- package/dist/solo/hourly/_build-battlegrounds-hero-stats.js.map +1 -0
- package/dist/solo/hourly/builders.d.ts +11 -0
- package/dist/solo/hourly/builders.js +81 -0
- package/dist/solo/hourly/builders.js.map +1 -0
- package/dist/solo/hourly/hero-stats-buikder.d.ts +4 -0
- package/dist/solo/hourly/hero-stats-buikder.js +64 -0
- package/dist/solo/hourly/hero-stats-buikder.js.map +1 -0
- package/dist/solo/hourly/hero-stats.d.ts +3 -0
- package/dist/solo/hourly/hero-stats.js +27 -0
- package/dist/solo/hourly/hero-stats.js.map +1 -0
- package/dist/{hourly → solo/hourly}/quest-stats.d.ts +1 -1
- package/dist/solo/hourly/quest-stats.js.map +1 -0
- package/dist/{hourly → solo/hourly}/quests/quest-stats-buikder.d.ts +2 -2
- package/dist/solo/hourly/quests/quest-stats-buikder.js.map +1 -0
- package/dist/{hourly → solo/hourly}/quests/reward-stats-builder.d.ts +2 -2
- package/dist/solo/hourly/quests/reward-stats-builder.js.map +1 -0
- package/dist/solo/hourly/rows.d.ts +4 -0
- package/dist/{hourly → solo/hourly}/rows.js +1 -1
- package/dist/solo/hourly/rows.js.map +1 -0
- package/dist/solo/hourly/utils.d.ts +9 -0
- package/dist/solo/hourly/utils.js +56 -0
- package/dist/solo/hourly/utils.js.map +1 -0
- package/package.json +1 -1
- package/dist/aggregate-hourly/config.js.map +0 -1
- package/dist/aggregate-hourly/heroes/_build-aggregated-stats.js.map +0 -1
- package/dist/aggregate-hourly/heroes/s3-saver.js.map +0 -1
- package/dist/aggregate-hourly/heroes/stats-merger.js.map +0 -1
- package/dist/aggregate-hourly/hourly-utils.js.map +0 -1
- package/dist/aggregate-hourly/percentiles.js.map +0 -1
- package/dist/aggregate-hourly/quests/_build-aggregated-stats.js.map +0 -1
- package/dist/aggregate-hourly/quests/quest-stats-merger.js.map +0 -1
- package/dist/aggregate-hourly/quests/reward-stats-merger.js.map +0 -1
- package/dist/aggregate-hourly/quests/s3-saver.js.map +0 -1
- package/dist/aggregate-hourly/s3-loader.js.map +0 -1
- package/dist/hourly/_build-battlegrounds-hero-stats.js.map +0 -1
- package/dist/hourly/builders.js.map +0 -1
- package/dist/hourly/hero-stats-buikder.js.map +0 -1
- package/dist/hourly/hero-stats.js.map +0 -1
- package/dist/hourly/quest-stats.js.map +0 -1
- package/dist/hourly/quests/quest-stats-buikder.js.map +0 -1
- package/dist/hourly/quests/reward-stats-builder.js.map +0 -1
- package/dist/hourly/rows.js.map +0 -1
- package/dist/hourly/utils.js.map +0 -1
- /package/dist/{aggregate-hourly → duos/aggregate-hourly}/config.d.ts +0 -0
- /package/dist/{aggregate-hourly → duos/aggregate-hourly}/config.js +0 -0
- /package/dist/{aggregate-hourly → duos/aggregate-hourly}/heroes/_build-aggregated-stats.d.ts +0 -0
- /package/dist/{aggregate-hourly → duos/aggregate-hourly}/heroes/_build-aggregated-stats.js +0 -0
- /package/dist/{aggregate-hourly → duos/aggregate-hourly}/heroes/s3-saver.js +0 -0
- /package/dist/{aggregate-hourly → duos/aggregate-hourly}/hourly-utils.js +0 -0
- /package/dist/{aggregate-hourly → duos/aggregate-hourly}/percentiles.js +0 -0
- /package/dist/{aggregate-hourly → duos/aggregate-hourly}/s3-loader.js +0 -0
- /package/dist/{hourly → duos/hourly}/builders.js +0 -0
- /package/dist/{hourly → duos/hourly}/hero-stats.js +0 -0
- /package/dist/{hourly → duos/hourly}/utils.js +0 -0
- /package/dist/{aggregate-hourly/quests → solo/aggregate-hourly/heroes}/_build-aggregated-stats.d.ts +0 -0
- /package/dist/{aggregate-hourly → solo/aggregate-hourly}/quests/_build-aggregated-stats.js +0 -0
- /package/dist/{aggregate-hourly → solo/aggregate-hourly}/quests/quest-stats-merger.js +0 -0
- /package/dist/{aggregate-hourly → solo/aggregate-hourly}/quests/reward-stats-merger.js +0 -0
- /package/dist/{aggregate-hourly → solo/aggregate-hourly}/quests/s3-saver.js +0 -0
- /package/dist/{hourly → solo/hourly}/quest-stats.js +0 -0
- /package/dist/{hourly → solo/hourly}/quests/quest-stats-buikder.js +0 -0
- /package/dist/{hourly → solo/hourly}/quests/reward-stats-builder.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/duos/aggregate-hourly/config.ts"],"names":[],"mappings":";;;AAAA,+FAA6E;AAEhE,QAAA,aAAa,GAAG,GAAG,kDAAgB,2EAA2E,CAAC;AAC/G,QAAA,cAAc,GAAG,GAAG,kDAAgB,4EAA4E,CAAC","sourcesContent":["import { STATS_KEY_PREFIX } from '../hourly/_build-battlegrounds-hero-stats';\r\n\r\nexport const STAT_KEY_HERO = `${STATS_KEY_PREFIX}/hero-stats/mmr-%mmrPercentile%/%timePeriod%/overview-from-hourly.gz.json`;\r\nexport const STAT_KEY_QUEST = `${STATS_KEY_PREFIX}/quest-stats/mmr-%mmrPercentile%/%timePeriod%/overview-from-hourly.gz.json`;\r\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_build-aggregated-stats.js","sourceRoot":"","sources":["../../../../src/duos/aggregate-hourly/heroes/_build-aggregated-stats.ts"],"names":[],"mappings":";;;;;;AAAA,qEAAwG;AACxG,iEAA+D;AAE/D,sDAA0B;AAE1B,gDAAqD;AACrD,4CAAoD;AACpD,yCAAyC;AACzC,iDAA4C;AAE5C,MAAM,QAAQ,GAAG,IAAI,gCAAe,EAAE,CAAC;AAC1B,QAAA,EAAE,GAAG,IAAI,qBAAE,EAAE,CAAC;AAC3B,MAAM,MAAM,GAAG,IAAI,iBAAG,CAAC,MAAM,EAAE,CAAC;AAEhC,kBAAe,KAAK,EAAE,KAAK,EAAE,OAAgB,EAAgB,EAAE;IAC9D,MAAM,QAAQ,CAAC,iBAAiB,EAAE,CAAC;IAEnC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE;QACtB,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;QAC9B,OAAO;KACP;IAED,MAAM,OAAO,GAAG,IAAA,mCAAgB,EAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAe,KAAK,CAAC,UAAU,CAAC;IAChD,MAAM,aAAa,GAAwB,KAAK,CAAC,aAAa,CAAC;IAI/D,MAAM,SAAS,GAAG,MAAM,IAAA,4CAAyB,GAAE,CAAC;IACpD,MAAM,UAAU,GAA8B,MAAM,IAAA,gCAAoB,EACvE,MAAM,EACN,UAAU,EACV,aAAa,EACb,SAAS,CACT,CAAC;IAEF,MAAM,UAAU,GAAG,UAAU;SAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACZ,IAAI,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC;QAChC,OAAO,EAAE,CAAC,CAAC,cAAc;QACzB,IAAI,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,OAAO,EAAE;KAC1C,CAAC,CAAC;SACF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAU1C,MAAM,WAAW,GAAiC,IAAA,yBAAU,EAAC,UAAU,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;IAClG,MAAM,cAAc,GAA6B,IAAA,iCAAmB,EAAC,UAAU,CAAC,CAAC;IAMjF,MAAM,YAAY,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,KAAK,eAAe,CAAC,CAAC;IAGrF,MAAM,IAAA,sBAAW,EAAC,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;IACtF,OAAO,EAAE,CAAC;AACX,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG,KAAK,EAAE,OAAgB,EAAE,EAAE;IACjD,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IAClC,MAAM,aAAa,GAA0B,CAAC,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;IACpG,MAAM,cAAc,GAAmC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IAC5E,KAAK,MAAM,UAAU,IAAI,aAAa,EAAE;QACvC,KAAK,MAAM,UAAU,IAAI,cAAc,EAAE;YACxC,MAAM,QAAQ,GAAG;gBAChB,UAAU,EAAE,UAAU;gBACtB,aAAa,EAAE,UAAU;aACzB,CAAC;YACF,MAAM,MAAM,GAAG;gBACd,YAAY,EAAE,OAAO,CAAC,YAAY;gBAClC,cAAc,EAAE,OAAO;gBACvB,OAAO,EAAE,MAAM;gBACf,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;aACjC,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,MAAM;iBACzB,MAAM,CAAC;gBACP,YAAY,EAAE,OAAO,CAAC,YAAY;gBAClC,cAAc,EAAE,OAAO;gBACvB,OAAO,EAAE,MAAM;gBACf,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;aACjC,CAAC;iBACD,OAAO,EAAE,CAAC;YAEZ,MAAM,IAAA,wBAAK,EAAC,EAAE,CAAC,CAAC;SAChB;KACD;AACF,CAAC,CAAC","sourcesContent":["import { S3, getLastBattlegroundsPatch, logBeforeTimeout, sleep } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService } from '@firestone-hs/reference-data';\r\nimport { Context } from 'aws-lambda';\r\nimport AWS from 'aws-sdk';\r\nimport { BgsGlobalHeroStat, BgsHeroStatsV2, MmrPercentile, MmrPercentileFilter, TimePeriod } from '../../../models';\r\nimport { buildMmrPercentiles } from '../percentiles';\r\nimport { loadHourlyDataFromS3 } from '../s3-loader';\r\nimport { persistData } from './s3-saver';\r\nimport { mergeStats } from './stats-merger';\r\n\r\nconst allCards = new AllCardsService();\r\nexport const s3 = new S3();\r\nconst lambda = new AWS.Lambda();\r\n\r\nexport default async (event, context: Context): Promise<any> => {\r\n\tawait allCards.initializeCardsDb();\r\n\r\n\tif (!event.timePeriod) {\r\n\t\tawait dispatchEvents(context);\r\n\t\treturn;\r\n\t}\r\n\r\n\tconst cleanup = logBeforeTimeout(context);\r\n\tconst timePeriod: TimePeriod = event.timePeriod;\r\n\tconst mmrPercentile: MmrPercentileFilter = event.mmrPercentile;\r\n\r\n\t// console.log('aggregating data', timePeriod, mmrPercentile);\r\n\t// Build the list of files based on the timeframe, and load all of these\r\n\tconst patchInfo = await getLastBattlegroundsPatch();\r\n\tconst hourlyData: readonly BgsHeroStatsV2[] = await loadHourlyDataFromS3(\r\n\t\t'hero',\r\n\t\ttimePeriod,\r\n\t\tmmrPercentile,\r\n\t\tpatchInfo,\r\n\t);\r\n\t// console.log('hourlyData', hourlyData.length);\r\n\tconst lastUpdate = hourlyData\r\n\t\t.map((d) => ({\r\n\t\t\tdate: new Date(d.lastUpdateDate),\r\n\t\t\tdateStr: d.lastUpdateDate,\r\n\t\t\ttime: new Date(d.lastUpdateDate).getTime(),\r\n\t\t}))\r\n\t\t.sort((a, b) => b.time - a.time)[0].date;\r\n\t// console.log('loaded hourly data', lastUpdate);\r\n\r\n\t// Here it's ok that the MMR corresponding to each MMR percentile is not the same across all the hourly data chunks\r\n\t// as the actual MMR evolves over time\r\n\t// The only issue is that the samples might be too small for the MMR percentiles to be really representative in\r\n\t// each group\r\n\t// Empirically, it looks like there isn't much variation on a hour-by-hour basis, so it should be ok\r\n\t// A possible solution, if this becomes an issue, is to compute the MMR percentiles on the full last day of data\r\n\t// when building the hourly data\r\n\tconst mergedStats: readonly BgsGlobalHeroStat[] = mergeStats(hourlyData, mmrPercentile, allCards);\r\n\tconst mmrPercentiles: readonly MmrPercentile[] = buildMmrPercentiles(hourlyData);\r\n\t// console.log(\r\n\t// \t'mergedStats',\r\n\t// \tmergedStats?.map((s) => s.dataPoints).reduce((a, b) => a + b, 0),\r\n\t// \tmergedStats?.length,\r\n\t// );\r\n\tconst testHeroStat = mergedStats.find((stat) => stat.heroCardId === 'BG21_HERO_010');\r\n\t// console.log('testHeroStat', testHeroStat, testHeroStat?.combatWinrate);\r\n\r\n\tawait persistData(mergedStats, mmrPercentiles, lastUpdate, timePeriod, mmrPercentile);\r\n\tcleanup();\r\n};\r\n\r\nconst dispatchEvents = async (context: Context) => {\r\n\tconsole.log('dispatching events');\r\n\tconst allTimePeriod: readonly TimePeriod[] = ['all-time', 'past-three', 'past-seven', 'last-patch'];\r\n\tconst mmrPercentiles: readonly MmrPercentileFilter[] = [100, 50, 25, 10, 1];\r\n\tfor (const timePeriod of allTimePeriod) {\r\n\t\tfor (const percentile of mmrPercentiles) {\r\n\t\t\tconst newEvent = {\r\n\t\t\t\ttimePeriod: timePeriod,\r\n\t\t\t\tmmrPercentile: percentile,\r\n\t\t\t};\r\n\t\t\tconst params = {\r\n\t\t\t\tFunctionName: context.functionName,\r\n\t\t\t\tInvocationType: 'Event',\r\n\t\t\t\tLogType: 'Tail',\r\n\t\t\t\tPayload: JSON.stringify(newEvent),\r\n\t\t\t};\r\n\t\t\t// console.log('\\tinvoking lambda', params);\r\n\t\t\tconst result = await lambda\r\n\t\t\t\t.invoke({\r\n\t\t\t\t\tFunctionName: context.functionName,\r\n\t\t\t\t\tInvocationType: 'Event',\r\n\t\t\t\t\tLogType: 'Tail',\r\n\t\t\t\t\tPayload: JSON.stringify(newEvent),\r\n\t\t\t\t})\r\n\t\t\t\t.promise();\r\n\t\t\t// console.log('\\tinvocation result', result);\r\n\t\t\tawait sleep(50);\r\n\t\t}\r\n\t}\r\n};\r\n"]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { BgsGlobalHeroStat, MmrPercentile, MmrPercentileFilter, TimePeriod } from '
|
|
1
|
+
import { BgsGlobalHeroStat, MmrPercentile, MmrPercentileFilter, TimePeriod } from '../../../models';
|
|
2
2
|
export declare const persistData: (mergedStats: readonly BgsGlobalHeroStat[], mmrPercentiles: readonly MmrPercentile[], lastUpdate: Date, timePeriod: TimePeriod, mmrPercentile: MmrPercentileFilter) => Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3-saver.js","sourceRoot":"","sources":["../../../../src/duos/aggregate-hourly/heroes/s3-saver.ts"],"names":[],"mappings":";;;AAAA,+BAAgC;AAEhC,kGAA4E;AAC5E,sCAA0C;AAC1C,uEAA+C;AAExC,MAAM,WAAW,GAAG,KAAK,EAC/B,WAAyC,EACzC,cAAwC,EACxC,UAAgB,EAChB,UAAsB,EACtB,aAAkC,EACjC,EAAE;IACH,MAAM,IAAI,GAAmB;QAC5B,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACrC,GAAG,IAAI;YACP,aAAa,EAAE,aAAa;YAC5B,UAAU,EAAE,UAAU;SACtB,CAAC,CAAC;QACH,cAAc,EAAE,UAAU;QAC1B,UAAU,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3E,cAAc,EAAE,cAAc;KAC9B,CAAC;IAEF,MAAM,4BAAE,CAAC,SAAS,CACjB,IAAA,eAAQ,EAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,EAC9B,8CAAY,EACZ,sBAAa,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,UAAU,CAAC,EAChG,kBAAkB,EAClB,MAAM,CACN,CAAC;AACH,CAAC,CAAC;AAzBW,QAAA,WAAW,eAyBtB","sourcesContent":["import { gzipSync } from 'zlib';\r\nimport { BgsGlobalHeroStat, BgsHeroStatsV2, MmrPercentile, MmrPercentileFilter, TimePeriod } from '../../../models';\r\nimport { STATS_BUCKET } from '../../hourly/_build-battlegrounds-hero-stats';\r\nimport { STAT_KEY_HERO } from '../config';\r\nimport { s3 } from './_build-aggregated-stats';\r\n\r\nexport const persistData = async (\r\n\tmergedStats: readonly BgsGlobalHeroStat[],\r\n\tmmrPercentiles: readonly MmrPercentile[],\r\n\tlastUpdate: Date,\r\n\ttimePeriod: TimePeriod,\r\n\tmmrPercentile: MmrPercentileFilter,\r\n) => {\r\n\tconst stat: BgsHeroStatsV2 = {\r\n\t\theroStats: mergedStats.map((stat) => ({\r\n\t\t\t...stat,\r\n\t\t\tmmrPercentile: mmrPercentile,\r\n\t\t\ttimePeriod: timePeriod,\r\n\t\t})),\r\n\t\tlastUpdateDate: lastUpdate,\r\n\t\tdataPoints: mergedStats.map((s) => s.dataPoints).reduce((a, b) => a + b, 0),\r\n\t\tmmrPercentiles: mmrPercentiles,\r\n\t};\r\n\t// console.log('persisting data', stat.dataPoints, stat.heroStats?.length);\r\n\tawait s3.writeFile(\r\n\t\tgzipSync(JSON.stringify(stat)),\r\n\t\tSTATS_BUCKET,\r\n\t\tSTAT_KEY_HERO.replace('%mmrPercentile%', `${mmrPercentile}`).replace('%timePeriod%', timePeriod),\r\n\t\t'application/json',\r\n\t\t'gzip',\r\n\t);\r\n};\r\n"]}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { AllCardsService } from '@firestone-hs/reference-data';
|
|
2
|
-
import { BgsGlobalHeroStat, BgsHeroStatsV2, MmrPercentileFilter } from '
|
|
2
|
+
import { BgsGlobalHeroStat, BgsHeroStatsV2, MmrPercentileFilter } from '../../../models';
|
|
3
3
|
export declare const mergeStats: (hourlyData: readonly BgsHeroStatsV2[], mmrPercentile: MmrPercentileFilter, allCards: AllCardsService) => readonly BgsGlobalHeroStat[];
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mergeStats = void 0;
|
|
4
|
+
const aws_lambda_utils_1 = require("@firestone-hs/aws-lambda-utils");
|
|
5
|
+
const util_functions_1 = require("../../../common/util-functions");
|
|
6
|
+
const mergeStats = (hourlyData, mmrPercentile, allCards) => {
|
|
7
|
+
const allStats = hourlyData
|
|
8
|
+
.flatMap((data) => data.heroStats)
|
|
9
|
+
.filter((stat) => stat.mmrPercentile === mmrPercentile)
|
|
10
|
+
.filter((stat) => stat.heroCardId !== "TB_BaconShop_HERO_PH");
|
|
11
|
+
const groupedByHero = (0, aws_lambda_utils_1.groupByFunction)((stat) => stat.heroCardId)(allStats);
|
|
12
|
+
const result = Object.values(groupedByHero).map((stats) => mergeStatsForSingleHero(stats, allCards));
|
|
13
|
+
return result;
|
|
14
|
+
};
|
|
15
|
+
exports.mergeStats = mergeStats;
|
|
16
|
+
const mergeStatsForSingleHero = (stats, allCards) => {
|
|
17
|
+
const ref = stats[0];
|
|
18
|
+
const debug = ref.heroCardId === 'BG21_HERO_010';
|
|
19
|
+
const totalWeightedAverage = stats.map((s) => s.averagePosition * s.dataPoints).reduce((a, b) => a + b, 0);
|
|
20
|
+
const totalDataPoints = stats.map((s) => s.dataPoints).reduce((a, b) => a + b, 0);
|
|
21
|
+
const averagePosition = totalWeightedAverage / totalDataPoints;
|
|
22
|
+
const totalWeightedVariance = stats
|
|
23
|
+
.map((s) => s.standardDeviation * s.standardDeviation * s.dataPoints)
|
|
24
|
+
.reduce((a, b) => a + b, 0);
|
|
25
|
+
const overallVariance = totalWeightedVariance / totalDataPoints;
|
|
26
|
+
const standardDeviation = Math.sqrt(overallVariance);
|
|
27
|
+
const standardDeviationOfTheMean = standardDeviation / Math.sqrt(totalDataPoints);
|
|
28
|
+
const result = {
|
|
29
|
+
heroCardId: ref.heroCardId,
|
|
30
|
+
dataPoints: stats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0),
|
|
31
|
+
averagePosition: (0, util_functions_1.round)(averagePosition),
|
|
32
|
+
standardDeviation: (0, util_functions_1.round)(standardDeviation),
|
|
33
|
+
standardDeviationOfTheMean: (0, util_functions_1.round)(standardDeviationOfTheMean),
|
|
34
|
+
conservativePositionEstimate: (0, util_functions_1.round)(averagePosition + 3 * standardDeviationOfTheMean),
|
|
35
|
+
placementDistribution: mergePlacementDistributions(stats),
|
|
36
|
+
warbandStats: mergeWarbandStats(stats),
|
|
37
|
+
combatWinrate: mergeCombatWinrate(stats, debug),
|
|
38
|
+
tribeStats: mergeTribeStats(stats, averagePosition).filter((s) => s.dataPointsOnMissingTribe > s.dataPoints / 20),
|
|
39
|
+
anomalyStats: [],
|
|
40
|
+
};
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
const mergeTribeStats = (stats, refAveragePosition) => {
|
|
44
|
+
const allTribeStats = stats.flatMap((stat) => stat.tribeStats);
|
|
45
|
+
const uniqueTribes = new Set(allTribeStats.map((tribe) => tribe.tribe));
|
|
46
|
+
const result = [...uniqueTribes].map((tribe) => {
|
|
47
|
+
const tribeStats = allTribeStats.filter((stat) => stat.tribe === tribe);
|
|
48
|
+
const totalStatDataPoints = tribeStats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0);
|
|
49
|
+
const averagePosition = tribeStats.map((stat) => stat.averagePosition * stat.dataPoints).reduce((a, b) => a + b, 0) /
|
|
50
|
+
totalStatDataPoints;
|
|
51
|
+
const averagePositionWithoutTribe = tribeStats
|
|
52
|
+
.map((stat) => stat.averagePositionWithoutTribe * stat.dataPointsOnMissingTribe)
|
|
53
|
+
.reduce((a, b) => a + b, 0) /
|
|
54
|
+
tribeStats.map((stat) => stat.dataPointsOnMissingTribe).reduce((a, b) => a + b, 0);
|
|
55
|
+
return {
|
|
56
|
+
tribe: tribe,
|
|
57
|
+
dataPoints: totalStatDataPoints,
|
|
58
|
+
dataPointsOnMissingTribe: tribeStats
|
|
59
|
+
.map((stat) => stat.dataPointsOnMissingTribe)
|
|
60
|
+
.reduce((a, b) => a + b, 0),
|
|
61
|
+
averagePosition: (0, util_functions_1.round)(averagePosition),
|
|
62
|
+
averagePositionWithoutTribe: (0, util_functions_1.round)(averagePositionWithoutTribe),
|
|
63
|
+
refAveragePosition: refAveragePosition,
|
|
64
|
+
impactAveragePosition: (0, util_functions_1.round)(averagePosition - refAveragePosition),
|
|
65
|
+
impactAveragePositionVsMissingTribe: (0, util_functions_1.round)(averagePositionWithoutTribe - refAveragePosition),
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
return result;
|
|
69
|
+
};
|
|
70
|
+
const mergePlacementDistributions = (stats) => {
|
|
71
|
+
var _a, _b;
|
|
72
|
+
const rawMerge = mergePlacementDistributionsRaw(stats);
|
|
73
|
+
const result = [];
|
|
74
|
+
const totalDataPoints = rawMerge.map((s) => s.totalMatches).reduce((a, b) => a + b, 0);
|
|
75
|
+
for (let i = 1; i <= 8; i++) {
|
|
76
|
+
const total = (_b = (_a = rawMerge.find((d) => d.rank === i)) === null || _a === void 0 ? void 0 : _a.totalMatches) !== null && _b !== void 0 ? _b : 0;
|
|
77
|
+
result.push({ rank: i, percentage: (0, util_functions_1.round)((100 * total) / totalDataPoints) });
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
};
|
|
81
|
+
const mergePlacementDistributionsRaw = (stats) => {
|
|
82
|
+
const rawStats = stats.map((stat) => stat.placementDistributionRaw);
|
|
83
|
+
const result = [];
|
|
84
|
+
for (let i = 1; i <= 8; i++) {
|
|
85
|
+
const total = rawStats
|
|
86
|
+
.map((d) => { var _a, _b; return (_b = (_a = d === null || d === void 0 ? void 0 : d.find((info) => info.rank === i)) === null || _a === void 0 ? void 0 : _a.totalMatches) !== null && _b !== void 0 ? _b : 0; })
|
|
87
|
+
.reduce((a, b) => a + b, 0);
|
|
88
|
+
result.push({ rank: i, totalMatches: total });
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
};
|
|
92
|
+
const mergeWarbandStats = (stats) => {
|
|
93
|
+
const { rawMerge, maxTurn } = mergeWarbandStatsRaw(stats);
|
|
94
|
+
const result = [];
|
|
95
|
+
for (let i = 1; i <= maxTurn; i++) {
|
|
96
|
+
const statsForTurn = rawMerge.find((d) => d.turn === i);
|
|
97
|
+
result.push({ turn: i, averageStats: (0, util_functions_1.round)(statsForTurn.totalStats / statsForTurn.dataPoints) });
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
};
|
|
101
|
+
const mergeWarbandStatsRaw = (stats) => {
|
|
102
|
+
const rawStats = stats.map((stat) => stat.warbandStatsRaw).filter((s) => !!(s === null || s === void 0 ? void 0 : s.length));
|
|
103
|
+
const result = [];
|
|
104
|
+
const maxTurn = Math.min(20, Math.max(...rawStats.map((stat) => getMaxTurn(stat))));
|
|
105
|
+
for (let i = 1; i <= maxTurn; i++) {
|
|
106
|
+
const rawStatsForTurn = rawStats.map((stat) => stat.find((info) => info.turn === i));
|
|
107
|
+
const totalStats = rawStatsForTurn.map((d) => { var _a; return (_a = d === null || d === void 0 ? void 0 : d.totalStats) !== null && _a !== void 0 ? _a : 0; }).reduce((a, b) => a + b, 0);
|
|
108
|
+
const totalDataPoints = rawStatsForTurn.map((d) => { var _a; return (_a = d === null || d === void 0 ? void 0 : d.dataPoints) !== null && _a !== void 0 ? _a : 0; }).reduce((a, b) => a + b, 0);
|
|
109
|
+
result.push({ turn: i, totalStats: totalStats, dataPoints: totalDataPoints });
|
|
110
|
+
}
|
|
111
|
+
return { rawMerge: result, maxTurn: maxTurn };
|
|
112
|
+
};
|
|
113
|
+
const mergeCombatWinrate = (stats, debug = false) => {
|
|
114
|
+
const { rawMerge, maxTurn } = mergeCombatWinrateRaw(stats, debug);
|
|
115
|
+
const result = [];
|
|
116
|
+
for (let i = 1; i <= maxTurn; i++) {
|
|
117
|
+
const statForTurn = rawMerge.find((d) => d.turn === i);
|
|
118
|
+
result.push({ turn: i, winrate: (0, util_functions_1.round)(statForTurn.totalWinrate / statForTurn.dataPoints) });
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
};
|
|
122
|
+
const mergeCombatWinrateRaw = (stats, debug = false) => {
|
|
123
|
+
const rawStats = stats.map((stat) => stat.combatWinrateRaw).filter((stat) => !!(stat === null || stat === void 0 ? void 0 : stat.length));
|
|
124
|
+
const result = [];
|
|
125
|
+
const maxTurn = Math.min(20, Math.max(...rawStats.map((stat) => getMaxTurn(stat))));
|
|
126
|
+
for (let i = 0; i <= maxTurn; i++) {
|
|
127
|
+
const rawStatsForTurn = rawStats.map((stat) => stat.find((info) => info.turn === i));
|
|
128
|
+
const totalWinrate = rawStatsForTurn.map((d) => { var _a; return (_a = d === null || d === void 0 ? void 0 : d.totalWinrate) !== null && _a !== void 0 ? _a : 0; }).reduce((a, b) => a + b, 0);
|
|
129
|
+
const totalDataPoints = rawStatsForTurn.map((d) => { var _a; return (_a = d === null || d === void 0 ? void 0 : d.dataPoints) !== null && _a !== void 0 ? _a : 0; }).reduce((a, b) => a + b, 0);
|
|
130
|
+
result.push({ turn: i, totalWinrate: totalWinrate, dataPoints: totalDataPoints });
|
|
131
|
+
}
|
|
132
|
+
return { rawMerge: result, maxTurn: maxTurn };
|
|
133
|
+
};
|
|
134
|
+
const getMaxTurn = (rawStats) => {
|
|
135
|
+
if (!(rawStats === null || rawStats === void 0 ? void 0 : rawStats.length)) {
|
|
136
|
+
return 0;
|
|
137
|
+
}
|
|
138
|
+
return Math.max(...rawStats.map((stat) => stat.turn));
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=stats-merger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats-merger.js","sourceRoot":"","sources":["../../../../src/duos/aggregate-hourly/heroes/stats-merger.ts"],"names":[],"mappings":"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","sourcesContent":["import { groupByFunction } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService, CardIds } from '@firestone-hs/reference-data';\r\nimport { round } from '../../../common/util-functions';\r\nimport { BgsGlobalHeroStat, BgsHeroStatsV2, BgsHeroTribeStat, MmrPercentileFilter } from '../../../models';\r\n\r\nexport const mergeStats = (\r\n\thourlyData: readonly BgsHeroStatsV2[],\r\n\tmmrPercentile: MmrPercentileFilter,\r\n\tallCards: AllCardsService,\r\n): readonly BgsGlobalHeroStat[] => {\r\n\tconst allStats: readonly BgsGlobalHeroStat[] = hourlyData\r\n\t\t.flatMap((data) => data.heroStats)\r\n\t\t.filter((stat) => stat.mmrPercentile === mmrPercentile)\r\n\t\t.filter((stat) => stat.heroCardId !== CardIds.BaconphheroHeroic);\r\n\t// console.debug('allStats', mmrPercentile, allStats.length, '/', hourlyData.length);\r\n\tconst groupedByHero: {\r\n\t\t[heroCardId: string]: readonly BgsGlobalHeroStat[];\r\n\t} = groupByFunction((stat: BgsGlobalHeroStat) => stat.heroCardId)(allStats);\r\n\tconst result: readonly BgsGlobalHeroStat[] = Object.values(groupedByHero).map((stats) =>\r\n\t\tmergeStatsForSingleHero(stats, allCards),\r\n\t);\r\n\treturn result;\r\n};\r\n\r\nconst mergeStatsForSingleHero = (stats: readonly BgsGlobalHeroStat[], allCards: AllCardsService): BgsGlobalHeroStat => {\r\n\tconst ref = stats[0];\r\n\tconst debug = ref.heroCardId === 'BG21_HERO_010';\r\n\r\n\tconst totalWeightedAverage = stats.map((s) => s.averagePosition * s.dataPoints).reduce((a, b) => a + b, 0);\r\n\tconst totalDataPoints = stats.map((s) => s.dataPoints).reduce((a, b) => a + b, 0);\r\n\tconst averagePosition = totalWeightedAverage / totalDataPoints;\r\n\r\n\tconst totalWeightedVariance = stats\r\n\t\t.map((s) => s.standardDeviation * s.standardDeviation * s.dataPoints)\r\n\t\t.reduce((a, b) => a + b, 0);\r\n\tconst overallVariance = totalWeightedVariance / totalDataPoints;\r\n\tconst standardDeviation = Math.sqrt(overallVariance);\r\n\tconst standardDeviationOfTheMean = standardDeviation / Math.sqrt(totalDataPoints);\r\n\r\n\tconst result: BgsGlobalHeroStat = {\r\n\t\theroCardId: ref.heroCardId,\r\n\t\tdataPoints: stats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0),\r\n\t\taveragePosition: round(averagePosition),\r\n\t\tstandardDeviation: round(standardDeviation),\r\n\t\tstandardDeviationOfTheMean: round(standardDeviationOfTheMean),\r\n\t\tconservativePositionEstimate: round(averagePosition + 3 * standardDeviationOfTheMean),\r\n\t\tplacementDistribution: mergePlacementDistributions(stats),\r\n\t\twarbandStats: mergeWarbandStats(stats),\r\n\t\tcombatWinrate: mergeCombatWinrate(stats, debug),\r\n\t\ttribeStats: mergeTribeStats(stats, averagePosition).filter(\r\n\t\t\t(s) => s.dataPointsOnMissingTribe > s.dataPoints / 20,\r\n\t\t),\r\n\t\tanomalyStats: [], // mergeAnomalyStats(stats),\r\n\t};\r\n\treturn result;\r\n};\r\n\r\nconst mergeTribeStats = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n\trefAveragePosition: number,\r\n): readonly BgsHeroTribeStat[] => {\r\n\tconst allTribeStats = stats.flatMap((stat) => stat.tribeStats);\r\n\tconst uniqueTribes = new Set(allTribeStats.map((tribe) => tribe.tribe));\r\n\tconst result: BgsHeroTribeStat[] = [...uniqueTribes].map((tribe) => {\r\n\t\tconst tribeStats = allTribeStats.filter((stat) => stat.tribe === tribe);\r\n\t\tconst totalStatDataPoints = tribeStats.map((stat) => stat.dataPoints).reduce((a, b) => a + b, 0);\r\n\t\tconst averagePosition =\r\n\t\t\ttribeStats.map((stat) => stat.averagePosition * stat.dataPoints).reduce((a, b) => a + b, 0) /\r\n\t\t\ttotalStatDataPoints;\r\n\t\tconst averagePositionWithoutTribe =\r\n\t\t\ttribeStats\r\n\t\t\t\t.map((stat) => stat.averagePositionWithoutTribe * stat.dataPointsOnMissingTribe)\r\n\t\t\t\t.reduce((a, b) => a + b, 0) /\r\n\t\t\ttribeStats.map((stat) => stat.dataPointsOnMissingTribe).reduce((a, b) => a + b, 0);\r\n\t\t// const impactAveragePosition =\r\n\t\t// \ttribeStats.map((stat) => stat.impactAveragePosition * stat.dataPoints).reduce((a, b) => a + b, 0) /\r\n\t\t// \ttotalStatDataPoints;\r\n\t\t// const impactAveragePositionVsMissingTribe =\r\n\t\t// \ttribeStats\r\n\t\t// \t\t.map((stat) => stat.impactAveragePositionVsMissingTribe * stat.dataPoints)\r\n\t\t// \t\t.reduce((a, b) => a + b, 0) / totalStatDataPoints;\r\n\t\treturn {\r\n\t\t\ttribe: tribe,\r\n\t\t\tdataPoints: totalStatDataPoints,\r\n\t\t\tdataPointsOnMissingTribe: tribeStats\r\n\t\t\t\t.map((stat) => stat.dataPointsOnMissingTribe)\r\n\t\t\t\t.reduce((a, b) => a + b, 0),\r\n\t\t\taveragePosition: round(averagePosition),\r\n\t\t\taveragePositionWithoutTribe: round(averagePositionWithoutTribe),\r\n\t\t\trefAveragePosition: refAveragePosition,\r\n\t\t\timpactAveragePosition: round(averagePosition - refAveragePosition),\r\n\t\t\t// impactAveragePositionDebug: round(impactAveragePosition),\r\n\t\t\timpactAveragePositionVsMissingTribe: round(averagePositionWithoutTribe - refAveragePosition),\r\n\t\t};\r\n\t});\r\n\treturn result;\r\n};\r\n\r\nconst mergePlacementDistributions = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n): readonly { rank: number; percentage: number }[] => {\r\n\tconst rawMerge = mergePlacementDistributionsRaw(stats);\r\n\tconst result: { rank: number; percentage: number }[] = [];\r\n\tconst totalDataPoints = rawMerge.map((s) => s.totalMatches).reduce((a, b) => a + b, 0);\r\n\tfor (let i = 1; i <= 8; i++) {\r\n\t\tconst total: number = rawMerge.find((d) => d.rank === i)?.totalMatches ?? 0;\r\n\t\tresult.push({ rank: i, percentage: round((100 * total) / totalDataPoints) });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergePlacementDistributionsRaw = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n): readonly { rank: number; totalMatches: number }[] => {\r\n\tconst rawStats = stats.map((stat) => stat.placementDistributionRaw);\r\n\t// console.debug(\r\n\t// \t'will merge placement distributions',\r\n\t// \tstats[0].heroCardId,\r\n\t// \tstats[0],\r\n\t// \trawStats.length,\r\n\t// \trawStats.filter((s) => !!s).length,\r\n\t// );\r\n\t// Legacy, can be removed after 2024-01-31\r\n\t// const pStats = stats\r\n\t// \t.filter((stat) => stat.placementDistribution?.length)\r\n\t// \t.map((stat) =>\r\n\t// \t\tstat.placementDistribution.map((s) => ({\r\n\t// \t\t\trank: s.rank,\r\n\t// \t\t\ttotalMatches: s.percentage * stat.dataPoints,\r\n\t// \t\t})),\r\n\t// \t);\r\n\t// const allRawStats = [...rawStats, ...pStats].filter((s) => !!s);\r\n\tconst result: { rank: number; totalMatches: number }[] = [];\r\n\tfor (let i = 1; i <= 8; i++) {\r\n\t\tconst total: number = rawStats\r\n\t\t\t.map((d) => d?.find((info) => info.rank === i)?.totalMatches ?? 0)\r\n\t\t\t.reduce((a, b) => a + b, 0);\r\n\t\tresult.push({ rank: i, totalMatches: total });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergeWarbandStats = (stats: readonly BgsGlobalHeroStat[]): readonly { turn: number; averageStats: number }[] => {\r\n\tconst { rawMerge, maxTurn } = mergeWarbandStatsRaw(stats);\r\n\tconst result: { turn: number; averageStats: number }[] = [];\r\n\tfor (let i = 1; i <= maxTurn; i++) {\r\n\t\tconst statsForTurn = rawMerge.find((d) => d.turn === i);\r\n\t\tresult.push({ turn: i, averageStats: round(statsForTurn.totalStats / statsForTurn.dataPoints) });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergeWarbandStatsRaw = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n): { rawMerge: readonly { turn: number; dataPoints: number; totalStats: number }[]; maxTurn: number } => {\r\n\tconst rawStats = stats.map((stat) => stat.warbandStatsRaw).filter((s) => !!s?.length);\r\n\tconst result: { turn: number; dataPoints: number; totalStats: number }[] = [];\r\n\tconst maxTurn = Math.min(20, Math.max(...rawStats.map((stat) => getMaxTurn(stat))));\r\n\tfor (let i = 1; i <= maxTurn; i++) {\r\n\t\tconst rawStatsForTurn = rawStats.map((stat) => stat.find((info) => info.turn === i));\r\n\t\tconst totalStats: number = rawStatsForTurn.map((d) => d?.totalStats ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tconst totalDataPoints = rawStatsForTurn.map((d) => d?.dataPoints ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tresult.push({ turn: i, totalStats: totalStats, dataPoints: totalDataPoints });\r\n\t}\r\n\treturn { rawMerge: result, maxTurn: maxTurn };\r\n};\r\n\r\nconst mergeCombatWinrate = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n\tdebug = false,\r\n): readonly { turn: number; winrate: number }[] => {\r\n\tconst { rawMerge, maxTurn } = mergeCombatWinrateRaw(stats, debug);\r\n\tconst result: { turn: number; winrate: number }[] = [];\r\n\tfor (let i = 1; i <= maxTurn; i++) {\r\n\t\tconst statForTurn = rawMerge.find((d) => d.turn === i);\r\n\t\tresult.push({ turn: i, winrate: round(statForTurn.totalWinrate / statForTurn.dataPoints) });\r\n\t}\r\n\treturn result;\r\n};\r\n\r\nconst mergeCombatWinrateRaw = (\r\n\tstats: readonly BgsGlobalHeroStat[],\r\n\tdebug = false,\r\n): { rawMerge: readonly { turn: number; dataPoints: number; totalWinrate: number }[]; maxTurn: number } => {\r\n\tconst rawStats = stats.map((stat) => stat.combatWinrateRaw).filter((stat) => !!stat?.length);\r\n\t// debug && console.log('rawStats', rawStats?.length, rawStats);\r\n\tconst result: { turn: number; dataPoints: number; totalWinrate: number }[] = [];\r\n\tconst maxTurn = Math.min(20, Math.max(...rawStats.map((stat) => getMaxTurn(stat))));\r\n\t// debug && console.log('maxTurn', maxTurn);\r\n\tfor (let i = 0; i <= maxTurn; i++) {\r\n\t\tconst rawStatsForTurn = rawStats.map((stat) => stat.find((info) => info.turn === i));\r\n\t\tconst totalWinrate: number = rawStatsForTurn.map((d) => d?.totalWinrate ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tconst totalDataPoints = rawStatsForTurn.map((d) => d?.dataPoints ?? 0).reduce((a, b) => a + b, 0);\r\n\t\tresult.push({ turn: i, totalWinrate: totalWinrate, dataPoints: totalDataPoints });\r\n\t}\r\n\t// debug && console.log('result', result);\r\n\treturn { rawMerge: result, maxTurn: maxTurn };\r\n};\r\n\r\nconst getMaxTurn = (rawStats: readonly { turn: number }[]): number => {\r\n\tif (!rawStats?.length) {\r\n\t\treturn 0;\r\n\t}\r\n\treturn Math.max(...rawStats.map((stat) => stat.turn));\r\n};\r\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { PatchInfo } from '@firestone-hs/aws-lambda-utils';
|
|
2
|
-
import { TimePeriod } from '
|
|
2
|
+
import { TimePeriod } from '../../models';
|
|
3
3
|
export declare const computeHoursBackFromNow: (timePeriod: TimePeriod, patchInfo: PatchInfo) => number;
|
|
4
4
|
export declare const buildFileNames: (hoursBack: number) => readonly string[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hourly-utils.js","sourceRoot":"","sources":["../../../src/duos/aggregate-hourly/hourly-utils.ts"],"names":[],"mappings":";;;AAIO,MAAM,uBAAuB,GAAG,CAAC,UAAsB,EAAE,SAAoB,EAAU,EAAE;IAC/F,QAAQ,UAAU,EAAE;QACnB,KAAK,YAAY;YAChB,OAAO,CAAC,GAAG,EAAE,CAAC;QACf,KAAK,YAAY;YAChB,OAAO,CAAC,GAAG,EAAE,CAAC;QACf,KAAK,UAAU;YACd,OAAO,EAAE,GAAG,EAAE,CAAC;QAChB,KAAK,YAAY;YAChB,MAAM,gBAAgB,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,gBAAgB,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;YAEvF,OAAO,KAAK,CAAC;KACd;AACF,CAAC,CAAC;AAdW,QAAA,uBAAuB,2BAclC;AAEK,MAAM,cAAc,GAAG,CAAC,SAAiB,EAAqB,EAAE;IAGtE,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE;QACnC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAC1D,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAExB,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACnC,SAAS,CAAC,IAAI,CAAC,GAAG,OAAO,EAAE,CAAC,CAAC;KAC7B;IACD,OAAO,SAAS,CAAC;AAClB,CAAC,CAAC;AAfW,QAAA,cAAc,kBAezB","sourcesContent":["/* eslint-disable no-case-declarations */\r\nimport { PatchInfo } from '@firestone-hs/aws-lambda-utils';\r\nimport { TimePeriod } from '../../models';\r\n\r\nexport const computeHoursBackFromNow = (timePeriod: TimePeriod, patchInfo: PatchInfo): number => {\r\n\tswitch (timePeriod) {\r\n\t\tcase 'past-three':\r\n\t\t\treturn 3 * 24;\r\n\t\tcase 'past-seven':\r\n\t\t\treturn 7 * 24;\r\n\t\tcase 'all-time':\r\n\t\t\treturn 20 * 24;\r\n\t\tcase 'last-patch':\r\n\t\t\tconst patchReleaseDate = new Date(patchInfo.date);\r\n\t\t\tconst hours = Math.floor((Date.now() - patchReleaseDate.getTime()) / (1000 * 60 * 60));\r\n\t\t\t// console.debug('hours since last patch', hours, patchReleaseDate, patchInfo);\r\n\t\t\treturn hours;\r\n\t}\r\n};\r\n\r\nexport const buildFileNames = (hoursBack: number): readonly string[] => {\r\n\t// Build a list of file names, in the form YYYY-MM-dd (e.g. 2020-05-01)\r\n\t// that start from the day before the current date and go back in time\r\n\tconst fileNames: string[] = [];\r\n\tconst now = new Date();\r\n\tfor (let i = 0; i < hoursBack; i++) {\r\n\t\tconst date = new Date(now.getTime() - i * 60 * 60 * 1000);\r\n\t\tdate.setMinutes(0);\r\n\t\tdate.setSeconds(0);\r\n\t\tdate.setMilliseconds(0);\r\n\t\t// The date in the format YYYY-MM-ddTHH:mm:ss.sssZ\r\n\t\tconst dateStr = date.toISOString();\r\n\t\tfileNames.push(`${dateStr}`);\r\n\t}\r\n\treturn fileNames;\r\n};\r\n"]}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { BgsQuestStats } from '
|
|
2
|
-
import { BgsHeroStatsV2, MmrPercentile } from '
|
|
1
|
+
import { BgsQuestStats } from '../../model-quests';
|
|
2
|
+
import { BgsHeroStatsV2, MmrPercentile } from '../../models';
|
|
3
3
|
export declare const buildMmrPercentiles: (hourlyData: readonly (BgsHeroStatsV2 | BgsQuestStats)[]) => readonly MmrPercentile[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"percentiles.js","sourceRoot":"","sources":["../../../src/duos/aggregate-hourly/percentiles.ts"],"names":[],"mappings":";;;AAGO,MAAM,mBAAmB,GAAG,CAClC,UAAuD,EAC5B,EAAE;IAG7B,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAC1B,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,OAAO,EAAE,CACrF,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC;AACrB,CAAC,CAAC;AARW,QAAA,mBAAmB,uBAQ9B","sourcesContent":["import { BgsQuestStats } from '../../model-quests';\r\nimport { BgsHeroStatsV2, MmrPercentile } from '../../models';\r\n\r\nexport const buildMmrPercentiles = (\r\n\thourlyData: readonly (BgsHeroStatsV2 | BgsQuestStats)[],\r\n): readonly MmrPercentile[] => {\r\n\t// For now we simply pick the latest MMR percentiles, as it reflects the most accurately the current\r\n\t// state of the game\r\n\treturn [...hourlyData].sort(\r\n\t\t(a, b) => new Date(b.lastUpdateDate).getTime() - new Date(a.lastUpdateDate).getTime(),\r\n\t)[0].mmrPercentiles;\r\n};\r\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PatchInfo } from '@firestone-hs/aws-lambda-utils';
|
|
2
|
-
import { BgsQuestStats } from '
|
|
3
|
-
import { BgsHeroStatsV2, MmrPercentileFilter, TimePeriod } from '
|
|
2
|
+
import { BgsQuestStats } from '../../model-quests';
|
|
3
|
+
import { BgsHeroStatsV2, MmrPercentileFilter, TimePeriod } from '../../models';
|
|
4
4
|
export type DataType = 'hero' | 'quest';
|
|
5
5
|
export type DataResult<T extends DataType> = T extends 'hero' ? BgsHeroStatsV2 : BgsQuestStats;
|
|
6
6
|
export declare const loadHourlyDataFromS3: <T extends DataType>(type: T, timePeriod: TimePeriod, mmrPercentile: MmrPercentileFilter, patchInfo: PatchInfo) => Promise<readonly DataResult<T>[]>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3-loader.js","sourceRoot":"","sources":["../../../src/duos/aggregate-hourly/s3-loader.ts"],"names":[],"mappings":";;;AAGA,+FAA4G;AAC5G,8EAAsD;AACtD,iDAAyE;AAKlE,MAAM,oBAAoB,GAAG,KAAK,EACxC,IAAO,EACP,UAAsB,EACtB,aAAkC,EAClC,SAAoB,EACgB,EAAE;IACtC,MAAM,SAAS,GAAW,IAAA,sCAAuB,EAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IACzE,MAAM,SAAS,GAAsB,IAAA,6BAAc,EAAC,SAAS,CAAC,CAAC;IAE/D,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,GAAG,CACpC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,wBAAwB,CAAC,IAAI,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC,CACpF,CAAC;IACF,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AACjD,CAAC,CAAC;AAbW,QAAA,oBAAoB,wBAa/B;AAEF,MAAM,wBAAwB,GAAG,KAAK,EACrC,IAAO,EACP,aAAkC,EAClC,QAAgB,EACS,EAAE;IAC3B,MAAM,OAAO,GAAG,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,iDAAe,CAAC,CAAC,CAAC,kDAAgB,CAAC;IACrE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IACxG,MAAM,IAAI,GAAG,MAAM,4BAAE,CAAC,eAAe,CAAC,8CAAY,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;IACvE,MAAM,MAAM,GAAkB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/C,OAAO,MAAM,CAAC;AACf,CAAC,CAAC","sourcesContent":["import { PatchInfo } from '@firestone-hs/aws-lambda-utils';\r\nimport { BgsQuestStats } from '../../model-quests';\r\nimport { BgsHeroStatsV2, MmrPercentileFilter, TimePeriod } from '../../models';\r\nimport { HOURLY_KEY_HERO, HOURLY_KEY_QUEST, STATS_BUCKET } from '../hourly/_build-battlegrounds-hero-stats';\r\nimport { s3 } from './heroes/_build-aggregated-stats';\r\nimport { buildFileNames, computeHoursBackFromNow } from './hourly-utils';\r\n\r\nexport type DataType = 'hero' | 'quest';\r\nexport type DataResult<T extends DataType> = T extends 'hero' ? BgsHeroStatsV2 : BgsQuestStats;\r\n\r\nexport const loadHourlyDataFromS3 = async <T extends DataType>(\r\n\ttype: T,\r\n\ttimePeriod: TimePeriod,\r\n\tmmrPercentile: MmrPercentileFilter,\r\n\tpatchInfo: PatchInfo,\r\n): Promise<readonly DataResult<T>[]> => {\r\n\tconst hoursBack: number = computeHoursBackFromNow(timePeriod, patchInfo);\r\n\tconst fileNames: readonly string[] = buildFileNames(hoursBack);\r\n\t// console.debug('fileNames', timePeriod, mmrPercentile, fileNames);\r\n\tconst fileResults = await Promise.all(\r\n\t\tfileNames.map((fileName) => loadHourlyDeckStatFromS3(type, mmrPercentile, fileName)),\r\n\t);\r\n\treturn fileResults.filter((result) => !!result);\r\n};\r\n\r\nconst loadHourlyDeckStatFromS3 = async <T extends DataType>(\r\n\ttype: T,\r\n\tmmrPercentile: MmrPercentileFilter,\r\n\tfileName: string,\r\n): Promise<DataResult<T>> => {\r\n\tconst mainKey = type === 'hero' ? HOURLY_KEY_HERO : HOURLY_KEY_QUEST;\r\n\tconst fileKey = mainKey.replace('%mmrPercentile%', `${mmrPercentile}`).replace('%startDate%', fileName);\r\n\tconst data = await s3.readGzipContent(STATS_BUCKET, fileKey, 1, false);\r\n\tconst result: DataResult<T> = JSON.parse(data);\r\n\treturn result;\r\n};\r\n"]}
|
|
@@ -2,7 +2,7 @@ import { S3 } from '@firestone-hs/aws-lambda-utils';
|
|
|
2
2
|
import { Context } from 'aws-lambda';
|
|
3
3
|
export declare const s3: S3;
|
|
4
4
|
export declare const STATS_BUCKET = "static.zerotoheroes.com";
|
|
5
|
-
export declare const STATS_KEY_PREFIX = "api/bgs";
|
|
5
|
+
export declare const STATS_KEY_PREFIX = "api/bgs/duo";
|
|
6
6
|
export declare const WORKING_ROWS_FILE: string;
|
|
7
7
|
export declare const HOURLY_KEY_HERO: string;
|
|
8
8
|
export declare const HOURLY_KEY_QUEST: string;
|
|
@@ -8,14 +8,13 @@ const aws_lambda_utils_1 = require("@firestone-hs/aws-lambda-utils");
|
|
|
8
8
|
const reference_data_1 = require("@firestone-hs/reference-data");
|
|
9
9
|
const aws_sdk_1 = __importDefault(require("aws-sdk"));
|
|
10
10
|
const hero_stats_1 = require("./hero-stats");
|
|
11
|
-
const quest_stats_1 = require("./quest-stats");
|
|
12
11
|
const rows_1 = require("./rows");
|
|
13
12
|
exports.s3 = new aws_lambda_utils_1.S3();
|
|
14
13
|
const allCards = new reference_data_1.AllCardsService();
|
|
15
14
|
const lambda = new aws_sdk_1.default.Lambda();
|
|
16
15
|
const allMmrPercentiles = [100, 50, 25, 10, 1];
|
|
17
16
|
exports.STATS_BUCKET = 'static.zerotoheroes.com';
|
|
18
|
-
exports.STATS_KEY_PREFIX = `api/bgs`;
|
|
17
|
+
exports.STATS_KEY_PREFIX = `api/bgs/duo`;
|
|
19
18
|
exports.WORKING_ROWS_FILE = `${exports.STATS_KEY_PREFIX}/working/working-rows-%time%.json`;
|
|
20
19
|
exports.HOURLY_KEY_HERO = `${exports.STATS_KEY_PREFIX}/hero-stats/mmr-%mmrPercentile%/hourly/%startDate%.gz.json`;
|
|
21
20
|
exports.HOURLY_KEY_QUEST = `${exports.STATS_KEY_PREFIX}/quest-stats/mmr-%mmrPercentile%/hourly/%startDate%.gz.json`;
|
|
@@ -25,11 +24,7 @@ exports.default = async (event, context) => {
|
|
|
25
24
|
const handleNewStats = async (event, context) => {
|
|
26
25
|
const cleanup = (0, aws_lambda_utils_1.logBeforeTimeout)(context);
|
|
27
26
|
await allCards.initializeCardsDb();
|
|
28
|
-
if (event.
|
|
29
|
-
const lastHourRows = await (0, rows_1.readRowsFromS3)(event.startDate);
|
|
30
|
-
await (0, quest_stats_1.buildQuestStats)(event.startDate, event.mmr, lastHourRows, allCards, exports.s3);
|
|
31
|
-
}
|
|
32
|
-
else if (event.statsV2) {
|
|
27
|
+
if (event.statsV2) {
|
|
33
28
|
const lastHourRows = await (0, rows_1.readRowsFromS3)(event.startDate);
|
|
34
29
|
await (0, hero_stats_1.buildHeroStats)(event.startDate, event.mmr, lastHourRows, allCards);
|
|
35
30
|
}
|
|
@@ -43,35 +38,11 @@ const handleNewStats = async (event, context) => {
|
|
|
43
38
|
endDate.setHours(endDate.getHours() + 1);
|
|
44
39
|
await (0, rows_1.saveRowsOnS3)(startDate, endDate, allCards);
|
|
45
40
|
await dispatchStatsV2Lambda(context, startDate);
|
|
46
|
-
await dispatchQuestsV2Lambda(context, startDate);
|
|
47
41
|
}
|
|
48
42
|
cleanup();
|
|
49
43
|
return { statusCode: 200, body: null };
|
|
50
44
|
};
|
|
51
45
|
exports.handleNewStats = handleNewStats;
|
|
52
|
-
const dispatchQuestsV2Lambda = async (context, startDate) => {
|
|
53
|
-
for (const mmr of allMmrPercentiles) {
|
|
54
|
-
const newEvent = {
|
|
55
|
-
questsV2: true,
|
|
56
|
-
mmr: mmr,
|
|
57
|
-
startDate: startDate,
|
|
58
|
-
};
|
|
59
|
-
const params = {
|
|
60
|
-
FunctionName: context.functionName,
|
|
61
|
-
InvocationType: 'Event',
|
|
62
|
-
LogType: 'Tail',
|
|
63
|
-
Payload: JSON.stringify(newEvent),
|
|
64
|
-
};
|
|
65
|
-
const result = await lambda
|
|
66
|
-
.invoke({
|
|
67
|
-
FunctionName: context.functionName,
|
|
68
|
-
InvocationType: 'Event',
|
|
69
|
-
LogType: 'Tail',
|
|
70
|
-
Payload: JSON.stringify(newEvent),
|
|
71
|
-
})
|
|
72
|
-
.promise();
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
46
|
const dispatchStatsV2Lambda = async (context, startDate) => {
|
|
76
47
|
for (const mmr of allMmrPercentiles) {
|
|
77
48
|
const newEvent = {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_build-battlegrounds-hero-stats.js","sourceRoot":"","sources":["../../../src/duos/hourly/_build-battlegrounds-hero-stats.ts"],"names":[],"mappings":";;;;;;AACA,qEAAsE;AACtE,iEAA+D;AAE/D,sDAA0B;AAE1B,6CAA8C;AAC9C,iCAAsD;AAEzC,QAAA,EAAE,GAAG,IAAI,qBAAE,EAAE,CAAC;AAC3B,MAAM,QAAQ,GAAG,IAAI,gCAAe,EAAE,CAAC;AACvC,MAAM,MAAM,GAAG,IAAI,iBAAG,CAAC,MAAM,EAAE,CAAC;AAEhC,MAAM,iBAAiB,GAA+B,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;AAE9D,QAAA,YAAY,GAAG,yBAAyB,CAAC;AACzC,QAAA,gBAAgB,GAAG,aAAa,CAAC;AACjC,QAAA,iBAAiB,GAAG,GAAG,wBAAgB,mCAAmC,CAAC;AAC3E,QAAA,eAAe,GAAG,GAAG,wBAAgB,4DAA4D,CAAC;AAClG,QAAA,gBAAgB,GAAG,GAAG,wBAAgB,6DAA6D,CAAC;AAEjH,kBAAe,KAAK,EAAE,KAAK,EAAE,OAAgB,EAAgB,EAAE;IAC9D,MAAM,IAAA,sBAAc,EAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC,CAAC;AAEK,MAAM,cAAc,GAAG,KAAK,EAAE,KAAK,EAAE,OAAgB,EAAE,EAAE;IAC/D,MAAM,OAAO,GAAG,IAAA,mCAAgB,EAAC,OAAO,CAAC,CAAC;IAE1C,MAAM,QAAQ,CAAC,iBAAiB,EAAE,CAAC;IAEnC,IAAI,KAAK,CAAC,OAAO,EAAE;QAClB,MAAM,YAAY,GAA8B,MAAM,IAAA,qBAAc,EAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAEtF,MAAM,IAAA,2BAAc,EAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,GAAG,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;KACzE;SAAM;QACN,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;QAC7B,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACxB,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACxB,SAAS,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC7B,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;QAG7C,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;QACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;QAEzC,MAAM,IAAA,mBAAY,EAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QACjD,MAAM,qBAAqB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;KAChD;IAED,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxC,CAAC,CAAC;AA1BW,QAAA,cAAc,kBA0BzB;AAEF,MAAM,qBAAqB,GAAG,KAAK,EAAE,OAAgB,EAAE,SAAe,EAAE,EAAE;IACzE,KAAK,MAAM,GAAG,IAAI,iBAAiB,EAAE;QACpC,MAAM,QAAQ,GAAG;YAChB,OAAO,EAAE,IAAI;YACb,GAAG,EAAE,GAAG;YACR,SAAS,EAAE,SAAS;SACpB,CAAC;QACF,MAAM,MAAM,GAAG;YACd,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,cAAc,EAAE,OAAO;YACvB,OAAO,EAAE,MAAM;YACf,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;SACjC,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,MAAM;aACzB,MAAM,CAAC;YACP,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,cAAc,EAAE,OAAO;YACvB,OAAO,EAAE,MAAM;YACf,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;SACjC,CAAC;aACD,OAAO,EAAE,CAAC;KAEZ;AACF,CAAC,CAAC","sourcesContent":["/* eslint-disable @typescript-eslint/no-use-before-define */\r\nimport { S3, logBeforeTimeout } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService } from '@firestone-hs/reference-data';\r\nimport { Context } from 'aws-lambda';\r\nimport AWS from 'aws-sdk';\r\nimport { InternalBgsRow } from '../../internal-model';\r\nimport { buildHeroStats } from './hero-stats';\r\nimport { readRowsFromS3, saveRowsOnS3 } from './rows';\r\n\r\nexport const s3 = new S3();\r\nconst allCards = new AllCardsService();\r\nconst lambda = new AWS.Lambda();\r\n\r\nconst allMmrPercentiles: (100 | 50 | 25 | 10 | 1)[] = [100, 50, 25, 10, 1];\r\n\r\nexport const STATS_BUCKET = 'static.zerotoheroes.com';\r\nexport const STATS_KEY_PREFIX = `api/bgs/duo`;\r\nexport const WORKING_ROWS_FILE = `${STATS_KEY_PREFIX}/working/working-rows-%time%.json`;\r\nexport const HOURLY_KEY_HERO = `${STATS_KEY_PREFIX}/hero-stats/mmr-%mmrPercentile%/hourly/%startDate%.gz.json`;\r\nexport const HOURLY_KEY_QUEST = `${STATS_KEY_PREFIX}/quest-stats/mmr-%mmrPercentile%/hourly/%startDate%.gz.json`;\r\n\r\nexport default async (event, context: Context): Promise<any> => {\r\n\tawait handleNewStats(event, context);\r\n};\r\n\r\nexport const handleNewStats = async (event, context: Context) => {\r\n\tconst cleanup = logBeforeTimeout(context);\r\n\t// logger.log('event', event);\r\n\tawait allCards.initializeCardsDb();\r\n\r\n\tif (event.statsV2) {\r\n\t\tconst lastHourRows: readonly InternalBgsRow[] = await readRowsFromS3(event.startDate);\r\n\t\t// logger.log('building hero stats', event.startDate, lastHourRows?.length);\r\n\t\tawait buildHeroStats(event.startDate, event.mmr, lastHourRows, allCards);\r\n\t} else {\r\n\t\tconst startDate = new Date();\r\n\t\tstartDate.setMinutes(0);\r\n\t\tstartDate.setSeconds(0);\r\n\t\tstartDate.setMilliseconds(0);\r\n\t\tstartDate.setHours(startDate.getHours() - 1);\r\n\t\t// console.log('processStartDate', startDate);\r\n\t\t// End one hour later\r\n\t\tconst endDate = new Date(startDate);\r\n\t\tendDate.setHours(endDate.getHours() + 1);\r\n\r\n\t\tawait saveRowsOnS3(startDate, endDate, allCards);\r\n\t\tawait dispatchStatsV2Lambda(context, startDate);\r\n\t}\r\n\r\n\tcleanup();\r\n\treturn { statusCode: 200, body: null };\r\n};\r\n\r\nconst dispatchStatsV2Lambda = async (context: Context, startDate: Date) => {\r\n\tfor (const mmr of allMmrPercentiles) {\r\n\t\tconst newEvent = {\r\n\t\t\tstatsV2: true,\r\n\t\t\tmmr: mmr,\r\n\t\t\tstartDate: startDate,\r\n\t\t};\r\n\t\tconst params = {\r\n\t\t\tFunctionName: context.functionName,\r\n\t\t\tInvocationType: 'Event',\r\n\t\t\tLogType: 'Tail',\r\n\t\t\tPayload: JSON.stringify(newEvent),\r\n\t\t};\r\n\t\t// logger.log('\\tinvoking lambda', params);\r\n\t\tconst result = await lambda\r\n\t\t\t.invoke({\r\n\t\t\t\tFunctionName: context.functionName,\r\n\t\t\t\tInvocationType: 'Event',\r\n\t\t\t\tLogType: 'Tail',\r\n\t\t\t\tPayload: JSON.stringify(newEvent),\r\n\t\t\t})\r\n\t\t\t.promise();\r\n\t\t// logger.log('\\tinvocation result', result);\r\n\t}\r\n};\r\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builders.js","sourceRoot":"","sources":["../../../src/duos/hourly/builders.ts"],"names":[],"mappings":"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","sourcesContent":["import { InternalBgsRow } from '../../internal-model';\r\n\r\nexport const 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\t// Before that, there was an issue with disconnects, where the first turn after the\r\n\t// reconnect would be turn 0, leading to an inflation of early turn stats\r\n\tconst validRows = rows.filter((row) => row.id > 5348374);\r\n\tfor (const row of validRows) {\r\n\t\tif (!row.warbandStats?.length) {\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tlet parsed: readonly { turn: number; totalStats: number }[] = [];\r\n\t\ttry {\r\n\t\t\tparsed = JSON.parse(row.warbandStats);\r\n\t\t} catch (e) {\r\n\t\t\tconsole.warn('Could not parse warband stats', row.id, row.warbandStats, e);\r\n\t\t}\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 || isNaN(turnInfo.totalStats)) {\r\n\t\t\t\tcontinue;\r\n\t\t\t}\r\n\t\t\t// To avoid polluting the stats with big Tarecgosa outliers\r\n\t\t\tif (turnInfo.totalStats > 20000) {\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\nexport const 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// logger.debug('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// logger.debug('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\t// if (debug) {\r\n\t\t// \tlogger.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\t// if (debug) {\r\n\t\t\t// \tlogger.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\t// if (debug) {\r\n\t\t\t// \tlogger.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\t// if (debug) {\r\n\t\t\t// \tlogger.log('\\t existingInfo after', existingInfo);\r\n\t\t\t// }\r\n\t\t\tdata['' + turnInfo.turn] = existingInfo;\r\n\t\t\t// if (debug) {\r\n\t\t\t// \tlogger.log('\\t data', data);\r\n\t\t\t// }\r\n\t\t}\r\n\t}\r\n\r\n\t// if (debug) {\r\n\t// \tlogger.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\t// if (debug) {\r\n\t// \tlogger.log('\\t result', result);\r\n\t// }\r\n\treturn result;\r\n};\r\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { AllCardsService } from '@firestone-hs/reference-data';
|
|
2
|
-
import { InternalBgsRow } from '
|
|
3
|
-
import { BgsGlobalHeroStat } from '
|
|
2
|
+
import { InternalBgsRow } from '../../internal-model';
|
|
3
|
+
import { BgsGlobalHeroStat } from '../../models';
|
|
4
4
|
export declare const buildHeroStatsForMmr: (rows: readonly InternalBgsRow[], allCards: AllCardsService) => readonly BgsGlobalHeroStat[];
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildHeroStatsForMmr = void 0;
|
|
4
4
|
const aws_lambda_utils_1 = require("@firestone-hs/aws-lambda-utils");
|
|
5
|
-
const util_functions_1 = require("
|
|
5
|
+
const util_functions_1 = require("../../common/util-functions");
|
|
6
6
|
const builders_1 = require("./builders");
|
|
7
7
|
const utils_1 = require("./utils");
|
|
8
8
|
const buildHeroStatsForMmr = (rows, allCards) => {
|
|
@@ -45,12 +45,15 @@ const buildTribeStats = (rows, refAveragePosition) => {
|
|
|
45
45
|
const rowsForTribe = rows.filter((r) => r.tribesExpanded.includes(tribe));
|
|
46
46
|
const rowsWithoutTribe = rows.filter((r) => !r.tribesExpanded.includes(tribe));
|
|
47
47
|
const averagePosition = average(rowsForTribe.map((r) => r.rank));
|
|
48
|
+
const averagePositionWithoutTribe = average(rowsWithoutTribe.map((r) => r.rank));
|
|
48
49
|
const result = {
|
|
49
50
|
tribe: tribe,
|
|
50
51
|
dataPoints: rowsForTribe.length,
|
|
51
52
|
dataPointsOnMissingTribe: rowsWithoutTribe.length,
|
|
52
|
-
averagePosition:
|
|
53
|
-
|
|
53
|
+
averagePosition: averagePosition,
|
|
54
|
+
averagePositionWithoutTribe: averagePositionWithoutTribe,
|
|
55
|
+
impactAveragePosition: averagePosition - refAveragePosition,
|
|
56
|
+
impactAveragePositionVsMissingTribe: averagePosition - averagePositionWithoutTribe,
|
|
54
57
|
};
|
|
55
58
|
return result;
|
|
56
59
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hero-stats-buikder.js","sourceRoot":"","sources":["../../../src/duos/hourly/hero-stats-buikder.ts"],"names":[],"mappings":";;;AAAA,qEAAiE;AAEjE,gEAAyE;AAGzE,yCAAmE;AACnE,mCAAqD;AAE9C,MAAM,oBAAoB,GAAG,CACnC,IAA+B,EAC/B,QAAyB,EACM,EAAE;IAEjC,MAAM,aAAa,GAEf,IAAA,kCAAe,EAAC,CAAC,GAAmB,EAAE,EAAE,CAAC,IAAA,oCAAmB,EAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAClG,OAAO,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC;AACtF,CAAC,CAAC;AATW,QAAA,oBAAoB,wBAS/B;AAGF,MAAM,uBAAuB,GAAG,CAAC,IAA+B,EAAqB,EAAE;IAEtF,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,MAAM,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEzD,MAAM,qBAAqB,GAAG,IAAA,kCAA0B,EAAC,IAAI,CAAC,CAAC;IAG/D,MAAM,iBAAiB,GAAG,IAAA,6BAAkB,EAAC,IAAI,CAAC,CAAC;IAMnD,MAAM,eAAe,GAAG,IAAA,4BAAiB,EAAC,IAAI,CAAC,CAAC;IAMhD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,aAAa,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC;IAC/D,MAAM,gBAAgB,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAClE,MAAM,YAAY,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACjE,MAAM,QAAQ,GAAG,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5C,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,0BAA0B,GAAG,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAE9E,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;IAK1D,MAAM,MAAM,GAAsB;QACjC,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,UAAU,EAAE,IAAI,CAAC,MAAM;QACvB,eAAe,EAAE,IAAA,sBAAK,EAAC,eAAe,CAAC;QACvC,iBAAiB,EAAE,IAAA,sBAAK,EAAC,iBAAiB,CAAC;QAC3C,0BAA0B,EAAE,IAAA,sBAAK,EAAC,0BAA0B,CAAC;QAC7D,4BAA4B,EAAE,IAAA,sBAAK,EAAC,eAAe,GAAG,CAAC,GAAG,0BAA0B,CAAC;QACrF,wBAAwB,EAAE,qBAAqB;QAC/C,gBAAgB,EAAE,iBAAiB;QACnC,eAAe,EAAE,eAAe;QAChC,UAAU,EAAE,UAAU;QACtB,YAAY,EAAE,IAAI;KAClB,CAAC;IAGF,OAAO,MAAM,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,IAA+B,EAAE,kBAA0B,EAA+B,EAAE;IACpH,MAAM,YAAY,GAAoB,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IAC1F,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACjC,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1E,MAAM,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/E,MAAM,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACjE,MAAM,2BAA2B,GAAG,OAAO,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACjF,MAAM,MAAM,GAAqB;YAChC,KAAK,EAAE,KAAK;YACZ,UAAU,EAAE,YAAY,CAAC,MAAM;YAC/B,wBAAwB,EAAE,gBAAgB,CAAC,MAAM;YACjD,eAAe,EAAE,eAAe;YAChC,2BAA2B,EAAE,2BAA2B;YACxD,qBAAqB,EAAE,eAAe,GAAG,kBAAkB;YAC3D,mCAAmC,EAAE,eAAe,GAAG,2BAA2B;SAClF,CAAC;QACF,OAAO,MAAM,CAAC;IACf,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC;AAqEF,MAAM,OAAO,GAAG,CAAC,IAAuB,EAAU,EAAE;IACnD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;AACtD,CAAC,CAAC","sourcesContent":["import { groupByFunction } from '@firestone-hs/aws-lambda-utils';\r\nimport { AllCardsService, Race } from '@firestone-hs/reference-data';\r\nimport { normalizeHeroCardId, round } from '../../common/util-functions';\r\nimport { InternalBgsRow } from '../../internal-model';\r\nimport { BgsGlobalHeroStat, BgsHeroTribeStat } from '../../models';\r\nimport { buildCombatWinrate, buildWarbandStats } from './builders';\r\nimport { buildPlacementDistribution } from './utils';\r\n\r\nexport const buildHeroStatsForMmr = (\r\n\trows: readonly InternalBgsRow[],\r\n\tallCards: AllCardsService,\r\n): readonly BgsGlobalHeroStat[] => {\r\n\t// This takes about 3s, so not impactful\r\n\tconst groupedByHero: {\r\n\t\t[questCardId: string]: readonly InternalBgsRow[];\r\n\t} = groupByFunction((row: InternalBgsRow) => normalizeHeroCardId(row.heroCardId, allCards))(rows);\r\n\treturn Object.values(groupedByHero).flatMap((data) => buildStatsForSingleHero(data));\r\n};\r\n\r\n// All rows here belong to a single hero\r\nconst buildStatsForSingleHero = (rows: readonly InternalBgsRow[]): BgsGlobalHeroStat => {\r\n\t// const startTime = new Date().getTime();\r\n\tconst ref = rows[0];\r\n\tconst averagePosition = average(rows.map((r) => r.rank));\r\n\t// const placementStartTime = new Date().getTime();\r\n\tconst placementDistribution = buildPlacementDistribution(rows);\r\n\t// const placementProcessTime = new Date().getTime() - placementStartTime;\r\n\t// const winrateStartTime = new Date().getTime();\r\n\tconst rawCombatWinrates = buildCombatWinrate(rows);\r\n\t// const winrateProcessTime = new Date().getTime() - winrateStartTime;\r\n\t// const combatWinrate: readonly { turn: number; winrate: number }[] = rawCombatWinrates.map((info) => ({\r\n\t// \tturn: info.turn,\r\n\t// \twinrate: round(info.totalWinrate / info.dataPoints),\r\n\t// }));\r\n\tconst rawWarbandStats = buildWarbandStats(rows);\r\n\t// const warbandStats: readonly { turn: number; averageStats: number }[] = rawWarbandStats.map((info) => ({\r\n\t// \tturn: info.turn,\r\n\t// \taverageStats: round(info.totalStats / info.dataPoints),\r\n\t// }));\r\n\r\n\tconst allRanks = rows.map((r) => r.rank);\r\n\tconst allDeviations = allRanks.map((r) => averagePosition - r);\r\n\tconst squareDeviations = allDeviations.map((d) => Math.pow(d, 2));\r\n\tconst sumOfSquares = squareDeviations.reduce((a, b) => a + b, 0);\r\n\tconst variance = sumOfSquares / rows.length;\r\n\tconst standardDeviation = Math.sqrt(variance);\r\n\tconst standardDeviationOfTheMean = standardDeviation / Math.sqrt(rows.length);\r\n\t// const tribeStartTime = new Date().getTime();\r\n\tconst tribeStats = buildTribeStats(rows, averagePosition);\r\n\t// const tribeProcessTime = new Date().getTime() - tribeStartTime;\r\n\t// const anomalyStartTime = new Date().getTime();\r\n\t// const anomalyStats = buildAnomalyStats(rows, averagePosition, placementDistribution, combatWinrate, warbandStats);\r\n\t// const anomalyProcessTime = new Date().getTime() - anomalyStartTime;\r\n\tconst result: BgsGlobalHeroStat = {\r\n\t\theroCardId: ref.heroCardId,\r\n\t\tdataPoints: rows.length,\r\n\t\taveragePosition: round(averagePosition),\r\n\t\tstandardDeviation: round(standardDeviation),\r\n\t\tstandardDeviationOfTheMean: round(standardDeviationOfTheMean),\r\n\t\tconservativePositionEstimate: round(averagePosition + 3 * standardDeviationOfTheMean),\r\n\t\tplacementDistributionRaw: placementDistribution,\r\n\t\tcombatWinrateRaw: rawCombatWinrates,\r\n\t\twarbandStatsRaw: rawWarbandStats,\r\n\t\ttribeStats: tribeStats,\r\n\t\tanomalyStats: null, //anomalyStats,\r\n\t};\r\n\t// const processTime = new Date().getTime() - startTime;\r\n\t// console.log('\\tbuilt for hero', result.heroCardId, result.dataPoints, result);\r\n\treturn result;\r\n};\r\n\r\nconst buildTribeStats = (rows: readonly InternalBgsRow[], refAveragePosition: number): readonly BgsHeroTribeStat[] => {\r\n\tconst uniqueTribes: readonly Race[] = [...new Set(rows.flatMap((r) => r.tribesExpanded))];\r\n\treturn uniqueTribes.map((tribe) => {\r\n\t\tconst rowsForTribe = rows.filter((r) => r.tribesExpanded.includes(tribe));\r\n\t\tconst rowsWithoutTribe = rows.filter((r) => !r.tribesExpanded.includes(tribe));\r\n\t\tconst averagePosition = average(rowsForTribe.map((r) => r.rank));\r\n\t\tconst averagePositionWithoutTribe = average(rowsWithoutTribe.map((r) => r.rank));\r\n\t\tconst result: BgsHeroTribeStat = {\r\n\t\t\ttribe: tribe,\r\n\t\t\tdataPoints: rowsForTribe.length,\r\n\t\t\tdataPointsOnMissingTribe: rowsWithoutTribe.length,\r\n\t\t\taveragePosition: averagePosition,\r\n\t\t\taveragePositionWithoutTribe: averagePositionWithoutTribe,\r\n\t\t\timpactAveragePosition: averagePosition - refAveragePosition,\r\n\t\t\timpactAveragePositionVsMissingTribe: averagePosition - averagePositionWithoutTribe,\r\n\t\t};\r\n\t\treturn result;\r\n\t});\r\n};\r\n\r\n// const buildAnomalyStats = (\r\n// \trows: readonly InternalBgsRow[],\r\n// \trefAveragePosition: number,\r\n// \trefPlacementDistribution: readonly { rank: number; percentage: number }[],\r\n// \trefCombatWinrate: readonly { turn: number; winrate: number }[],\r\n// \trefWarbandStats: readonly { turn: number; averageStats: number }[],\r\n// ): readonly BgsHeroAnomalyStat[] => {\r\n// \tconst rowsWithAnomalies = rows.filter((r) => !!r.bgsAnomalies?.length);\r\n// \tconst uniqueAnomalies: readonly string[] = [...new Set(rowsWithAnomalies.flatMap((r) => r.bgsAnomalies))];\r\n// \treturn uniqueAnomalies.map((anomaly) => {\r\n// \t\tconst rowsForAnomaly = rowsWithAnomalies.filter((r) => r.bgsAnomalies.includes(anomaly));\r\n// \t\tconst averagePosition = average(rowsForAnomaly.map((r) => r.rank));\r\n// \t\tconst placementDistribution = buildPlacementDistribution(rowsForAnomaly);\r\n// \t\tconst rawCombatWinrates = buildCombatWinrate(rowsForAnomaly);\r\n// \t\tconst combatWinrate = rawCombatWinrates.map((info) => ({\r\n// \t\t\tturn: info.turn,\r\n// \t\t\twinrate: round(info.totalWinrate / info.dataPoints),\r\n// \t\t}));\r\n// \t\tconst rawWarbandStats = buildWarbandStats(rowsForAnomaly);\r\n// \t\tconst warbandStats: readonly { turn: number; averageStats: number }[] = rawWarbandStats.map((info) => ({\r\n// \t\t\tturn: info.turn,\r\n// \t\t\taverageStats: round(info.totalStats / info.dataPoints),\r\n// \t\t}));\r\n// \t\tconst result: BgsHeroAnomalyStat = {\r\n// \t\t\tanomaly: anomaly,\r\n// \t\t\tdataPoints: rowsForAnomaly.length,\r\n// \t\t\taveragePosition: round(averagePosition),\r\n// \t\t\timpactAveragePosition: round(averagePosition - refAveragePosition),\r\n// \t\t\tplacementDistributionRaw: placementDistribution,\r\n// \t\t\timpactPlacementDistribution: refPlacementDistribution.map((p) => {\r\n// \t\t\t\tconst newPlacementInfo = placementDistribution.find((p2) => p2.rank === p.rank);\r\n// \t\t\t\t// Cna happen when there isn't a lot of data points, typically for high MMR\r\n// \t\t\t\tif (!newPlacementInfo) {\r\n// \t\t\t\t\t// console.log('missing placement info', placementDistribution, p);\r\n// \t\t\t\t}\r\n// \t\t\t\treturn {\r\n// \t\t\t\t\trank: p.rank,\r\n// \t\t\t\t\timpact: round((newPlacementInfo?.percentage ?? 0) - p.percentage),\r\n// \t\t\t\t};\r\n// \t\t\t}),\r\n// \t\t\tcombatWinrate: combatWinrate,\r\n// \t\t\timpactCombatWinrate: refCombatWinrate.map((c) => {\r\n// \t\t\t\tconst newCombatWinrate = combatWinrate.find((c2) => c2.turn === c.turn);\r\n// \t\t\t\tif (!newCombatWinrate) {\r\n// \t\t\t\t\t// console.debug('missing winrate info', combatWinrate);\r\n// \t\t\t\t}\r\n// \t\t\t\treturn {\r\n// \t\t\t\t\tturn: c.turn,\r\n// \t\t\t\t\timpact: round((newCombatWinrate?.winrate ?? 0) - c.winrate),\r\n// \t\t\t\t};\r\n// \t\t\t}),\r\n// \t\t\twarbandStats: warbandStats,\r\n// \t\t\timpactWarbandStats: refWarbandStats.map((c) => {\r\n// \t\t\t\tconst newWarbandStats = warbandStats.find((c2) => c2.turn === c.turn);\r\n// \t\t\t\tif (!newWarbandStats) {\r\n// \t\t\t\t\t// console.debug('missing warband info', warbandStats);\r\n// \t\t\t\t}\r\n// \t\t\t\treturn {\r\n// \t\t\t\t\tturn: c.turn,\r\n// \t\t\t\t\timpact: round((newWarbandStats?.averageStats ?? 0) - c.averageStats),\r\n// \t\t\t\t};\r\n// \t\t\t}),\r\n// \t\t};\r\n// \t\treturn result;\r\n// \t});\r\n// };\r\n\r\nconst average = (data: readonly number[]): number => {\r\n\treturn data.reduce((a, b) => a + b, 0) / data.length;\r\n};\r\n"]}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { AllCardsService } from '@firestone-hs/reference-data';
|
|
2
|
-
import { InternalBgsRow } from '
|
|
2
|
+
import { InternalBgsRow } from '../../internal-model';
|
|
3
3
|
export declare const buildHeroStats: (startDate: string, percentile: 100 | 50 | 25 | 10 | 1, rows: readonly InternalBgsRow[], allCards: AllCardsService) => Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hero-stats.js","sourceRoot":"","sources":["../../../src/duos/hourly/hero-stats.ts"],"names":[],"mappings":";;;AACA,+BAAgC;AAGhC,uFAAsF;AACtF,6DAA4D;AAC5D,mCAA8C;AAEvC,MAAM,cAAc,GAAG,KAAK,EAClC,SAAiB,EACjB,UAAkC,EAClC,IAA+B,EAC/B,QAAyB,EACxB,EAAE;IACH,MAAM,cAAc,GAA6B,IAAA,2BAAmB,EAAC,IAAI,CAAC,CAAC;IAC3E,MAAM,aAAa,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC;IAC9E,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,UAAU,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC;IAE1G,MAAM,SAAS,GAAiC,IAAA,yCAAoB,EAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACxF,MAAM,OAAO,GAAmB;QAC/B,cAAc,EAAE,IAAI,IAAI,EAAE;QAC1B,cAAc,EAAE,cAAc;QAC9B,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACnC,GAAG,IAAI;YACP,aAAa,EAAE,UAAU;YACzB,UAAU,EAAE,IAAI;SAChB,CAAC,CAAC;QACH,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;KACzE,CAAC;IAEF,MAAM,WAAW,GAAG,iDAAe,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;IAClH,MAAM,oCAAE,CAAC,SAAS,CAAC,IAAA,eAAQ,EAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,8CAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,CAAC,CAAC;AAE9G,CAAC,CAAC;AAzBW,QAAA,cAAc,kBAyBzB","sourcesContent":["import { AllCardsService } from '@firestone-hs/reference-data';\r\nimport { gzipSync } from 'zlib';\r\nimport { InternalBgsRow } from '../../internal-model';\r\nimport { BgsGlobalHeroStat, BgsHeroStatsV2, MmrPercentile } from '../../models';\r\nimport { HOURLY_KEY_HERO, STATS_BUCKET, s3 } from './_build-battlegrounds-hero-stats';\r\nimport { buildHeroStatsForMmr } from './hero-stats-buikder';\r\nimport { buildMmrPercentiles } from './utils';\r\n\r\nexport const buildHeroStats = async (\r\n\tstartDate: string,\r\n\tpercentile: 100 | 50 | 25 | 10 | 1,\r\n\trows: readonly InternalBgsRow[],\r\n\tallCards: AllCardsService,\r\n) => {\r\n\tconst mmrPercentiles: readonly MmrPercentile[] = buildMmrPercentiles(rows);\r\n\tconst mmrPercentile = mmrPercentiles.find((p) => p.percentile === percentile);\r\n\tconst mmrRows = rows.filter((row) => mmrPercentile.percentile === 100 || row.rating >= mmrPercentile.mmr);\r\n\r\n\tconst heroStats: readonly BgsGlobalHeroStat[] = buildHeroStatsForMmr(mmrRows, allCards);\r\n\tconst statsV2: BgsHeroStatsV2 = {\r\n\t\tlastUpdateDate: new Date(),\r\n\t\tmmrPercentiles: mmrPercentiles,\r\n\t\theroStats: heroStats.map((stat) => ({\r\n\t\t\t...stat,\r\n\t\t\tmmrPercentile: percentile,\r\n\t\t\ttimePeriod: null,\r\n\t\t})),\r\n\t\tdataPoints: heroStats.map((s) => s.dataPoints).reduce((a, b) => a + b, 0),\r\n\t};\r\n\t// logger.log('\\tbuilt stats', statsV2.dataPoints, statsV2.heroStats?.length);\r\n\tconst destination = HOURLY_KEY_HERO.replace('%mmrPercentile%', `${percentile}`).replace('%startDate%', startDate);\r\n\tawait s3.writeFile(gzipSync(JSON.stringify(statsV2)), STATS_BUCKET, destination, 'application/json', 'gzip');\r\n\t// console.log('written file', destination);\r\n};\r\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { AllCardsService } from '@firestone-hs/reference-data';
|
|
2
|
-
import { InternalBgsRow } from '
|
|
2
|
+
import { InternalBgsRow } from '../../internal-model';
|
|
3
3
|
export declare const readRowsFromS3: (startDate: string) => Promise<readonly InternalBgsRow[]>;
|
|
4
4
|
export declare const saveRowsOnS3: (startDate: Date, endDate: Date, allCards: AllCardsService) => Promise<void>;
|