diff --git a/.gitignore b/.gitignore index b985a57..f99e79e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ node_modules notes.md app/t2-stat-parser/serverStats/stats +app/t2-stat-parser/serverStats/mlData +app/t2-stat-parser/serverStats/lData docker-compose.deploy.yml traefik.yml diff --git a/app/t2-stat-parser/main.go b/app/t2-stat-parser/main.go index 10079d3..bf68e57 100644 --- a/app/t2-stat-parser/main.go +++ b/app/t2-stat-parser/main.go @@ -14,9 +14,9 @@ import ( ) func main() { - fmt.Println("Starting FTP stat file download") + // fmt.Println("Starting FTP stat file download") initFTP() - fmt.Println("Stat files downloaded!") + // fmt.Println("Stat files downloaded!") fmt.Println("Starting stat parser") initParser() diff --git a/app/t2-stat-parser/parser.go b/app/t2-stat-parser/parser.go index 200dbb0..cab5af2 100644 --- a/app/t2-stat-parser/parser.go +++ b/app/t2-stat-parser/parser.go @@ -59,12 +59,12 @@ type Game struct { dbStatOverWrite int `db.players:"stat_overwrite"` statOverWrite int - gameMap string `db.games:"map"` - gameID int `db.games:"game_id"` - gameType string `db.games:"gametype"` - dateStamp string `db.games:"datestamp"` - stats string `db.games:"stats"` - uuid string `db.games:"uuid"` + gameMap string `db.game_detail:"map"` + gameID int `db.game_detail:"game_id"` + gameType string `db.game_detail:"gametype"` + dateStamp string `db.game_detail:"datestamp"` + stats string `db.game_detail:"stats"` + uuid string `db.game_detail:"uuid"` } func initParser() { @@ -265,7 +265,10 @@ func parseStatOverWriteLine(g Game, mStatLine map[string][]string, arrPosition i fmt.Println(g) } - if checkGameEntry(g) == false && g.gameID != 0 { + // Check if we need to create a new game record + checkGameRecord(g) + + if checkGameEntryForPlayer(g) == false && g.gameID != 0 { fmt.Println("does not exist, add") // insert game stat addPlayerGameStat(g, strings.ToLower(gt)) @@ -283,6 +286,28 @@ func rowExists(query string, args ...interface{}) bool { return exists } +func checkGameRecord(g Game) { + check := rowExists("select game_id from games where game_id = $1 and map = $2", g.gameID, g.gameMap) + if !check { + createGame(g) + } else { + fmt.Println("Game Record ", g.gameID, g.gameMap, " already exists") + } +} +func createGame(g Game) { + fmt.Println("Creating new Game ", g.gameMap, g.gameID, g.dateStamp, g.gameType) + + if g.gameID != 0 { + sqlInsert := `insert into games(map, game_id, datestamp, gametype) values($1,$2,$3,$4)` + _, err := db.Exec(sqlInsert, g.gameMap, g.gameID, g.dateStamp, g.gameType) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to create new game record (Possible Dupe ID): %v\n", err) + // Don't exit - just skip this insert + // os.Exit(1) + } + } +} + func checkPlayer(g Game) { check := rowExists("select player_guid from players where player_guid = $1", g.playerGUID) if !check { @@ -292,8 +317,8 @@ func checkPlayer(g Game) { } } -func checkGameEntry(g Game) bool { - check := rowExists("select player_guid from games where player_guid = $1 and game_id = $2 and map = $3", g.playerGUID, g.gameID, g.gameMap) +func checkGameEntryForPlayer(g Game) bool { + check := rowExists("select player_guid from game_detail where player_guid = $1 and game_id = $2 and map = $3", g.playerGUID, g.gameID, g.gameMap) if !check { return false } else { @@ -341,7 +366,7 @@ func addPlayerGameStat(g Game, gt string) { if g.dateStamp != "0" { // Insert new stat line fmt.Println("New stat line!", g.playerName, g.dateStamp) - sqlInsert := `insert into games(player_guid, player_name, stat_overwrite, map, game_id, stats, datestamp, uuid, gametype) values($1,$2,$3,$4,$5,$6,$7,$8,$9)` + sqlInsert := `insert into game_detail(player_guid, player_name, stat_overwrite, map, game_id, stats, datestamp, uuid, gametype) values($1,$2,$3,$4,$5,$6,$7,$8,$9)` _, err := db.Exec(sqlInsert, g.playerGUID, g.playerName, g.statOverWrite, g.gameMap, g.gameID, g.stats, g.dateStamp, g.uuid, g.gameType) if err != nil { fmt.Fprintf(os.Stderr, "Unable to add player's game stat: %v\n", err) diff --git a/app/t2-stat-parser/serverStats/dir.txt b/app/t2-stat-parser/serverStats/dir.txt new file mode 100644 index 0000000..19910b3 --- /dev/null +++ b/app/t2-stat-parser/serverStats/dir.txt @@ -0,0 +1,7 @@ +./lData +./mlData + +./stats/CTFGame +./stats/DMGame +./stats/LakRabbitGame +./stats/SCTF/Game \ No newline at end of file diff --git a/app/webapp/app/Controllers/Http/GameController.js b/app/webapp/app/Controllers/Http/GameController.js index 54fcef7..7ac4ea8 100644 --- a/app/webapp/app/Controllers/Http/GameController.js +++ b/app/webapp/app/Controllers/Http/GameController.js @@ -1,71 +1,64 @@ -'use strict' +'use strict'; -const Database = use('Database') +const Database = use('Database'); class GameController { + // /games + async index({ inertia }) { + const pageTitle = `Latest Games`; - // /games - async index({ inertia }) { + // const gamesQry = await Database.raw(` + // SELECT distinct game_id,map,gametype, datestamp + // FROM games + // WHERE (stats->>'score' IS NOT NULL) AND (game_id <> 0) + // ORDER BY game_id desc + // LIMIT 2000; + // `); - const pageTitle = `Latest Games`; + // const gamesQry = await Database.raw(` + // WITH gamelist AS ( + // SELECT DISTINCT p.game_id, p.map, p.gametype, p.datestamp, count(*) OVER (PARTITION BY game_id) as count from games p + // ) + // SELECT * from gamelist + // WHERE (count > 1) AND (game_id <> 0) + // ORDER BY game_id desc + // LIMIT 2000 + // `); - // const gamesQry = await Database.raw(` - // SELECT distinct game_id,map,gametype, datestamp - // FROM games - // WHERE (stats->>'score' IS NOT NULL) AND (game_id <> 0) - // ORDER BY game_id desc - // LIMIT 2000; - // `); + // // filter out duplicate game_ids (https://dev.to/marinamosti/removing-duplicates-in-an-array-of-objects-in-js-with-sets-3fep) + // const games = gamesQry.rows.reduce((game, current) => { + // const x = game.find(item => item.game_id === current.game_id); + // if (!x) { + // return game.concat([current]); + // } else { + // return game; + // } + // }, []); - const gamesQry = await Database.raw(` - WITH gamelist AS ( - SELECT DISTINCT p.game_id, p.map, p.gametype, p.datestamp, count(*) OVER (PARTITION BY game_id) as count from games p - ) - SELECT * from gamelist - WHERE (count > 1) AND (game_id <> 0) - ORDER BY game_id desc - LIMIT 2000 - `); + const games = await Database.from('games').orderBy('game_id', 'desc').limit(200); + // const CTFgames = await Database.from('games').where({gametype: 'CTFGame'}).orderBy('game_id', 'desc').limit(120); + // const LAKgames = await Database.from('games').where({gametype: 'LakRabbitGame'}).orderBy('game_id', 'desc').limit(120); - // filter out duplicate game_ids (https://dev.to/marinamosti/removing-duplicates-in-an-array-of-objects-in-js-with-sets-3fep) - const games = gamesQry.rows.reduce((game, current) => { + // move the 0 score display logic here - const x = game.find(item => item.game_id === current.game_id); - if (!x) { - return game.concat([current]); - } else { - return game; - } - }, []); - - // move the 0 score display logic here - - return inertia.render('Games/Main', { pageTitle, games }, { edgeVar: 'server-variable' }) - } - - - // game/:game_id - async game({ inertia, request }) { - const gameInfo = await Database.from('games') - .distinct('game_id', - 'map', - 'player_name', - 'player_guid', - 'gametype', - 'stats', - 'datestamp') - .where({ game_id: request.params.game_id }) - const pageTitle = { - name: gameInfo[0]['map'], - gametype: gameInfo[0]['gametype'] - } - - return inertia.render('Games/Game', { pageTitle, gameInfo }, { edgeVar: 'server-variable' }) - } + return inertia.render('Games/Main', { pageTitle, games }, { edgeVar: 'server-variable' }); + } + // game/:game_id + async game({ inertia, request }) { + const gameInfo = await Database.from('game_detail') + .select('game_id', 'map', 'player_name', 'player_guid', 'gametype', 'stats', 'datestamp') + .where({ game_id: request.params.game_id }) + .orderByRaw("stats->>'scoreTG' desc"); + const pageTitle = { + name: gameInfo[0]['map'], + gametype: gameInfo[0]['gametype'] + }; + return inertia.render('Games/Game', { pageTitle, gameInfo }, { edgeVar: 'server-variable' }); + } } -module.exports = GameController +module.exports = GameController; diff --git a/app/webapp/app/Controllers/Http/PlayerController.js b/app/webapp/app/Controllers/Http/PlayerController.js index d2521b1..14b7eb3 100644 --- a/app/webapp/app/Controllers/Http/PlayerController.js +++ b/app/webapp/app/Controllers/Http/PlayerController.js @@ -1,84 +1,82 @@ -'use strict' +'use strict'; -const Database = use('Database') +const Database = use('Database'); class PlayerController { + // Main Players List + async index({ inertia }) { + const pageTitle = 'All Players'; + const players = await Database.table('players') + .distinct( + 'player_guid', + 'player_name', + 'total_games_ctfgame', + 'total_games_dmgame', + 'total_games_lakrabbitgame', + 'total_games_sctfgame', + 'updated_at' + ) + .groupBy('player_guid') + .orderBy('player_name', 'asc') + .limit(1000); - // Main Players List - async index({ inertia }) { - const pageTitle = "All Players" - const players = await Database.table('players') - .distinct('player_guid', - 'player_name', - 'total_games_ctfgame', - 'total_games_dmgame', - 'total_games_lakrabbitgame', - 'total_games_sctfgame', - 'updated_at') - .groupBy('player_guid') - .orderBy('player_name', 'asc') - .limit(1000) + return inertia.render('Players/Main', { pageTitle, players }, { edgeVar: 'server-variable' }); + } - return inertia.render('Players/Main', { pageTitle, players }, { edgeVar: 'server-variable' }) - } + // Player Detail + async player({ inertia, request }) { + const playerInfo = await Database.from('players') + .distinct( + 'player_guid', + 'player_name', + 'total_games_ctfgame', + 'total_games_dmgame', + 'total_games_lakrabbitgame', + 'total_games_sctfgame' + ) + .where({ player_guid: request.params.player_guid }) + .limit(50); - // Player Detail - async player({ inertia, request }) { - const playerInfo = await Database.from('players') - .distinct('player_guid', - 'player_name', - 'total_games_ctfgame', - 'total_games_dmgame', - 'total_games_lakrabbitgame', - 'total_games_sctfgame') - .where({ player_guid: request.params.player_guid }) - .limit(50) + const playerStatData = await Database.from('game_detail') + .select('game_id', 'gametype', 'stats') + .where({ player_guid: request.params.player_guid }) + .orderBy('game_id', 'desc') + .limit(50); + // Dynamically generate and sum the stats object + let playerStatTotals = {}, + statKeys = Object.keys(playerStatData[0].stats); - const playerStatData = await Database.from('games') - .select('game_id', - 'gametype', - 'stats') - .where({ player_guid: request.params.player_guid }) - .orderBy('game_id', 'desc') - .limit(50) + for (let i = 0; i < statKeys.length; i++) { + if (statKeys[i] === 'map' || statKeys[i] === 'dateStamp' || statKeys[i] === 'timeDayMonth') { + continue; + } + playerStatTotals[statKeys[i]] = 0; + } + // Loop through the playerStatsData query from the DB + playerStatData.map((statLine) => { + // look through each object in playerStatsData array + for (let [ key, value ] of Object.entries(statLine.stats)) { + // console.log(`${key}: ${value}`); + // If the stat item exists, add it -- if not create a new key in playerStatTotals + if (playerStatTotals.hasOwnProperty(key) === true) { + playerStatTotals[key] = playerStatTotals[key] + Number(value); + } else { + playerStatTotals[key] = Number(value); + } + } + }); - // Dynamically generate and sum the stats object - let playerStatTotals = {}, - statKeys = Object.keys(playerStatData[0].stats) + let playerData = { + player: playerInfo[0], + stats: playerStatData, + totals: playerStatTotals + }; - for(let i = 0 ; i < statKeys.length; i++) { - if(statKeys[i] === "map" || - statKeys[i] === "dateStamp" || - statKeys[i] === "timeDayMonth" ){continue;} - playerStatTotals[statKeys[i]] = 0; - } - - // Loop through the playerStatsData query from the DB - playerStatData.map(statLine => { - // look through each object in playerStatsData array - for (let [key, value] of Object.entries(statLine.stats)) { - // console.log(`${key}: ${value}`); - // If the stat item exists, add it -- if not create a new key in playerStatTotals - if(playerStatTotals.hasOwnProperty(key) === true){ - playerStatTotals[key] = playerStatTotals[key] + Number(value); - }else{ - playerStatTotals[key] = Number(value); - } - } - }) - - - let playerData = { - player: playerInfo[0], - stats: playerStatData, - totals: playerStatTotals - } - - const pageTitle = playerData.player.player_name - return inertia.render('Players/Player', { pageTitle, playerData }, { edgeVar: 'server-variable' }) - } + const pageTitle = playerData.player.player_name; + return inertia.render('Players/Player', { pageTitle, playerData }, { edgeVar: 'server-variable' }); + } } -module.exports = PlayerController +module.exports = PlayerController; diff --git a/app/webapp/resources/js/Pages/Games/Game.js b/app/webapp/resources/js/Pages/Games/Game.js index 7e6818d..18cb97c 100644 --- a/app/webapp/resources/js/Pages/Games/Game.js +++ b/app/webapp/resources/js/Pages/Games/Game.js @@ -1,170 +1,171 @@ -import React, {PureComponent} from 'react' -import { InertiaLink } from '@inertiajs/inertia-react' -import Layout from '@/Shared/Layout' +import React, { PureComponent } from 'react'; +import { InertiaLink } from '@inertiajs/inertia-react'; +import Layout from '@/Shared/Layout'; -import {PieChart, Pie, Sector} from 'recharts' +import { PieChart, Pie, Sector } from 'recharts'; const renderActiveShape = (props) => { - const RADIAN = Math.PI / 180; - const { cx, cy, midAngle, innerRadius, outerRadius, startAngle, endAngle, - fill, payload, percent, value } = props; - const sin = Math.sin(-RADIAN * midAngle); - const cos = Math.cos(-RADIAN * midAngle); - const sx = cx + (outerRadius + 10) * cos; - const sy = cy + (outerRadius + 10) * sin; - const mx = cx + (outerRadius + 30) * cos; - const my = cy + (outerRadius + 30) * sin; - const ex = mx + (cos >= 0 ? 1 : -1) * 22; - const ey = my; - const textAnchor = cos >= 0 ? 'start' : 'end'; + const RADIAN = Math.PI / 180; + const { cx, cy, midAngle, innerRadius, outerRadius, startAngle, endAngle, fill, payload, percent, value } = props; + const sin = Math.sin(-RADIAN * midAngle); + const cos = Math.cos(-RADIAN * midAngle); + const sx = cx + (outerRadius + 10) * cos; + const sy = cy + (outerRadius + 10) * sin; + const mx = cx + (outerRadius + 30) * cos; + const my = cy + (outerRadius + 30) * sin; + const ex = mx + (cos >= 0 ? 1 : -1) * 22; + const ey = my; + const textAnchor = cos >= 0 ? 'start' : 'end'; - return ( - - {payload.name} - - - - - = 0 ? 1 : -1) * 12} y={ey} textAnchor={textAnchor} fill="#333">{`${value}`} - = 0 ? 1 : -1) * 12} y={ey} dy={18} textAnchor={textAnchor} fill="#999"> - {`(${(percent * 100).toFixed(2)}%)`} - - - ); + return ( + + + {payload.name} + + + + + + = 0 ? 1 : -1) * 12} y={ey} textAnchor={textAnchor} fill="#333">{`${value}`} + = 0 ? 1 : -1) * 12} y={ey} dy={18} textAnchor={textAnchor} fill="#999"> + {`(${(percent * 100).toFixed(2)}%)`} + + + ); }; export class TwoLevelPieChart extends PureComponent { - state = { - activeIndex: ((this.props.data.oScore >= this.props.data.dScore) ? 0 : 1 ), - data: [{name: 'Offense', value: this.props.data.oScore}, - {name: 'Defense', value: this.props.data.dScore}] - }; + state = { + activeIndex: this.props.data.oScore >= this.props.data.dScore ? 0 : 1, + data: [ { name: 'Offense', value: this.props.data.oScore }, { name: 'Defense', value: this.props.data.dScore } ] + }; - onPieEnter = (data, index) => { - this.setState({ - activeIndex: index, - }); - }; + onPieEnter = (data, index) => { + this.setState({ + activeIndex: index + }); + }; - render() { - return ( - - - - ); - } + render() { + return ( + + + + ); + } } - const PlayerRow = (player, index) => { - // dont show scoreless players - //if (Number(player.stats.score) <= 0){return} - - return
-
-
-

- {player.player_name} -

-
-
-
-
-
- Total Score -
-
- {player.stats.score} -
-
-
- { - (player.gametype == "CTFGame" || player.gametype == "SCtFGame") ? - : '' - } -
-
-
- Kills / Assists -
-
- {player.stats.kills} / {player.stats.assist} -
-
-
-
- MAs -
-
- {player.stats.totalMA} -
-
-
-
- Flag Grabs / Caps -
-
- {player.stats.flagGrabs} / {player.stats.flagCaps} -
-
-
-
- Flag Defends / Carrier Kills / Returns -
-
- {player.stats.flagDefends} / {player.stats.carrierKills} / {player.stats.flagReturns} -
-
-
-
-
-
; -} - + // dont show scoreless players + // if (Number(player.stats.scoreTG) <= 0) { + // return; + // } + return ( +
+
+
+

