brawlset/backend/cache.go

295 lines
8.8 KiB
Go

package main
import (
"fmt"
"log"
"sort"
"strconv"
"strings"
"unsafe"
"github.com/pocketbase/pocketbase/core"
)
type CacheBrawlsetListItem struct {
Name string
SanitizedName string
IconsSvgUri []string
}
type CacheCarteListItem struct {
Name string
Url string
Layout string
SmallImage string
SmallImageBack string
NormalImage string
NormalImageBack string
Price float64
CardmarketUri string
NumberOfDecks int
NumberOfPossibleDecks int
PercentageOfDecks float64
Synergy float64
}
type CacheCarteListItemWithSynergy struct {
MainCard CacheCarteListItem
CanBeCommander bool
Cards map[string][]CacheCarteListItem
}
type CacheCarteSearch struct {
Name string
Url string
SetName string
}
type CacheBrawlsetData struct {
Name string
SanitizedName string
Cards map[string][]CacheCarteListItem
}
func (app *application) SetupCache() {
app.pb.OnBootstrap().BindFunc(func(e *core.BootstrapEvent) error {
if err := e.Next(); err != nil {
return err
}
GenerateCache(e.App)
return nil
})
}
func GenerateCache(pb core.App) {
log.Println("Creating json data cache...")
defer timer("Generating cache")()
var totalSize int64
brawlsets := []Brawlset{}
mtgSetsQuery := []MtgSet{}
mtgSets := map[string]MtgSet{}
cardsQuery := []Carte{}
cards := map[string]Carte{}
cacheCardsSearch := []CacheCarteSearch{}
cardsBySet := map[string][]Carte{}
decks := []Deck{}
decksByBset := map[string][]Deck{}
err := pb.DB().
NewQuery("SELECT * FROM brawlset").
All(&brawlsets)
if err != nil {
log.Println(fmt.Sprintf("[ERROR] %v", err))
}
err = pb.DB().
NewQuery("SELECT * FROM mtg_set").
All(&mtgSetsQuery)
if err != nil {
log.Println(fmt.Sprintf("[ERROR] %v", err))
}
err = pb.DB().
NewQuery("SELECT * FROM deck").
All(&decks)
if err != nil {
log.Println(fmt.Sprintf("[ERROR] %v", err))
}
for _, v := range decks {
decksByBset[v.Brawlset] = append(decksByBset[v.Brawlset], v)
}
err = pb.DB().
NewQuery("SELECT * FROM carte").
All(&cardsQuery)
if err != nil {
log.Println(fmt.Sprintf("[ERROR] %v", err))
}
for _, v := range mtgSetsQuery {
mtgSets[v.ID] = v
}
for _, v := range cardsQuery {
cards[v.ID] = v
cardsBySet[v.MtgSet] = append(cardsBySet[v.MtgSet], v)
cacheCardsSearch = append(cacheCardsSearch, CacheCarteSearch{ Name: v.Name, Url: fmt.Sprintf("/card/%s-%s", v.SetCode,v.SanitizedName), SetName: mtgSets[v.MtgSet].Name})
}
totalSize += int64(unsafe.Sizeof(cacheCardsSearch))
pb.Store().Set("searchData", cacheCardsSearch)
cacheBrawlsetList := []CacheBrawlsetListItem{}
for _, v := range brawlsets {
setIconList := []string{}
numberOfDecksPerColorIdentity := map[string]int{}
cacheBrawlsetData := CacheBrawlsetData{Name: v.Name, SanitizedName: v.SanitizedName, Cards: map[string][]CacheCarteListItem{}}
numberOfDecksPerCard := map[string]int{}
synergyPerCards := map[string]map[string]int{}
for _, d := range decksByBset[v.ID] {
possibleColorIdentities := GetAllColorCombination(d.ColorIdentity)
for _, color := range possibleColorIdentities {
if _, ok := numberOfDecksPerColorIdentity[color]; ok {
numberOfDecksPerColorIdentity[color]++
} else {
numberOfDecksPerColorIdentity[color] += 1
}
}
// Increment number of deck for commander
if _, ok := numberOfDecksPerCard[fmt.Sprintf("c-%s",d.Commander)]; ok {
numberOfDecksPerCard[fmt.Sprintf("c-%s",d.Commander)]++
} else {
numberOfDecksPerCard[fmt.Sprintf("c-%s",d.Commander)] = 1
}
// Increment number of deck for each card
for _, c := range d.Cards {
if _, ok := synergyPerCards[fmt.Sprintf("c-%s",d.Commander)]; !ok {
synergyPerCards[fmt.Sprintf("c-%s", d.Commander)] = map[string]int{}
}
if _, ok := synergyPerCards[fmt.Sprintf("c-%s",d.Commander)][c.ID]; ok {
synergyPerCards[fmt.Sprintf("c-%s",d.Commander)][c.ID]++
} else {
synergyPerCards[fmt.Sprintf("c-%s",d.Commander)][c.ID] = 1
}
// Add number of decks per card
if _, ok := numberOfDecksPerCard[c.ID]; ok {
numberOfDecksPerCard[c.ID]++
} else {
numberOfDecksPerCard[c.ID] = 1
}
// Add number of decks with the two same cards for synergy
for _, tc := range d.Cards {
if _, ok := synergyPerCards[c.ID]; !ok {
synergyPerCards[c.ID] = map[string]int{}
}
if _, ok := synergyPerCards[c.ID][tc.ID]; ok {
synergyPerCards[c.ID][tc.ID]++
} else {
synergyPerCards[c.ID][tc.ID] = 1
}
}
}
}
for _, s := range v.Sets {
setIconList = append(setIconList, mtgSets[s].IconUri)
for _, c := range cardsBySet[s] {
obj := CreateCardData(c, decksByBset, numberOfDecksPerColorIdentity, numberOfDecksPerCard, v.ID)
cacheBrawlsetData.Cards[c.CardType] = append(cacheBrawlsetData.Cards[c.CardType], obj)
if c.CanBeCommander {
obj.NumberOfDecks = numberOfDecksPerCard[fmt.Sprintf("c-%s",c.ID)]
obj.Url = fmt.Sprintf("/commander/%s-%s", c.SetCode,c.SanitizedName)
cacheBrawlsetData.Cards["commander"] = append(cacheBrawlsetData.Cards["commander"], obj)
detailsObj := CacheCarteListItemWithSynergy{
MainCard: obj,
CanBeCommander: true,
Cards: map[string][]CacheCarteListItem{},
}
// Add each card that already appeared in a deck
for k := range synergyPerCards[fmt.Sprintf("c-%s",c.ID)] {
synergyObj := CreateCardData(cards[k], decksByBset, numberOfDecksPerColorIdentity, numberOfDecksPerCard, v.ID)
synergy := 0 - synergyObj.PercentageOfDecks
if numberOfDecksPerCard[fmt.Sprintf("c-%s",c.ID)] != 0 {
synergy = (float64(synergyPerCards[fmt.Sprintf("c-%s",c.ID)][k]) / float64(numberOfDecksPerCard[fmt.Sprintf("c-%s",c.ID)])) - synergyObj.PercentageOfDecks
}
synergyObj.Synergy = synergy
detailsObj.Cards[cards[k].CardType] = append(detailsObj.Cards[cards[k].CardType], synergyObj)
}
for k := range detailsObj.Cards {
sort.Slice(detailsObj.Cards[k], func(i, j int) bool {
if detailsObj.Cards[k][i].Synergy == detailsObj.Cards[k][j].Synergy {
return detailsObj.Cards[k][i].PercentageOfDecks > detailsObj.Cards[k][j].PercentageOfDecks
}
return detailsObj.Cards[k][i].Synergy > detailsObj.Cards[k][j].Synergy
})
}
totalSize += int64(unsafe.Sizeof(detailsObj))
pb.Store().Set(fmt.Sprintf("json/commander/%s-%s", c.SetCode, c.SanitizedName), detailsObj)
}
}
}
for k := range cacheBrawlsetData.Cards {
if k == "commander" {
sort.Slice(cacheBrawlsetData.Cards[k], func(i, j int) bool { return cacheBrawlsetData.Cards[k][i].NumberOfDecks > cacheBrawlsetData.Cards[k][j].NumberOfDecks})
} else {
sort.Slice(cacheBrawlsetData.Cards[k], func(i, j int) bool {
if cacheBrawlsetData.Cards[k][i].PercentageOfDecks == cacheBrawlsetData.Cards[k][j].PercentageOfDecks {
return cacheBrawlsetData.Cards[k][i].NumberOfDecks > cacheBrawlsetData.Cards[k][j].NumberOfDecks
}
return cacheBrawlsetData.Cards[k][i].PercentageOfDecks > cacheBrawlsetData.Cards[k][j].PercentageOfDecks
})
}
}
cacheBrawlsetList = append(cacheBrawlsetList, CacheBrawlsetListItem{Name: v.Name, SanitizedName: v.SanitizedName, IconsSvgUri: setIconList})
totalSize += int64(unsafe.Sizeof(cacheBrawlsetData))
pb.Store().Set("json/brawlset/" + v.SanitizedName, cacheBrawlsetData)
}
totalSize += int64(unsafe.Sizeof(cacheBrawlsetList))
pb.Store().Set("json/misc/brawlsets", cacheBrawlsetList)
log.Println(fmt.Sprintf("Total cache size : %d", totalSize))
}
func GetAllColorCombination(s []string) []string {
res := []string{strings.Join(s,"")}
for i := 1; i < len(s); i++ {
combinations := CreateCombination(s, i)
res = append(res, combinations...)
}
return res
}
func CreateCardData(c Carte, decksByBset map[string][]Deck, numberOfDecksPerColorIdentity map[string]int, numberOfDecksPerCard map[string]int, bsetID string) CacheCarteListItem {
price, err := strconv.ParseFloat(c.Price, 64)
if err != nil {
price = 0.0
}
numberOfPossibleDecks := 0
if len(c.ColorIdentity) == 0 {
numberOfPossibleDecks = len(decksByBset[bsetID])
} else {
numberOfPossibleDecks = numberOfDecksPerColorIdentity[strings.Join(c.ColorIdentity,"")]
}
percentageOfDecks := 0.0
if numberOfPossibleDecks != 0 {
percentageOfDecks = float64(numberOfDecksPerCard[c.ID]) / float64(numberOfPossibleDecks)
}
return CacheCarteListItem{
Name: c.Name,
Url: fmt.Sprintf("/card/%s-%s", c.SetCode,c.SanitizedName),
Layout: c.Layout,
SmallImage: c.SmallImage,
SmallImageBack: c.SmallImageBack,
NormalImage: c.NormalImage,
NormalImageBack: c.NormalImageBack,
Price: price,
CardmarketUri: c.CardmarketUri,
NumberOfDecks: numberOfDecksPerCard[c.ID],
NumberOfPossibleDecks: numberOfPossibleDecks,
PercentageOfDecks: percentageOfDecks,
}
}