Replace CardLocation table with ProductLocation

This commit is contained in:
The Magician 2025-01-03 14:21:16 +00:00
parent 540a1df24e
commit 9076edb6f6
18 changed files with 38 additions and 1069 deletions

View File

@ -1,37 +0,0 @@
package database
import (
"database/sql"
"time"
)
const CacheTypeAllCardsBulkData = "AllCardsBulkData"
func GetCacheTimestampByType(db *sql.DB, cacheType string) (string, error) {
var timestamp string
query := "SELECT Stamp FROM CacheTimestamp WHERE CacheType = ?;"
err := db.QueryRow(query, cacheType).Scan(&timestamp)
return timestamp, err
}
func InsertOrUpdateCacheTimestampByType(db *sql.DB, cacheType string, stamp time.Time) error {
query := `INSERT INTO CacheTimestamp (CacheType, Stamp)
VALUES (?, ?)
ON DUPLICATE KEY
UPDATE Stamp = ?;`
insertOrUpdate, err := db.Prepare(query)
defer insertOrUpdate.Close()
if err != nil {
return err
}
_, err = insertOrUpdate.Exec(cacheType, stamp, stamp)
if err != nil {
return err
}
return nil
}

View File

@ -1,12 +0,0 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE IF NOT EXISTS CacheTimestamp (
CacheType ENUM('AllCardsBulkData') PRIMARY KEY,
Stamp DATETIME NOT NULL
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS CacheTimestamp;
-- +goose StatementEnd

View File

@ -1,13 +1,13 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE IF NOT EXISTS CardtraderExpansion (
Id INT PRIMARY KEY AUTO_INCREMENT,
Id INT PRIMARY KEY,
Code VARCHAR(20) NOT NULL,
Name VARCHAR(255) NOT NULL
);
CREATE TABLE IF NOT EXISTS CardtraderBlueprint (
Id INT PRIMARY KEY AUTO_INCREMENT,
Id INT PRIMARY KEY,
ScryfallCardId VARCHAR(100) NULL,
CardtraderCategoryId INT NOT NULL,
CardtraderExpansionId INT NOT NULL,

View File

@ -1,14 +0,0 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE IF NOT EXISTS ScryfallSet (
SetCode VARCHAR(6) PRIMARY KEY,
Name VARCHAR(60) NOT NULL,
CardCount INT NOT NULL,
IconSvgUri VARCHAR(60) NOT NULL
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS ScryfallSet;
-- +goose StatementEnd

View File

@ -3,7 +3,7 @@
CREATE TABLE IF NOT EXISTS StorageArea (
Id INT AUTO_INCREMENT PRIMARY KEY,
Name VARCHAR(100) NOT NULL,
StorageType ENUM('Binder', 'Box')
StorageType ENUM('Binder', 'Box') NOT NULL
);
-- +goose StatementEnd

View File

@ -1,9 +1,9 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE IF NOT EXISTS CardLocation (
CREATE TABLE IF NOT EXISTS ProductLocation (
Id INT AUTO_INCREMENT PRIMARY KEY,
ScryfallCardId VARCHAR(36) NULL,
FOREIGN KEY (ScryfallCardId) REFERENCES ScryfallCard(Id),
CardtraderBlueprintId INT NULL,
FOREIGN KEY (CardtraderBlueprintId) REFERENCES CardtraderBlueprint(Id),
StorageAreaId INT NOT NULL,
FOREIGN KEY (StorageAreaId) REFERENCES StorageArea(Id),
Position INT NULL,
@ -13,5 +13,5 @@ CREATE TABLE IF NOT EXISTS CardLocation (
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS CardLocation;
DROP TABLE IF EXISTS ProductLocation;
-- +goose StatementEnd

View File

@ -1,19 +0,0 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE IF NOT EXISTS ScryfallCard (
Id VARCHAR(36) PRIMARY KEY,
Name VARCHAR(150) NOT NULL,
ScryfallSetCode VARCHAR(6) NOT NULL,
FOREIGN KEY (ScryfallSetCode) REFERENCES ScryfallSet(SetCode),
HasFoilPrinting BOOLEAN NOT NULL,
IsPromo BOOLEAN NOT NULL,
CollectorNumber VARCHAR(10) NOT NULL,
ImageUrl VARCHAR(100) NOT NULL,
Language VARCHAR(3) NOT NULL
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS ScryfallCard;
-- +goose StatementEnd

View File

@ -1,78 +0,0 @@
package database
import (
"database/sql"
)
type ScryfallCard struct {
Id string
Name string
ScryfallSetCode string
HasFoilPrinting bool
IsPromo bool
CollectorNumber string
ImageUrl string
Language string
}
func InsertScryfallCard(db *sql.DB, scryfallCard ScryfallCard) error {
query := `INSERT INTO ScryfallCard (
Id,
Name,
ScryfallSetCode,
HasFoilPrinting,
IsPromo,
CollectorNumber,
ImageUrl,
Language)
VALUES (?, ?, ?, ?, ?, ?, ?, ?);`
insert, err := db.Prepare(query)
defer insert.Close()
if err != nil {
return err
}
_, err = insert.Exec(scryfallCard.Id,
scryfallCard.Name,
scryfallCard.ScryfallSetCode,
scryfallCard.HasFoilPrinting,
scryfallCard.IsPromo,
scryfallCard.CollectorNumber,
scryfallCard.ImageUrl,
scryfallCard.Language)
if err != nil {
return err
}
return nil
}
func GetAllScryfallCards(db *sql.DB) ([]ScryfallCard, error) {
var scryfallCards []ScryfallCard
query := `SELECT Id, Name, ScryfallSetCode, HasFoilPrinting, IsPromo, CollectorNumber, Language FROM CardPrinting;`
rows, err := db.Query(query)
defer rows.Close()
if err != nil {
return scryfallCards, err
}
var scryfallCard ScryfallCard
for rows.Next() {
err := rows.Scan(&scryfallCard.Id,
&scryfallCard.Name,
&scryfallCard.ScryfallSetCode,
&scryfallCard.HasFoilPrinting,
&scryfallCard.IsPromo,
&scryfallCard.CollectorNumber,
&scryfallCard.Language)
if err != nil {
return scryfallCards, err
}
scryfallCards = append(scryfallCards, scryfallCard)
}
return scryfallCards, nil
}

View File

@ -1,23 +0,0 @@
package database
import (
"database/sql"
"sevenkeys/logic/scryfall"
)
func InsertSet(db *sql.DB, set scryfall.Set) error {
query := `INSERT INTO ScryfallSet (SetCode, Name, CardCount, IconSvgUri) VALUES (?, ?, ?, ?);`
insert, err := db.Prepare(query)
defer insert.Close()
if err != nil {
return err
}
_, err = insert.Exec(set.Code, set.Name, set.CardCount, set.IconSvgUri)
if err != nil {
return err
}
return nil
}

View File

@ -1,29 +0,0 @@
package logic
import (
"sevenkeys/database"
"strings"
)
func filterPrinting(scryfallCard database.ScryfallCard, searchCriteria SearchCriteria) bool {
if searchCriteria.SetCode != "" && !strings.Contains(scryfallCard.ScryfallSetCode, searchCriteria.SetCode) {
return true
}
if searchCriteria.CollectorNumber != "" && !strings.Contains(scryfallCard.CollectorNumber, searchCriteria.CollectorNumber) {
return true
}
if searchCriteria.Promo == False && scryfallCard.IsPromo {
return true
}
if searchCriteria.Promo == True && !scryfallCard.IsPromo {
return true
}
if searchCriteria.Language != "" && scryfallCard.Language != searchCriteria.Language {
return true
}
return false
}

View File

@ -1,426 +0,0 @@
package logic
import (
"sevenkeys/database"
"testing"
)
func TestFilterPrinting_ReturnsTrue_IfSetCodeDoesNotMatch(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: false,
IsPromo: false,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "otj",
Foil: False,
Promo: False,
Language: "en",
}
filter := filterPrinting(printing, searchCriteria)
if filter != true {
t.Errorf("filter was false")
}
}
func TestFilterPrinting_ReturnsFalse_IfSetCodeDoesMatch(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: false,
IsPromo: false,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
Foil: False,
Promo: False,
Language: "en",
}
filter := filterPrinting(printing, searchCriteria)
if filter != false {
t.Errorf("filter was true")
}
}
func TestFilterPrinting_ReturnsFalse_IfSetCodeNotSet(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: false,
IsPromo: false,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "",
Foil: False,
Promo: False,
Language: "en",
}
filter := filterPrinting(printing, searchCriteria)
if filter != false {
t.Errorf("filter was true")
}
}
func TestFilterPrinting_ReturnsTrue_IfFoilCardInNonFoilSearch(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: true,
IsPromo: false,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
Foil: False,
Promo: False,
Language: "en",
}
filter := filterPrinting(printing, searchCriteria)
if filter != true {
t.Errorf("filter was false")
}
}
func TestFilterPrinting_ReturnsTrue_IfNonFoilCardInFoilSearch(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: false,
IsPromo: false,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
Foil: True,
Promo: False,
Language: "en",
}
filter := filterPrinting(printing, searchCriteria)
if filter != true {
t.Errorf("filter was false")
}
}
func TestFilterPrinting_ReturnsFalse_IfNonFoilCardInNonFoilSearch(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: false,
IsPromo: false,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
Foil: False,
Promo: False,
Language: "en",
}
filter := filterPrinting(printing, searchCriteria)
if filter != false {
t.Errorf("filter was true")
}
}
func TestFilterPrinting_ReturnsFalse_IfFoilCardInFoilSearch(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: true,
IsPromo: false,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
Foil: True,
Promo: False,
Language: "en",
}
filter := filterPrinting(printing, searchCriteria)
if filter != false {
t.Errorf("filter was true")
}
}
func TestFilterPrinting_ReturnsFalse_IfFoilCardInEitherFoilSearch(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: true,
IsPromo: false,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
Foil: Either,
Promo: False,
Language: "en",
}
filter := filterPrinting(printing, searchCriteria)
if filter != false {
t.Errorf("filter was true")
}
}
func TestFilterPrinting_ReturnsFalse_IfNonFoilCardInEitherFoilSearch(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: false,
IsPromo: false,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
Foil: Either,
Promo: False,
Language: "en",
}
filter := filterPrinting(printing, searchCriteria)
if filter != false {
t.Errorf("filter was true")
}
}
func TestFilterPrinting_ReturnsTrue_IfPromoCardInNonPromoSearch(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: false,
IsPromo: true,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
Foil: False,
Promo: False,
Language: "en",
}
filter := filterPrinting(printing, searchCriteria)
if filter != true {
t.Errorf("filter was false")
}
}
func TestFilterPrinting_ReturnsTrue_IfNonPromoCardInPromoSearch(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: false,
IsPromo: false,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
Foil: False,
Promo: True,
Language: "en",
}
filter := filterPrinting(printing, searchCriteria)
if filter != true {
t.Errorf("filter was false")
}
}
func TestFilterPrinting_ReturnsFalse_IfNonPromoCardInNonPromoSearch(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: false,
IsPromo: false,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
Foil: False,
Promo: False,
Language: "en",
}
filter := filterPrinting(printing, searchCriteria)
if filter != false {
t.Errorf("filter was true")
}
}
func TestFilterPrinting_ReturnsFalse_IfPromoCardInPromoSearch(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: false,
IsPromo: true,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
Foil: False,
Promo: True,
Language: "en",
}
filter := filterPrinting(printing, searchCriteria)
if filter != false {
t.Errorf("filter was true")
}
}
func TestFilterPrinting_ReturnsFalse_IfPromoCardInEitherPromoSearch(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: false,
IsPromo: true,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
Foil: False,
Promo: Either,
Language: "en",
}
filter := filterPrinting(printing, searchCriteria)
if filter != false {
t.Errorf("filter was true")
}
}
func TestFilterPrinting_ReturnsFalse_IfNonPromoCardInEitherPromoSearch(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: false,
IsPromo: false,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
Foil: False,
Promo: Either,
Language: "en",
}
filter := filterPrinting(printing, searchCriteria)
if filter != false {
t.Errorf("filter was true")
}
}
func TestFilterPrinting_ReturnsTrue_IfLanguageDoesNotMatch(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: false,
IsPromo: false,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
Foil: False,
Promo: False,
Language: "de",
}
filter := filterPrinting(printing, searchCriteria)
if filter != true {
t.Errorf("filter was false")
}
}
func TestFilterPrinting_ReturnsFalse_IfLanguageDoesMatch(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: false,
IsPromo: false,
Language: "de",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
Foil: False,
Promo: False,
Language: "de",
}
filter := filterPrinting(printing, searchCriteria)
if filter != false {
t.Errorf("filter was true")
}
}
func TestFilterPrinting_ReturnsFalse_IfLanguageNotSet(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
IsFoil: false,
IsPromo: false,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
Foil: False,
Promo: False,
Language: "",
}
filter := filterPrinting(printing, searchCriteria)
if filter != false {
t.Errorf("filter was true")
}
}
func TestFilterPrinting_ReturnsTrue_IfSetCodeMatchesButCollectorNumberDoesnt(t *testing.T) {
printing := database.CardPrinting{
SetCode: "rtr",
CollectorNumber: "301",
IsFoil: false,
IsPromo: false,
Language: "en",
}
searchCriteria := SearchCriteria{
SetCode: "rtr",
CollectorNumber: "299",
Foil: False,
Promo: False,
Language: "",
}
filter := filterPrinting(printing, searchCriteria)
if filter != true {
t.Errorf("filter was false")
}
}