+ + {player.player_name} + +

+
+
+
+
+
Total Score
+
+ {player.stats.scoreTG} +
+
+
+ {player.gametype == 'CTFGame' || player.gametype == 'SCtFGame' ? ( + + ) : ( + '' + )} +
+
+
Kills / Assists
+
+ {player.stats.killsTG} / {player.stats.assistTG} +
+
+
+
MAs
+
+ {player.stats.totalMATG} +
+
+
+
Flag Grabs / Caps
+
+ {player.stats.flagGrabsTG} / {player.stats.flagCapsTG} +
+
+
+
+ Flag Defends / Capper Kills / Returns +
+
+ {player.stats.flagDefendsTG} / {player.stats.carrierKillsTG} /{' '} + {player.stats.flagReturnsTG} +
+
+
+
+
+
+ ); +}; export default function Game(props) { - return ( - - -
- { props.gameInfo.map((game, index) => PlayerRow(game, index)) } -
-{/* + return ( + +
+ {props.gameInfo.map((game, index) => PlayerRow(game, index))} +
+ {/*
{JSON.stringify(props.gameInfo)}
*/} -
- ) +
+ ); } diff --git a/app/webapp/resources/js/Pages/Players/Player.js b/app/webapp/resources/js/Pages/Players/Player.js index b8d999e..51ea18c 100644 --- a/app/webapp/resources/js/Pages/Players/Player.js +++ b/app/webapp/resources/js/Pages/Players/Player.js @@ -1,166 +1,170 @@ -import React from 'react' -import { InertiaLink } from '@inertiajs/inertia-react' -import Layout from '@/Shared/Layout' -import GameTypesPlayedCols from '@/Components/GameTypesPlayedCols' - -import {Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis} from 'recharts' +import React from 'react'; +import { InertiaLink } from '@inertiajs/inertia-react'; +import Layout from '@/Shared/Layout'; +import GameTypesPlayedCols from '@/Components/GameTypesPlayedCols'; +import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis } from 'recharts'; const returnWeaponTotals = (statTotals) => { - let totals = [ - { weapon: 'Chaingun', val: statTotals["chainKills"]}, - { weapon: 'Disc', val: statTotals["discKills"] }, - { weapon: 'Grenade Launcher', val: statTotals["grenadeKills"]}, - { weapon: 'Shocklance', val: statTotals["shockLanceKills"]}, - { weapon: 'Laser Rifle', val: statTotals["laserKills"]}, - { weapon: 'Blaster', val: statTotals["blasterKills"]}, - { weapon: 'Plasma Rifle', val: statTotals["plasmaKills"]}, - { weapon: 'Mortar Launcher', val: statTotals["mortarKills"]}, - { weapon: 'Missile Launcher', val: statTotals["missileKills"]}, - { weapon: 'Hand Grenade', val: statTotals["hGrenadeKills"]}, - { weapon: 'Mine', val: statTotals["mineKills"]}, - { weapon: 'Satchel', val: statTotals["satchelChargeKills"]}, - ]; + let totals = [ + { weapon: 'Chaingun', val: statTotals['cgKillsTG'] }, + { weapon: 'Disc', val: statTotals['discKillsTG'] }, + { weapon: 'Grenade Launcher', val: statTotals['grenadeKillsTG'] }, + { weapon: 'Shocklance', val: statTotals['shockKillsTG'] }, + { weapon: 'Laser Rifle', val: statTotals['laserKillsTG'] }, + { weapon: 'Blaster', val: statTotals['blasterKillsTG'] }, + { weapon: 'Plasma Rifle', val: statTotals['plasmaKillsTG'] }, + { weapon: 'Mortar Launcher', val: statTotals['mortarKillsTG'] }, + { weapon: 'Missile Launcher', val: statTotals['missileKillsTG'] }, + { weapon: 'Hand Grenade', val: statTotals['hGrenadeKillsTG'] }, + { weapon: 'Mine', val: statTotals['mineKillsTG'] }, + { weapon: 'Satchel', val: statTotals['satchelKillsTG'] } + ]; - // dont return if the val is 0 - return totals.filter(function (el) { - return el.val > 0; - }); + // dont return if the val is 0 + return totals.filter(function(el) { + return el.val > 0; + }); }; - const GameCard = (player, index) => { - // only display card if player has score - // if (Number(player.stats.score) <= 0){return} - - return
- -
-
-
-

- {player.stats.map} -

- -

- {player.stats.dateStamp} -

-
-
- - { player.gametype } - -
-
-
-
-
- Total Score -
-
- {player.stats.score} -
-
-
-
- Kills / Assists -
-
- {player.stats.kills} / {player.stats.assist} -
-
-
-
- MAs -
-
- {player.stats.totalMA} -
-
-
-
- Flag Grabs / Caps -
-
- {player.stats.flagGrabs} / {player.stats.flagCaps} -
-
-
-
- Flag Defends / Carrier Kills / Returns -
-
- {player.stats.flagDefends} / {player.stats.carrierKills} / {player.stats.flagReturns} -
-
-
; -} + // only display card if player has score + // if (Number(player.stats.score) <= 0){return} + return ( +
+
+
+
+

