diff --git a/backend/README.md b/backend/README.md index d887cbe..731cdb3 100644 --- a/backend/README.md +++ b/backend/README.md @@ -2,7 +2,8 @@ Le backend du site Brawlset est codé en [GO](https://go.dev/) et utilise [Pocke # TODO -- [ ] Feat : Créer le système d'ELO +- [x] Feat : Créer le système d'ELO +- [ ] Feat : Update les ELO quand il y a un changement ou une suppression de match - [x] Fix : Changer le recto / verso des cartes - [ ] Feat : Système d'évènements à ajouter sur la page principale - [ ] Fix : Refuse to create deck when cards are not found diff --git a/backend/hooks.go b/backend/hooks.go new file mode 100644 index 0000000..d76192e --- /dev/null +++ b/backend/hooks.go @@ -0,0 +1,287 @@ +package main + +import ( + "fmt" + "log" + "math" + "slices" + + "github.com/pocketbase/dbx" + "github.com/pocketbase/pocketbase/core" +) + +type DuelResult struct { + Player1_NewELO float64 + Player2_NewELO float64 + Commander1_NewELO float64 + Commander2_NewELO float64 +} + +func (app *application) SetupHooks() { + + // Set default values after creation + app.pb.OnRecordAfterCreateSuccess("elo_player").BindFunc(func (e *core.RecordEvent) error { + e.Record.Set("elo_casual", 100) + e.Record.Set("elo_tournament", 100) + e.App.Save(e.Record) + + log.Println("record created") + return e.Next() + }) + + // When a ELO entry has been added + app.pb.OnRecordCreate("elo").BindFunc(func (e *core.RecordEvent) error { + date := e.Record.GetString("date") + official := e.Record.GetBool("official") + player1Id := e.Record.GetString("player_1") + player2Id := e.Record.GetString("player_2") + commander1Id := e.Record.GetString("commandant_1") + commander2Id := e.Record.GetString("commandant_2") + score1 := e.Record.GetInt("score_1") + score2 := e.Record.GetInt("score_2") + + // Base data in case it's the first match + player1_ELO := 100.0 + player2_ELO := 100.0 + commander1_ELO := 100.0 + commander2_ELO := 100.0 + + // Update ELO With last match ELO (Tournament or Casual) + updateEloPlayerWithLastMatch(e, player1Id, date, &player1_ELO, official) + updateEloPlayerWithLastMatch(e, player2Id, date, &player2_ELO, official) + + // Update ELO With last match ELO (Tournament or Casual) + updateEloCommanderWithLastMatch(e, commander1Id, date, &commander1_ELO, official) + updateEloCommanderWithLastMatch(e, commander2Id, date, &commander2_ELO, official) + + newData := CalculateELO(player1_ELO, player2_ELO, commander1_ELO, commander2_ELO, score1, score2) + + e.Record.Set("last_elo_player_1", player1_ELO) + e.Record.Set("elo_player_1", newData.Player1_NewELO) + e.Record.Set("last_elo_player_2", player2_ELO) + e.Record.Set("elo_player_2", newData.Player2_NewELO) + e.Record.Set("last_elo_commandant_1", commander1_ELO) + e.Record.Set("elo_commandant_1", newData.Commander1_NewELO) + e.Record.Set("last_elo_commandant_2", commander2_ELO) + e.Record.Set("elo_commandant_2", newData.Commander2_NewELO) + + recordPlayer1, _ := e.App.FindRecordById("elo_player", player1Id) + recordPlayer2, _ := e.App.FindRecordById("elo_player", player2Id) + if official { + recordPlayer1.Set("elo_tournament", newData.Player1_NewELO) + recordPlayer2.Set("elo_tournament", newData.Player2_NewELO) + } else { + recordPlayer1.Set("elo_casual", newData.Player1_NewELO) + recordPlayer2.Set("elo_casual", newData.Player2_NewELO) + } + + recordCommander1, _ := e.App.FindRecordById("carte", commander1Id) + recordCommander2, _ := e.App.FindRecordById("carte", commander2Id) + if official { + recordCommander1.Set("elo_tournament", newData.Commander1_NewELO) + recordCommander2.Set("elo_tournament", newData.Commander2_NewELO) + } else { + recordCommander1.Set("elo_casual", newData.Commander1_NewELO) + recordCommander2.Set("elo_casual", newData.Commander2_NewELO) + } + + e.App.Save(recordPlayer1) + e.App.Save(recordPlayer2) + e.App.Save(recordCommander1) + e.App.Save(recordCommander2) + + updateNextMatchs(e, newData, official) + + return e.Next() + }) +} + +func updateNextMatchs(e *core.RecordEvent, newData DuelResult, official bool) { + needToRecalculateElo := false + + nextMatchsCount, _ := e.App.CountRecords("elo", dbx.And(dbx.And(dbx.NewExp("date > {:date}", dbx.Params{"date": e.Record.GetString("date")}), dbx.NewExp("official = {:official}", dbx.Params{"official": official})),dbx.Or(dbx.NewExp("player_1 = {:id}", dbx.Params{"id": e.Record.GetString("player_1")}), dbx.NewExp("player_2 = {:id}", dbx.Params{"id": e.Record.GetString("player_1")})))) + if nextMatchsCount > 0 { + needToRecalculateElo = true + } + + nextMatchsCount, _ = e.App.CountRecords("elo", dbx.And(dbx.And(dbx.NewExp("date > {:date}", dbx.Params{"date": e.Record.GetString("date")}), dbx.NewExp("official = {:official}", dbx.Params{"official": official})),dbx.Or(dbx.NewExp("player_1 = {:id}", dbx.Params{"id": e.Record.GetString("player_2")}), dbx.NewExp("player_2 = {:id}", dbx.Params{"id": e.Record.GetString("player_2")})))) + if nextMatchsCount > 0 { + needToRecalculateElo = true + } + + nextMatchsCount, _ = e.App.CountRecords("elo", dbx.And(dbx.And(dbx.NewExp("date > {:date}", dbx.Params{"date": e.Record.GetString("date")}), dbx.NewExp("official = {:official}", dbx.Params{"official": official})),dbx.Or(dbx.NewExp("commandant_1 = {:id}", dbx.Params{"id": e.Record.GetString("commandant_1")}), dbx.NewExp("commandant_2 = {:id}", dbx.Params{"id": e.Record.GetString("commandant_1")})))) + if nextMatchsCount > 0 { + needToRecalculateElo = true + } + + nextMatchsCount, _ = e.App.CountRecords("elo", dbx.And(dbx.And(dbx.NewExp("date > {:date}", dbx.Params{"date": e.Record.GetString("date")}), dbx.NewExp("official = {:official}", dbx.Params{"official": official})),dbx.Or(dbx.NewExp("commandant_1 = {:id}", dbx.Params{"id": e.Record.GetString("commandant_2")}), dbx.NewExp("commandant_2 = {:id}", dbx.Params{"id": e.Record.GetString("commandant_2")})))) + if nextMatchsCount > 0 { + needToRecalculateElo = true + } + + if needToRecalculateElo { + eloMatchs := []Elo{} + log.Println(fmt.Sprintf("Matchs found after %s", e.Record.GetString("id"))) + + playerIdChanged := []string{ e.Record.GetString("player_1"), e.Record.GetString("player_2") } + newPlayerData := map[string]float64{} + newPlayerData[e.Record.GetString("player_1")] = newData.Player1_NewELO + newPlayerData[e.Record.GetString("player_2")] = newData.Player2_NewELO + newCommanderData := map[string]float64{} + newCommanderData[e.Record.GetString("commandant_1")] = newData.Commander1_NewELO + newCommanderData[e.Record.GetString("commandant_2")] = newData.Commander2_NewELO + commanderIdChanged := []string{ e.Record.GetString("commandant_1"), e.Record.GetString("commandant_2") } + err := e.App.RecordQuery("elo"). + Where(dbx.NewExp("date > {:date}", dbx.Params{"date":e.Record.GetString("date")})). + AndWhere(dbx.NewExp("official = {:official}",dbx.Params{"official": official})). + OrderBy("date ASC"). + All(&eloMatchs) + if err != nil { + log.Println(err) + } + + for _, v := range eloMatchs { + // Check if Match is concerned by the ELO recalculate + if slices.Contains(playerIdChanged, v.Player1) || slices.Contains(playerIdChanged, v.Player2) { + record, _ := e.App.FindRecordById("elo", v.ID) + // If Player1 is not in the ID changed, now it is + if !slices.Contains(playerIdChanged, v.Player1) { + playerIdChanged = append(playerIdChanged, v.Player1) + } else { + record.Set("last_elo_player_1", newPlayerData[v.Player1]) + } + // If Player2 is not in the ID changed, now it is + if !slices.Contains(playerIdChanged, v.Player2) { + playerIdChanged = append(playerIdChanged, v.Player2) + } else { + record.Set("last_elo_player_2", newPlayerData[v.Player2]) + } + + tempEloData := CalculateELO(record.GetFloat("last_elo_player_1"), record.GetFloat("last_elo_player_2"), record.GetFloat("last_elo_commandant_1"), record.GetFloat("last_elo_commandant_2"), record.GetInt("score_1"), record.GetInt("score_2")) + record.Set("elo_player_1", tempEloData.Player1_NewELO) + record.Set("elo_player_2", tempEloData.Player2_NewELO) + + newPlayerData[v.Player1] = tempEloData.Player1_NewELO + newPlayerData[v.Player2] = tempEloData.Player2_NewELO + + e.App.Save(record) + } + + if slices.Contains(commanderIdChanged, v.Commander1) || slices.Contains(commanderIdChanged, v.Commander2) { + record, _ := e.App.FindRecordById("elo", v.ID) + // If Player1 is not in the ID changed, now it is + if !slices.Contains(commanderIdChanged, v.Commander1) { + commanderIdChanged = append(commanderIdChanged, v.Commander1) + } else { + record.Set("last_elo_commandant_1", newCommanderData[v.Commander1]) + } + // If Player2 is not in the ID changed, now it is + if !slices.Contains(commanderIdChanged, v.Commander2) { + commanderIdChanged = append(commanderIdChanged, v.Commander2) + } else { + record.Set("last_elo_commandant_2", newCommanderData[v.Commander2]) + } + + tempEloData := CalculateELO(record.GetFloat("last_elo_player_1"), record.GetFloat("last_elo_player_2"), record.GetFloat("last_elo_commandant_1"), record.GetFloat("last_elo_commandant_2"), record.GetInt("score_1"), record.GetInt("score_2")) + record.Set("elo_commandant_1", tempEloData.Commander1_NewELO) + record.Set("elo_commandant_2", tempEloData.Commander2_NewELO) + + newCommanderData[v.Commander1] = tempEloData.Commander1_NewELO + newCommanderData[v.Commander2] = tempEloData.Commander2_NewELO + + e.App.Save(record) + } + } + + recordPlayer1, _ := e.App.FindRecordById("elo_player", e.Record.GetString("player_1")) + recordPlayer2, _ := e.App.FindRecordById("elo_player", e.Record.GetString("player_2")) + if official { + recordPlayer1.Set("elo_tournament", newPlayerData[e.Record.GetString("player_1")]) + recordPlayer2.Set("elo_tournament", newPlayerData[e.Record.GetString("player_2")]) + } else { + recordPlayer1.Set("elo_casual", newPlayerData[e.Record.GetString("player_1")]) + recordPlayer2.Set("elo_casual", newPlayerData[e.Record.GetString("player_2")]) + } + + recordCommander1, _ := e.App.FindRecordById("carte", e.Record.GetString("commandant_1")) + recordCommander2, _ := e.App.FindRecordById("carte", e.Record.GetString("commandant_2")) + if official { + recordCommander1.Set("elo_tournament", newCommanderData[e.Record.GetString("commandant_1")]) + recordCommander2.Set("elo_tournament", newCommanderData[e.Record.GetString("commandant_2")]) + } else { + recordCommander1.Set("elo_casual", newCommanderData[e.Record.GetString("commandant_1")]) + recordCommander2.Set("elo_casual", newCommanderData[e.Record.GetString("commandant_2")]) + } + + e.App.Save(recordPlayer1) + e.App.Save(recordPlayer2) + e.App.Save(recordCommander1) + e.App.Save(recordCommander2) + } +} + +func updateEloPlayerWithLastMatch(e *core.RecordEvent, playerId string, date string, elo *float64, official bool) { + lastMatch := Elo{} + err := e.App.RecordQuery("elo"). + Where(dbx.NewExp("date < {:date}", dbx.Params{"date": date})). + AndWhere(dbx.NewExp("official = {:official}", dbx.Params{"official": official})). + AndWhere(dbx.Or(dbx.NewExp("player_1 = {:id}", dbx.Params{"id": playerId}), dbx.NewExp("player_2 = {:id}", dbx.Params{"id": playerId}))). + OrderBy("date DESC"). + One(&lastMatch) + if err == nil { + // If a match exists before this one update ELO + if lastMatch.Player1 == playerId { + *elo = lastMatch.EloPlayer1 + } else { + *elo = lastMatch.EloPlayer2 + } + } +} + +func updateEloCommanderWithLastMatch(e *core.RecordEvent, commanderId string, date string, elo *float64, official bool) { + lastMatch := Elo{} + err := e.App.RecordQuery("elo"). + Where(dbx.NewExp("date < {:date}", dbx.Params{"date": date})). + AndWhere(dbx.NewExp("official = {:official}", dbx.Params{"official": official})). + AndWhere(dbx.Or(dbx.NewExp("commandant_1 = {:id}", dbx.Params{"id": commanderId}), dbx.NewExp("commandant_2 = {:id}", dbx.Params{"id": commanderId}))). + OrderBy("date DESC"). + One(&lastMatch) + if err == nil { + // If a match exists before this one update ELO + if lastMatch.Commander1 == commanderId { + *elo = lastMatch.EloCommander1 + } else { + *elo = lastMatch.EloCommander2 + } + } +} + +func CalculateELO(player1_ELO float64, player2_ELO float64, commander1_ELO float64, commander2_ELO float64, score1 int, score2 int) DuelResult { + K_PLAYER := 30.0 + K_COMMANDER := 20.0 + + won_player1 := 0.0 + won_player2 := 0.0 + + if score1 < score2 { + won_player2 = 1.0 + } else if score1 > score2 { + won_player1 = 1.0 + } else if score1 == score2 { + won_player1 = 0.5 + won_player2 = 0.5 + } + + eloModifier_player1 := won_player1 - 1.0 / (1 + math.Pow(10, (player2_ELO - player1_ELO) / 400.0)) + eloModifier_player2 := won_player2 - 1.0 / (1 + math.Pow(10, (player1_ELO - player2_ELO) / 400.0)) + + eloModifier_commander1 := won_player1 - 1.0 / (1 + math.Pow(10, (commander2_ELO - commander1_ELO) / 400.0)) + eloModifier_commander2 := won_player2 - 1.0 / (1 + math.Pow(10, (commander1_ELO - commander2_ELO) / 400.0)) + + return DuelResult{ + Player1_NewELO: player1_ELO + K_PLAYER * eloModifier_player1, + Player2_NewELO: player2_ELO + K_PLAYER * eloModifier_player2, + Commander1_NewELO: commander1_ELO + K_COMMANDER * eloModifier_commander1, + Commander2_NewELO: commander2_ELO + K_COMMANDER * eloModifier_commander2, + } +} diff --git a/backend/main.go b/backend/main.go index db9216a..b191b6d 100644 --- a/backend/main.go +++ b/backend/main.go @@ -44,6 +44,7 @@ func main() { app.SetupCache() app.SetupCrons() app.SetupRoutes() + app.SetupHooks() log.Fatal(app.pb.Start()) } diff --git a/backend/migrations/1749578804_elo.go b/backend/migrations/1749578804_elo.go new file mode 100644 index 0000000..a507bd8 --- /dev/null +++ b/backend/migrations/1749578804_elo.go @@ -0,0 +1,578 @@ +package migrations + +import ( + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" +) + +func init() { + m.Register(func(app core.App) error { + jsonData := `[ + { + "id": "pbc_2856227530", + "listRule": null, + "viewRule": null, + "createRule": null, + "updateRule": null, + "deleteRule": null, + "name": "elo", + "type": "base", + "fields": [ + { + "autogeneratePattern": "[a-z0-9]{15}", + "hidden": false, + "id": "text3208210256", + "max": 15, + "min": 15, + "name": "id", + "pattern": "^[a-z0-9]+$", + "presentable": false, + "primaryKey": true, + "required": true, + "system": true, + "type": "text" + }, + { + "hidden": false, + "id": "date2862495610", + "max": "", + "min": "", + "name": "date", + "presentable": false, + "required": true, + "system": false, + "type": "date" + }, + { + "cascadeDelete": false, + "collectionId": "pbc_1697519085", + "hidden": false, + "id": "relation2239679623", + "maxSelect": 1, + "minSelect": 0, + "name": "player_1", + "presentable": false, + "required": true, + "system": false, + "type": "relation" + }, + { + "cascadeDelete": false, + "collectionId": "pbc_1905410326", + "hidden": false, + "id": "relation2047047275", + "maxSelect": 1, + "minSelect": 0, + "name": "commandant_1", + "presentable": false, + "required": true, + "system": false, + "type": "relation" + }, + { + "hidden": false, + "id": "number909748445", + "max": null, + "min": null, + "name": "score_1", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "cascadeDelete": false, + "collectionId": "pbc_1697519085", + "hidden": false, + "id": "relation477601085", + "maxSelect": 1, + "minSelect": 0, + "name": "player_2", + "presentable": false, + "required": true, + "system": false, + "type": "relation" + }, + { + "cascadeDelete": false, + "collectionId": "pbc_1905410326", + "hidden": false, + "id": "relation3809093585", + "maxSelect": 1, + "minSelect": 0, + "name": "commandant_2", + "presentable": false, + "required": true, + "system": false, + "type": "relation" + }, + { + "hidden": false, + "id": "number2939222375", + "max": null, + "min": null, + "name": "score_2", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "bool2557948429", + "name": "official", + "presentable": false, + "required": false, + "system": false, + "type": "bool" + }, + { + "hidden": false, + "id": "number1561173596", + "max": null, + "min": null, + "name": "elo_commandant_1", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "number3288649702", + "max": null, + "min": null, + "name": "elo_commandant_2", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "number4239465276", + "max": null, + "min": null, + "name": "elo_player_1", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "number1706576518", + "max": null, + "min": null, + "name": "elo_player_2", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "number4115095912", + "max": null, + "min": null, + "name": "last_elo_player_1", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "number1817063634", + "max": null, + "min": null, + "name": "last_elo_player_2", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "number2067666352", + "max": null, + "min": null, + "name": "last_elo_commandant_1", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "number3795272714", + "max": null, + "min": null, + "name": "last_elo_commandant_2", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "autodate3332085495", + "name": "updated", + "onCreate": true, + "onUpdate": true, + "presentable": false, + "system": false, + "type": "autodate" + } + ], + "indexes": [], + "system": false + }, + { + "id": "pbc_1697519085", + "listRule": null, + "viewRule": null, + "createRule": null, + "updateRule": null, + "deleteRule": null, + "name": "elo_player", + "type": "base", + "fields": [ + { + "autogeneratePattern": "[a-z0-9]{15}", + "hidden": false, + "id": "text3208210256", + "max": 15, + "min": 15, + "name": "id", + "pattern": "^[a-z0-9]+$", + "presentable": false, + "primaryKey": true, + "required": true, + "system": true, + "type": "text" + }, + { + "cascadeDelete": false, + "collectionId": "_pb_users_auth_", + "hidden": false, + "id": "relation2375276105", + "maxSelect": 1, + "minSelect": 0, + "name": "user", + "presentable": false, + "required": false, + "system": false, + "type": "relation" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text4166911607", + "max": 0, + "min": 0, + "name": "username", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "hidden": false, + "id": "number2334461934", + "max": null, + "min": null, + "name": "elo_casual", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "number2732192018", + "max": null, + "min": null, + "name": "elo_tournament", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + } + ], + "indexes": [], + "system": false + }, + { + "id": "pbc_1905410326", + "listRule": null, + "viewRule": null, + "createRule": null, + "updateRule": null, + "deleteRule": null, + "name": "carte", + "type": "base", + "fields": [ + { + "autogeneratePattern": "[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}", + "hidden": false, + "id": "text3208210256", + "max": 36, + "min": 36, + "name": "id", + "pattern": "^[a-z0-9\\-]+$", + "presentable": false, + "primaryKey": true, + "required": true, + "system": true, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text1579384326", + "max": 0, + "min": 0, + "name": "name", + "pattern": "", + "presentable": true, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text3273110370", + "max": 0, + "min": 0, + "name": "sanitized_name", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text976907234", + "max": 0, + "min": 0, + "name": "layout", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "exceptDomains": null, + "hidden": false, + "id": "url1456686396", + "name": "small_image", + "onlyDomains": null, + "presentable": false, + "required": false, + "system": false, + "type": "url" + }, + { + "exceptDomains": null, + "hidden": false, + "id": "url2615033119", + "name": "small_image_back", + "onlyDomains": null, + "presentable": false, + "required": false, + "system": false, + "type": "url" + }, + { + "exceptDomains": null, + "hidden": false, + "id": "url2291853061", + "name": "normal_image", + "onlyDomains": null, + "presentable": false, + "required": false, + "system": false, + "type": "url" + }, + { + "exceptDomains": null, + "hidden": false, + "id": "url2961008824", + "name": "normal_image_back", + "onlyDomains": null, + "presentable": false, + "required": false, + "system": false, + "type": "url" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text2363381545", + "max": 0, + "min": 0, + "name": "type", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "hidden": false, + "id": "select3054531206", + "maxSelect": 5, + "name": "color_identity", + "presentable": false, + "required": false, + "system": false, + "type": "select", + "values": [ + "B", + "W", + "R", + "G", + "U" + ] + }, + { + "hidden": false, + "id": "date3520360348", + "max": "", + "min": "", + "name": "released_at", + "presentable": false, + "required": false, + "system": false, + "type": "date" + }, + { + "cascadeDelete": false, + "collectionId": "pbc_3912384429", + "hidden": false, + "id": "relation3860080092", + "maxSelect": 1, + "minSelect": 0, + "name": "mtg_set", + "presentable": false, + "required": false, + "system": false, + "type": "relation" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text3805467153", + "max": 0, + "min": 0, + "name": "set_code", + "pattern": "", + "presentable": true, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "hidden": false, + "id": "number3402113753", + "max": null, + "min": null, + "name": "price", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "exceptDomains": null, + "hidden": false, + "id": "url518645060", + "name": "cardmarket_url", + "onlyDomains": null, + "presentable": false, + "required": false, + "system": false, + "type": "url" + }, + { + "hidden": false, + "id": "bool1734659578", + "name": "can_be_commander", + "presentable": false, + "required": false, + "system": false, + "type": "bool" + }, + { + "hidden": false, + "id": "bool2605256118", + "name": "banned", + "presentable": false, + "required": false, + "system": false, + "type": "bool" + }, + { + "hidden": false, + "id": "number2334461934", + "max": null, + "min": null, + "name": "elo_casual", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "number2732192018", + "max": null, + "min": null, + "name": "elo_tournament", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + } + ], + "indexes": [], + "system": false + } +]` + + // add up queries... + + return app.ImportCollectionsByMarshaledJSON([]byte(jsonData), false) + }, func(app core.App) error { + // add down queries... + + return nil + }) +} diff --git a/backend/types.go b/backend/types.go index b519f3b..af65399 100644 --- a/backend/types.go +++ b/backend/types.go @@ -55,3 +55,27 @@ type Deck struct { Brawlset string `db:"brawlset" json:"brawslet,omitempty"` Cards types.JSONArray[DeckCard] `db:"cartes" json:"cartes,omitempty"` } + +type Elo struct { + ID string `db:"id" json:"id"` + Date string `db:"date" json:"date"` + Player1 string `db:"player_1" json:"player_1"` + Player2 string `db:"player_2" json:"player_2"` + Commander1 string `db:"commander_1" json:"commander_1"` + Commander2 string `db:"commander_2" json:"commander_2"` + Score1 int `db:"score_1" json:"score_1"` + Score2 int `db:"score_2" json:"score_2"` + Official bool `db:"official" json:"official"` + EloCommander1 float64 `db:"elo_commandant_1" json:"elo_commander_1"` + EloCommander2 float64 `db:"elo_commandant_2" json:"elo_commander_2"` + EloPlayer1 float64 `db:"elo_player_1" json:"elo_player_1"` + EloPlayer2 float64 `db:"elo_player_2" json:"elo_player_2"` +} + +type EloPlayer struct { + ID string `db:"id" json:"id"` + User string `db:"user" json:"user"` + Username string `db:"username" json:"username"` + EloCasual float64 `db:"elo_casual" json:"elo_casual"` + EloTournament float64 `db:"elo_tournament" json:"elo_tournament"` +}