162 lines
5.2 KiB
Go
162 lines
5.2 KiB
Go
package serra
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/mitchellh/mapstructure"
|
|
"github.com/schollz/progressbar/v3"
|
|
"github.com/spf13/cobra"
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
)
|
|
|
|
func init() {
|
|
rootCmd.AddCommand(updateCmd)
|
|
}
|
|
|
|
var updateCmd = &cobra.Command{
|
|
Aliases: []string{"u"},
|
|
Use: "update",
|
|
Short: "update card values from scryfall",
|
|
Long: `the update mechanism iterates over each card in your collection and fetches its price. after all cards you own in a set are updated, the set value will update. after all sets are updated, the whole collection value is updated.`,
|
|
SilenceErrors: true,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
|
|
client := storageConnect()
|
|
l := Logger()
|
|
defer storageDisconnect(client)
|
|
|
|
// update sets
|
|
setscoll := &Collection{client.Database("serra").Collection("sets")}
|
|
coll := &Collection{client.Database("serra").Collection("cards")}
|
|
totalcoll := &Collection{client.Database("serra").Collection("total")}
|
|
|
|
// predefine query for set analysis. used for total stats later
|
|
projectStage := bson.D{{"$project",
|
|
bson.D{
|
|
{"serra_count", true},
|
|
{"serra_count_foil", true},
|
|
{"set", true},
|
|
{"last_price", bson.D{{"$arrayElemAt", bson.A{"$serra_prices", -1}}}}}}}
|
|
groupStage := bson.D{
|
|
{"$group", bson.D{
|
|
{"_id", ""},
|
|
{"eur", bson.D{{"$sum", bson.D{{"$multiply", bson.A{"$last_price.eur", "$serra_count"}}}}}},
|
|
{"eurfoil", bson.D{{"$sum", bson.D{{"$multiply", bson.A{"$last_price.eur_foil", "$serra_count_foil"}}}}}},
|
|
{"usd", bson.D{{"$sum", bson.D{{"$multiply", bson.A{"$last_price.usd", "$serra_count"}}}}}},
|
|
{"usdfoil", bson.D{{"$sum", bson.D{{"$multiply", bson.A{"$last_price.usd_foil", "$serra_count_foil"}}}}}},
|
|
}}}
|
|
|
|
l.Info("Fetching bulk data from scryfall...")
|
|
downloadURL, err := fetchBulkDownloadURL()
|
|
if err != nil {
|
|
l.Error("Could not extract bulk download URL:", err)
|
|
return err
|
|
}
|
|
l.Infof("Found latest bulkfile url: %s", downloadURL)
|
|
|
|
l.Info("Downloading bulk data file...")
|
|
bulkFilePath, err := downloadBulkData(downloadURL)
|
|
if err != nil {
|
|
l.Error("Could not fetch bulk json from scryfall", err)
|
|
return err
|
|
}
|
|
|
|
l.Info("Loading bulk data file...")
|
|
updatedCards, err := loadBulkFile(bulkFilePath)
|
|
if err != nil {
|
|
l.Error("Could not load bulk file:", err)
|
|
return err
|
|
}
|
|
l.Infof("Successfully loaded %d cards. Starting Update.", len(updatedCards))
|
|
|
|
sets, _ := fetchSets()
|
|
for _, set := range sets.Data {
|
|
|
|
// When downloading new sets, PriceList needs to be initialized
|
|
// This query silently fails if set was already downloaded. Not nice but ok for now.
|
|
// TODO: make this not fail silently
|
|
set.SerraPrices = []PriceEntry{}
|
|
setscoll.storageAddSet(&set)
|
|
|
|
cards, _ := coll.storageFind(bson.D{{"set", set.Code}}, bson.D{{"_id", 1}}, 0, 0)
|
|
|
|
// if no cards in collection for this set, skip it
|
|
if len(cards) == 0 {
|
|
continue
|
|
}
|
|
|
|
bar := progressbar.NewOptions(len(cards),
|
|
progressbar.OptionSetWidth(50),
|
|
progressbar.OptionSetDescription(fmt.Sprintf("%s, %s%s%s\t", set.ReleasedAt[0:4], Yellow, set.Code, Reset)),
|
|
progressbar.OptionEnableColorCodes(true),
|
|
progressbar.OptionShowCount(),
|
|
progressbar.OptionSetTheme(progressbar.Theme{
|
|
Saucer: "[green]=[reset]",
|
|
SaucerHead: "[green]>[reset]",
|
|
SaucerPadding: " ",
|
|
BarStart: "|",
|
|
BarEnd: "| " + set.Name,
|
|
}),
|
|
)
|
|
|
|
for _, card := range cards {
|
|
bar.Add(1)
|
|
updatedCard, err := getCardFromBulk(updatedCards, card.Set, card.CollectorNumber)
|
|
if err != nil {
|
|
l.Error(err)
|
|
continue
|
|
}
|
|
|
|
updatedCard.Prices.Date = primitive.NewDateTimeFromTime(time.Now())
|
|
|
|
update := bson.M{
|
|
"$set": bson.M{"serra_updated": primitive.NewDateTimeFromTime(time.Now()), "prices": updatedCard.Prices, "cmc": updatedCard.Cmc, "cardmarketid": updatedCard.CardmarketID, "tcgplayerid": updatedCard.TCGPlayerID},
|
|
"$push": bson.M{"serra_prices": updatedCard.Prices},
|
|
}
|
|
coll.storageUpdate(bson.M{"_id": bson.M{"$eq": card.ID}}, update)
|
|
}
|
|
fmt.Println()
|
|
|
|
// update set value sum
|
|
|
|
// calculate value summary
|
|
matchStage := bson.D{{"$match", bson.D{{"set", set.Code}}}}
|
|
setValue, _ := coll.storageAggregate(mongo.Pipeline{matchStage, projectStage, groupStage})
|
|
|
|
p := PriceEntry{}
|
|
s := setValue[0]
|
|
|
|
p.Date = primitive.NewDateTimeFromTime(time.Now())
|
|
|
|
// fill struct PriceEntry with map from mongoresult
|
|
mapstructure.Decode(s, &p)
|
|
|
|
// do the update
|
|
setUpdate := bson.M{
|
|
"$set": bson.M{"serra_updated": p.Date, "cardcount": set.CardCount},
|
|
"$push": bson.M{"serra_prices": p},
|
|
}
|
|
setscoll.storageUpdate(bson.M{"code": bson.M{"$eq": set.Code}}, setUpdate)
|
|
}
|
|
|
|
totalValue, _ := coll.storageAggregate(mongo.Pipeline{projectStage, groupStage})
|
|
|
|
t := PriceEntry{}
|
|
t.Date = primitive.NewDateTimeFromTime(time.Now())
|
|
mapstructure.Decode(totalValue[0], &t)
|
|
|
|
// This is here to be able to fetch currency from
|
|
// constructed new priceentry
|
|
tmpCard := Card{}
|
|
tmpCard.Prices = t
|
|
|
|
l.Infof("\nUpdating total value of collection to: %s%.02f%s%s\n", Yellow, tmpCard.getValue(false)+tmpCard.getValue(true), getCurrency(), Reset)
|
|
totalcoll.storageAddTotal(t)
|
|
|
|
return nil
|
|
},
|
|
}
|