View File

@ -41,32 +41,6 @@ func GetCardAtLocation(db *sql.DB, cardLocationId int) (database.LocateCardResul
return result, nil
}
func LocateCards(db *sql.DB, cardNames []string, criteria SearchCriteria) ([]database.LocateCardResult, error) {
results, err := database.GetLocateResults(db, cardNames)
if err != nil {
return results, err
}
var filteredResults []database.LocateCardResult
for _, result := range results {
scryfallCard := database.ScryfallCard{
ScryfallSetCode: result.SetCode,
IsPromo: result.IsPromo,
Language: result.Language,
}
filter := filterPrinting(scryfallCard, criteria)
if filter {
continue
}
filteredResults = append(filteredResults, result)
}
return filteredResults, nil
}
func GetLocationDescription(location database.LocateCardResult) string {
var description string

View File

@ -1,63 +0,0 @@
package scryfall
import (
"encoding/json"
"errors"
"io"
"net/http"
"time"
)
type BulkData struct {
Id string `json:"id"`
Uri string `json:"uri"`
Type string `json:"type"`
Name string `json:"name"`
Description string `json:"description"`
DownloadUri string `json:"download_uri"`
UpdatedAt string `json:"updated_at"`
UpdatedAtTime time.Time `json:"ignore"`
Size int `json:"size"`
ContentType string `json:"content_type"`
ContentEncoding string `json:"content_encoding"`
}
const BULK_DATA_URI = "https://api.scryfall.com/bulk-data"
const BulkDataTypeOracleCards string = "oracle_cards"
const BulkDataTypeUniqueArtwork string = "unique_artwork"
const BulkDataTypeDefaultCards string = "default_cards"
const BulkDataTypeAllCards string = "all_cards"
const BulkDataTypeRulings string = "rulings"
func GetBulkDataByType(bulkDataType string) (BulkData, error) {
response, err := http.Get(BULK_DATA_URI + "/" + bulkDataType)
if err != nil {
return BulkData{}, err
}
if response.StatusCode != http.StatusOK {
return BulkData{}, errors.New("HTTP request failed with code: " + string(response.StatusCode))
}
defer response.Body.Close()
bulkDataBytes, err := io.ReadAll(response.Body)
if err != nil {
return BulkData{}, err
}
var bulkData BulkData
err = json.Unmarshal(bulkDataBytes, &bulkData)
if err != nil {
return BulkData{}, err
}
bulkData.UpdatedAtTime, err = time.Parse(ScryfallTimestampFormat, bulkData.UpdatedAt)
if err != nil {
return BulkData{}, err
}
// Round to the nearest second; this is so that comparison with the timestamp stored in the database works as intended
bulkData.UpdatedAtTime = bulkData.UpdatedAtTime.Truncate(time.Second)
return bulkData, nil
}

View File

@ -1,49 +0,0 @@
package scryfall
import (
"encoding/json"
"errors"
"io"
"net/http"
)
type SetList struct {
Object string `json:"object"`
HasMore bool `json:"has_more"`
Data []Set `json:"data"`
}
type Set struct {
Code string `json:"code"`
Name string `json:"name"`
CardCount int `json:"card_count"`
IconSvgUri string `json:"icon_svg_uri"`
Digital bool `json:"digital"`
}
const SETS_API_URL string = "https://api.scryfall.com/sets"
func GetAllSets() ([]Set, error) {
response, err := http.Get(SETS_API_URL)
if err != nil {
return []Set{}, nil
}
if response.StatusCode != http.StatusOK {
return []Set{}, errors.New("HTTP request failed with code: " + string(response.StatusCode))
}
defer response.Body.Close()
setsBytes, err := io.ReadAll(response.Body)
if err != nil {
return []Set{}, err
}
var setList SetList
err = json.Unmarshal(setsBytes, &setList)
if err != nil {
return []Set{}, err
}
return setList.Data, nil
}

View File

@ -1,3 +0,0 @@
package scryfall
const ScryfallTimestampFormat = "2006-01-02T15:04:05.999-07:00"

View File

@ -2,7 +2,6 @@ package logic
import (
"database/sql"
"fmt"
"io"
"os"
"os/exec"
@ -27,38 +26,6 @@ type SearchCriteria struct {
type CardPrintingSearchOptions map[string]string
func GetAllCardPrintingSearchOptions(db *sql.DB, searchCriteria SearchCriteria) (CardPrintingSearchOptions, error) {
var searchOptions CardPrintingSearchOptions = make(CardPrintingSearchOptions)
scryfallCards, err := database.GetAllScryfallCards(db)
if err != nil {
return searchOptions, err
}
for _, scryfallCard := range scryfallCards {
// Filter based on search criteria
filter := filterPrinting(scryfallCard, searchCriteria)
if filter {
continue
}
// Construct search option string
searchString := fmt.Sprintf("%s (%s %s) [%s]",
scryfallCard.Name,
scryfallCard.ScryfallSetCode,
scryfallCard.CollectorNumber,
scryfallCard.Language)
if scryfallCard.IsPromo {
searchString += " PROMO"
}
searchOptions[searchString] = scryfallCard.Id
}
return searchOptions, err
}
type StorageSearchOptions map[string]int
func GetAllStorageSearchOptions(db *sql.DB) (StorageSearchOptions, error) {

View File

@ -41,8 +41,6 @@ func main() {
switch flag.Args()[0] {
case UpdateSubcommand:
update.UpdateScryfallData(db)
err := update.UpdateCardtraderData(db, config.CardtraderToken)
logic.Check(err)
break
@ -103,38 +101,39 @@ func main() {
err = logic.ImportDelverLensCards(db, delverLensCards, storageAreaId)
logic.Check(err)
break
case SearchPrintingsSubcommand:
searchPrintingsCmd := flag.NewFlagSet(SearchPrintingsSubcommand, flag.ExitOnError)
setCode := searchPrintingsCmd.String("set-code", "", "The code for the set the card we're searching for belongs to.")
collectorNumber := searchPrintingsCmd.String("collector-number", "", "The collector number of the card we're searching for.")
//foil := searchPrintingsCmd.String("foil", "E", "Whether the card we're searching for is foil.")
searchPrintingsCmd.Parse(flag.Args()[1:])
searchCriteria := logic.SearchCriteria{
SetCode: *setCode,
CollectorNumber: *collectorNumber,
Promo: logic.Either,
Language: "en",
}
/*
if *foil == "Y" {
searchCriteria.Foil = logic.True
} else if *foil == "N" {
searchCriteria.Foil = logic.False
} else {
searchCriteria.Foil = logic.Either
}
*/
TODO: Rewrite this to search Blueprints
case SearchPrintingsSubcommand:
searchPrintingsCmd := flag.NewFlagSet(SearchPrintingsSubcommand, flag.ExitOnError)
searchOptions, err := logic.GetAllCardPrintingSearchOptions(db, searchCriteria)
logic.Check(err)
id, _, err := logic.GenericSearch(searchOptions)
logic.Check(err)
fmt.Println(id)
break
setCode := searchPrintingsCmd.String("set-code", "", "The code for the set the card we're searching for belongs to.")
collectorNumber := searchPrintingsCmd.String("collector-number", "", "The collector number of the card we're searching for.")
//foil := searchPrintingsCmd.String("foil", "E", "Whether the card we're searching for is foil.")
searchPrintingsCmd.Parse(flag.Args()[1:])
searchCriteria := logic.SearchCriteria{
SetCode: *setCode,
CollectorNumber: *collectorNumber,
Promo: logic.Either,
Language: "en",
}
if *foil == "Y" {
searchCriteria.Foil = logic.True
} else if *foil == "N" {
searchCriteria.Foil = logic.False
} else {
searchCriteria.Foil = logic.Either
}
searchOptions, err := logic.GetAllCardPrintingSearchOptions(db, searchCriteria)
logic.Check(err)
id, _, err := logic.GenericSearch(searchOptions)
logic.Check(err)
fmt.Println(id)
break
*/
case SearchStorageSubcommand:
searchOptions, err := logic.GetAllStorageSearchOptions(db)
logic.Check(err)

View File

@ -1,218 +0,0 @@
package update
import (
"database/sql"
"encoding/json"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"sevenkeys/database"
"sevenkeys/logic"
"sevenkeys/logic/scryfall"
"time"
sqlerr "github.com/go-mysql/errors"
)
func scryfallLog(msg string) {
log.Printf("scryfall: %s\n", msg)
}
const CACHE_DIR string = "cache"
const SET_ICON_CACHE_DIR string = CACHE_DIR + "/seticons/"
const SET_ICON_FILE_EXTENSION string = ".svg"
const ALL_CARDS_CACHE_FILENAME = CACHE_DIR + "/all-cards.json"
const GAME_PAPER = "paper"
func createCacheDirectories() error {
err := os.Mkdir(CACHE_DIR, os.ModePerm)
if err != nil && !os.IsExist(err) {
return err
}
err = os.Mkdir(SET_ICON_CACHE_DIR, os.ModePerm)
if err != nil && !os.IsExist(err) {
return err
}
return nil
}
func checkScryfallUpdates(db *sql.DB, bulkData scryfall.BulkData) (bool, error) {
cachedFileTimestampStr, err := database.GetCacheTimestampByType(db, database.CacheTypeAllCardsBulkData)
if err == sql.ErrNoRows {
return true, nil
} else if err != nil {
return false, err
}
cachedFileTimestamp, err := time.Parse("2006-01-02 15:04:05", cachedFileTimestampStr)
return bulkData.UpdatedAtTime.After(cachedFileTimestamp), nil
}
func updateSets(db *sql.DB) error {
sets, err := scryfall.GetAllSets()
if err != nil {
return err
}
for _, set := range sets {
// We're only interested in paper cards, so skip importing
// any sets that were only released in a video game
if set.Digital {
continue
}
err := database.InsertSet(db, set)
// If we already have this set in the database, then we can just skip it
if ok, insertErr := sqlerr.Error(err); ok {
if insertErr == sqlerr.ErrDupeKey {
continue
}
}
if err != nil {
return err
}
response, err := http.Get(set.IconSvgUri)
defer response.Body.Close()
if err != nil {
return nil
}
iconFilename := SET_ICON_CACHE_DIR + set.Code + SET_ICON_FILE_EXTENSION
iconFile, err := os.Create(iconFilename)
if err != nil {
return err
}
io.Copy(iconFile, response.Body)
}
scryfallLog("Sets updated.")
return nil
}
func cacheBulkCardsFile(db *sql.DB, bulkData scryfall.BulkData) error {
bulkCardsResponse, err := http.Get(bulkData.DownloadUri)
defer bulkCardsResponse.Body.Close()
if err != nil {
return err
}
cacheFile, err := os.Create(ALL_CARDS_CACHE_FILENAME)
defer cacheFile.Close()
if err != nil {
return err
}
io.Copy(cacheFile, bulkCardsResponse.Body)
err = database.InsertOrUpdateCacheTimestampByType(db, database.CacheTypeAllCardsBulkData, bulkData.UpdatedAtTime)
if err != nil {
return err
}
return nil
}
func isPaper(card scryfall.Card) bool {
var paper bool = false
for _, game := range card.Games {
if game == GAME_PAPER {
paper = true
}
}
return paper
}
func getScryfallCard(card scryfall.Card) database.ScryfallCard {
return database.ScryfallCard{
Id: card.Id,
Name: card.Name,
ScryfallSetCode: card.Set,
HasFoilPrinting: card.Foil,
IsPromo: card.Promo,
CollectorNumber: card.CollectorNumber,
ImageUrl: card.ImageUris["png"],
Language: card.Language,
}
}
func updateCards(db *sql.DB, bulkData scryfall.BulkData) error {
scryfallLog("Caching bulk cards file")
err := cacheBulkCardsFile(db, bulkData)
if err != nil {
return err
}
scryfallLog("Cached bulk cards file")
scryfallLog("Reading cached file")
cardsBytes, err := ioutil.ReadFile(ALL_CARDS_CACHE_FILENAME)
if err != nil {
return err
}
scryfallLog("Read cached file")
scryfallLog("Unmarshaling JSON")
var cards []scryfall.Card
err = json.Unmarshal(cardsBytes, &cards)
if err != nil {
return err
}
scryfallLog("Unmarshaled JSON")
scryfallLog("Inserting cards")
for _, card := range cards {
// We're only interested in paper cards, so skip cards or printings of a card which
// aren't available in paper
if !isPaper(card) {
continue
}
scryfallCard := getScryfallCard(card)
err := database.InsertScryfallCard(db, scryfallCard)
// If we already have this card in the database, then we can just skip it
if ok, insertErr := sqlerr.Error(err); ok {
if insertErr == sqlerr.ErrDupeKey {
continue
}
}
if err != nil {
return err
}
}
scryfallLog("Inserted cards")
return nil
}
func UpdateScryfallData(db *sql.DB) {
bulkData, err := scryfall.GetBulkDataByType(scryfall.BulkDataTypeAllCards)
logic.Check(err)
needsUpdate, err := checkScryfallUpdates(db, bulkData)
logic.Check(err)
if !needsUpdate {
scryfallLog("No update required.")
return
}
createCacheDirectories()
err = updateSets(db)
logic.Check(err)
err = updateCards(db, bulkData)
logic.Check(err)
scryfallLog("Update finished.")
}