+ + {player.stats.map} + +

+

+ + {player.stats.dateStamp} + +

+
+
+ + {player.gametype} + +
+
+
+
+
Total Score
+
{player.stats.scoreTG}
+
+
+
Kills / Assists
+
+ {player.stats.killsTG} / {player.stats.assistTG} +
+
+
+
MAs
+
{player.stats.totalMATG}
+
+
+
Flag Grabs / Caps
+
+ {player.stats.flagGrabsTG} / {player.stats.flagCapsTG} +
+
+
+
Flag Defends / Carrier Kills / Returns
+
+ {player.stats.flagDefendsTG} / {player.stats.carrierKillsTG} / {player.stats.flagReturnsTG} +
+
+
+ ); +}; export default function Player(props) { - return ( - + return ( + +
+
+
+

Aggregate

+

Stat Totals

+
+
-
+
+
+
+
Games Played
+
+ +
+
+
+
Weapon Usage
+
+ + + + + + +
+
+
+
+
-
-
-

Aggregate

-

- Stat Totals -

-
-
- -
-
-
-
- Games Played -
-
- -
-
-
-
- Weapon Usage -
-
- - - - - - -
-
-
-
-
- -
-
-
-

Game History

-

- Stats for past {props.playerData.stats.length} games -

-
-
-
- { props.playerData.stats.map((player, index) => GameCard(player, index)) } -
-
-{/* +
+
+
+

Game History

+

+ Stats for past {props.playerData.stats.length} games +

+
+
+
+ {props.playerData.stats.map((player, index) => GameCard(player, index))} +
+
+ {/*
{JSON.stringify(props.playerData.stats)}
*/} -
- ) +
+ ); } diff --git a/build/postgres/docker-entrypoint-initdb.d/backup/t2_stats.sql b/build/postgres/docker-entrypoint-initdb.d/backup/t2_stats.sql index 4f8576d..7ec275d 100644 --- a/build/postgres/docker-entrypoint-initdb.d/backup/t2_stats.sql +++ b/build/postgres/docker-entrypoint-initdb.d/backup/t2_stats.sql @@ -31,27 +31,54 @@ CREATE TABLE "public"."players" ( ); + + DROP TABLE IF EXISTS "public"."games"; -CREATE SEQUENCE IF NOT EXISTS games_id_seq; -- Table Definition CREATE TABLE "public"."games" ( + "game_id" numeric NOT NULL UNIQUE, + "map" text NOT NULL, + "datestamp" timestamp NOT NULL, + "gametype" text NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT game_pk PRIMARY KEY (game_id) +); + + + + + + + + +DROP TABLE IF EXISTS "public"."game_detail"; +CREATE SEQUENCE IF NOT EXISTS games_id_seq; + +-- Table Definition +CREATE TABLE "public"."game_detail" ( "id" int4 NOT NULL DEFAULT nextval('games_id_seq'::regclass), "player_guid" numeric NOT NULL, "player_name" text NOT NULL, "stat_overwrite" numeric NOT NULL, "map" text NOT NULL, - "game_id" numeric NOT NULL DEFAULT 0, + "game_id" numeric NOT NULL, "stats" jsonb NOT NULL, "datestamp" timestamp NOT NULL, "uuid" text NOT NULL UNIQUE, "gametype" text NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT games_pk PRIMARY KEY (id), + FOREIGN KEY (game_id) REFERENCES games (game_id), FOREIGN KEY (player_guid) REFERENCES players (player_guid) ); + + + + + CREATE OR REPLACE FUNCTION trigger_set_timestamp() RETURNS TRIGGER AS $$ BEGIN