From 9a20bfb8c85d263f73b0fb9e5e1994b375070119 Mon Sep 17 00:00:00 2001 From: iamBadgers Date: Sat, 8 Jun 2024 12:20:49 -0700 Subject: [PATCH] move overall stats to its own service file --- backend/src/app.ts | 120 +------------------------------- backend/src/characterservice.ts | 2 +- backend/src/db.ts | 25 +++++++ backend/src/gameservice.ts | 10 +-- backend/src/rushstatsservice.ts | 115 ++++++++++++++++++++++++++++++ 5 files changed, 149 insertions(+), 123 deletions(-) create mode 100644 backend/src/rushstatsservice.ts diff --git a/backend/src/app.ts b/backend/src/app.ts index aee72f5..5f50c87 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -5,6 +5,7 @@ import { database, Character, Game, Pick, App } from './db' import { OrderByParser, FilterParser } from './tokenizer' import { addGameApis } from './gameservice' import { addCharacterApis } from './characterservice' +import { addRushStatsApis } from './rushstatsservice' const app = express() const jsonParser = json() @@ -12,125 +13,8 @@ const port = 3001 addGameApis(app, jsonParser) addCharacterApis(app, jsonParser) +addRushStatsApis(app, jsonParser) -app.post('/api/serverstats/gamestats', jsonParser, async (req, res) => { - const monthId = req.body.monthId - let startSeconds = 0 - let endSeconds = Number.MAX_SAFE_INTEGER - - if (monthId != -1) { - const month = monthId % 12 - const year = Math.floor(monthId / 12) + 2023 - - const startDate = new Date(year, month, 1) - const endDate = new Date(year, month + 1, -1) - - startSeconds = startDate.getTime() / 1000 - endSeconds = endDate.getTime() / 1000 - } - - const serverGameStats = await database.query( - 'SELECT ' + - 'COUNT(*) as TotalGames, ' + - 'SUM(CASE WHEN status = "Complete" THEN 1 ELSE 0 END) as Complete, ' + - 'SUM(CASE WHEN status = "Postponed" THEN 1 ELSE 0 END) as Postponed, ' + - 'SUM(CASE WHEN status = "Pending" THEN 1 ELSE 0 END) as Pending, ' + - 'SUM(CASE WHEN event = "TRUE" THEN 1 ELSE 0 END) as Events, ' + - 'SUM(CASE WHEN fix = "TRUE" THEN 1 ELSE 0 END) as Fixes, ' + - 'SUM(payoutEB) as TotalEB, ' + - 'SUM(payoutIP) as TotalIP, ' + - 'SUM(payoutEB) / SUM(CASE WHEN status = "Complete" THEN 1 ELSE 0 END) as AverageEB, ' + - 'SUM(payoutIP) / SUM(CASE WHEN status = "Complete" THEN 1 ELSE 0 END) as AverageIP ' + - 'FROM Games WHERE postdate BETWEEN :startSeconds AND :endSeconds', - { - type: QueryTypes.SELECT, - replacements: { - startSeconds, - endSeconds - } - } - ) - res.send(serverGameStats[0]) -}) - -app.post('/api/serverstats/rolestats', jsonParser, async (req, res) => { - const monthId = req.body.monthId - - let startSeconds = 0 - let endSeconds = Number.MAX_SAFE_INTEGER - - if (monthId != -1) { - const month = monthId % 12 - const year = Math.floor(monthId / 12) + 2023 - - const startDate = new Date(year, month, 1) - const endDate = new Date(year, month + 1, -1) - - startSeconds = startDate.getTime() / 1000 - endSeconds = endDate.getTime() / 1000 - } - - const games = await Game.findAll({ - include: [ - { model: Character, as: 'characterPickedForGame' }, - { model: Character, as: 'characterAppliedForGame' } - ], - where: { postdate: { [Op.between]: [startSeconds, endSeconds] } } - }) - - // count active roles - let activeCharacters = new Map() - let pickedCharacterCount = new Map() - let appedCharacterCount = new Map() - - games.forEach((game, gameNum) => { - const picks = game.dataValues.characterPickedForGame - const appls = game.dataValues.characterAppliedForGame - - picks.forEach((character, characterNum) => { - const role = character.dataValues.role - // Count role application - pickedCharacterCount.set(role, (pickedCharacterCount.get(role) || 0) + 1) - }) - - appls.forEach((character, characterNum) => { - const role = character.dataValues.role - const charId = character.dataValues.id - // Add apllied characters to active list - if (!activeCharacters.has(role)) { - activeCharacters.set(role, new Set()) - } - activeCharacters.get(role).add(charId) - // Count role application - appedCharacterCount.set(role, (appedCharacterCount.get(role) || 0) + 1) - }) - }) - - const roleNames = [ - 'Fixer', - 'Tech', - 'Medtech', - 'Media', - 'Netrunner', - 'Solo', - 'Nomad', - 'Exec', - 'Lawman', - 'Rocker' - ] - - const result = {} - - roleNames.forEach((roleName) => { - result[roleName] = { - apps: appedCharacterCount.get(roleName) || 0, - picks: pickedCharacterCount.get(roleName) || 0, - active: activeCharacters.has(roleName) ? activeCharacters.get(roleName).size : 0 - } - }) - - res.send(result) -}) app.use(express.static(__dirname + '/frontend')) app.use('/', (req, res) => { diff --git a/backend/src/characterservice.ts b/backend/src/characterservice.ts index ba8e33e..a4c0340 100644 --- a/backend/src/characterservice.ts +++ b/backend/src/characterservice.ts @@ -88,7 +88,7 @@ export function addCharacterApis(app, jsonParser) { if (gameHistory) { res.send(gameHistory) } else { - res.status(404).send("Could nor find character.") + res.status(404).send('Could nor find character.') } } catch (e) { res.status(500).send(e) diff --git a/backend/src/db.ts b/backend/src/db.ts index c12e568..2cbe422 100644 --- a/backend/src/db.ts +++ b/backend/src/db.ts @@ -1,5 +1,30 @@ import { Sequelize, Model, DataTypes } from 'sequelize' +export type RoleName = + | 'Fixer' + | 'Tech' + | 'Medtech' + | 'Media' + | 'Netrunner' + | 'Solo' + | 'Nomad' + | 'Exec' + | 'Lawman' + | 'Rocker' + +export const roleNames: RoleName[] = [ + 'Fixer', + 'Tech', + 'Medtech', + 'Media', + 'Netrunner', + 'Solo', + 'Nomad', + 'Exec', + 'Lawman', + 'Rocker' +] + const databasePath = './testdb.db' export const database = new Sequelize({ diff --git a/backend/src/gameservice.ts b/backend/src/gameservice.ts index 6e3fdc8..0ac3398 100644 --- a/backend/src/gameservice.ts +++ b/backend/src/gameservice.ts @@ -51,8 +51,9 @@ export function addGameApis(app, jsonParser) { app.post('/api/game/:gameId/apps', async (req, res) => { try { const apps = await App.findAll({ - include: { model: Character, as: "appliedCharacter"}, - where: { gameId: req.params['gameId'] } }) + include: { model: Character, as: 'appliedCharacter' }, + where: { gameId: req.params['gameId'] } + }) if (!apps) { res.status(404).send('Apps not found.') } else { @@ -66,8 +67,9 @@ export function addGameApis(app, jsonParser) { app.post('/api/game/:gameId/picks', async (req, res) => { try { const picks = await Pick.findAll({ - include: { model: Character, as: "pickedCharacter"}, - where: { gameId: req.params['gameId'] } }) + include: { model: Character, as: 'pickedCharacter' }, + where: { gameId: req.params['gameId'] } + }) if (!picks) { res.status(404).send('Picks not found') } else { diff --git a/backend/src/rushstatsservice.ts b/backend/src/rushstatsservice.ts new file mode 100644 index 0000000..7d9dd1d --- /dev/null +++ b/backend/src/rushstatsservice.ts @@ -0,0 +1,115 @@ +import { database, Character, Game, Pick, App, roleNames } from './db' +import { Op, QueryTypes } from 'sequelize' + +const serverGameStatsQuery = + 'SELECT ' + + 'COUNT(*) as TotalGames, ' + + 'SUM(CASE WHEN status = "Complete" THEN 1 ELSE 0 END) as Complete, ' + + 'SUM(CASE WHEN status = "Postponed" THEN 1 ELSE 0 END) as Postponed, ' + + 'SUM(CASE WHEN status = "Pending" THEN 1 ELSE 0 END) as Pending, ' + + 'SUM(CASE WHEN event = "TRUE" THEN 1 ELSE 0 END) as Events, ' + + 'SUM(CASE WHEN fix = "TRUE" THEN 1 ELSE 0 END) as Fixes, ' + + 'SUM(payoutEB) as TotalEB, ' + + 'SUM(payoutIP) as TotalIP, ' + + 'SUM(payoutEB) / SUM(CASE WHEN status = "Complete" THEN 1 ELSE 0 END) as AverageEB, ' + + 'SUM(payoutIP) / SUM(CASE WHEN status = "Complete" THEN 1 ELSE 0 END) as AverageIP ' + + 'FROM Games WHERE postdate BETWEEN :startSeconds AND :endSeconds' + +const startingYear = 2023 + +function getMonthNumber(monthId: number): number { + return monthId % 12 +} + +function getYearNumber(monthId: number): number { + return Math.floor(monthId / 12 + startingYear) +} + +function monthIdToStartSeconds(monthId: number): number { + if (monthId) { + const yearNumber = getYearNumber(monthId) + const monthNumber = getMonthNumber(monthId) + return new Date(yearNumber, monthNumber, 1).getTime() / 1000 + } + return 0 +} + +function monthIdToEndSeconds(monthId: number): number { + if (monthId) { + const yearNumber = getYearNumber(monthId) + const monthNumber = getMonthNumber(monthId) + return new Date(yearNumber, monthNumber + 1, -1).getTime() / 1000 + } + return 0 +} + +export function addRushStatsApis(app, jsonParser) { + app.post('/api/serverstats/gamestats', jsonParser, async (req, res) => { + const monthId = req.body.monthId + let startSeconds = monthIdToStartSeconds(monthId) + let endSeconds = monthIdToEndSeconds(monthId) + + const serverGameStats = await database.query(serverGameStatsQuery, { + type: QueryTypes.SELECT, + replacements: { + startSeconds, + endSeconds + } + }) + res.send(serverGameStats[0]) + }) + + app.post('/api/serverstats/rolestats', jsonParser, async (req, res) => { + const monthId = req.body.monthId + let startSeconds = monthIdToStartSeconds(monthId) + let endSeconds = monthIdToEndSeconds(monthId) + + const games = await Game.findAll({ + include: [ + { model: Character, as: 'characterPickedForGame' }, + { model: Character, as: 'characterAppliedForGame' } + ], + where: { postdate: { [Op.between]: [startSeconds, endSeconds] } } + }) + + // count active roles + let activeCharacters = new Map() + let pickedCharacterCount = new Map() + let appedCharacterCount = new Map() + + games.forEach((game, gameNum) => { + const picks = game.dataValues.characterPickedForGame + const appls = game.dataValues.characterAppliedForGame + + picks.forEach((character, characterNum) => { + const role = character.dataValues.role + // Count role application + pickedCharacterCount.set(role, (pickedCharacterCount.get(role) || 0) + 1) + }) + + appls.forEach((character, characterNum) => { + const role = character.dataValues.role + const charId = character.dataValues.id + // Add apllied characters to active list + if (!activeCharacters.has(role)) { + activeCharacters.set(role, new Set()) + } + activeCharacters.get(role).add(charId) + // Count role application + appedCharacterCount.set(role, (appedCharacterCount.get(role) || 0) + 1) + }) + }) + + const result = {} + + roleNames.forEach((roleName) => { + result[roleName] = { + apps: appedCharacterCount.get(roleName) || 0, + picks: pickedCharacterCount.get(roleName) || 0, + active: activeCharacters.has(roleName) ? activeCharacters.get(roleName).size : 0 + } + }) + + res.send(result) + }) +}