Compare commits
No commits in common. "99ba02ef059ea8e5d041e5176cddbf6ca39af2be" and "827a71f6d3cc5c546c8e3278258f44d7ab9c40b4" have entirely different histories.
99ba02ef05
...
827a71f6d3
|
@ -1,24 +1,17 @@
|
|||
build:
|
||||
go build
|
||||
|
||||
connect:
|
||||
mysql --user=root --password=$(shell pass show sevenkeys/mysql)
|
||||
|
||||
dump:
|
||||
mysqldump --user=root --password=$(shell pass show sevenkeys/mysql) sevenkeys >sevenkeys.sql
|
||||
|
||||
dev_up:
|
||||
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_dev?parseTime=true&multiStatements=true" up
|
||||
dev_down:
|
||||
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_dev?parseTime=true&multiStatements=true" down
|
||||
dev_reset:
|
||||
dev_create:
|
||||
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_development?parseTime=true&multiStatements=true" up
|
||||
dev_rollback:
|
||||
rm -rf cache/
|
||||
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_dev?parseTime=true&multiStatements=true" reset
|
||||
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_development?parseTime=true&multiStatements=true" reset
|
||||
|
||||
prod_up:
|
||||
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_prod?parseTime=true&multiStatements=true" up
|
||||
prod_down:
|
||||
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_prod?parseTime=true&multiStatements=true" down
|
||||
prod_reset:
|
||||
prod_create:
|
||||
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys?parseTime=true&multiStatements=true" up
|
||||
prod_rollback:
|
||||
rm -rf cache/
|
||||
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_prod?parseTime=true&multiStatements=true" reset
|
||||
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys?parseTime=true&multiStatements=true" reset
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"DatabaseConfig": {
|
||||
"User": "root",
|
||||
"Passwd": "o7MS6CIn660jIApSP",
|
||||
"Net": "tcp",
|
||||
"Addr": "127.0.0.1:3306",
|
||||
"DBName": "sevenkeys_dev",
|
||||
"AllowNativePasswords": true
|
||||
},
|
||||
"CardtraderToken": "eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJjYXJkdHJhZGVyLXByb2R1Y3Rpb24iLCJzdWIiOiJhcHA6MTI4NzciLCJhdWQiOiJhcHA6MTI4NzciLCJleHAiOjQ4ODkzNDkwOTYsImp0aSI6IjIyZTdmOGVjLWMzYWQtNDUzYi1iMWIxLWQ0Y2M5ZTFjNmExNiIsImlhdCI6MTczMzY3NTQ5NiwibmFtZSI6IlRoZVdoZWVsT2ZGb3J0dW5lIEFwcCAyMDI0MTEyNTIwMTQzNCJ9.rDAdlgPPcyisLdXHu1suyqcha6SzKamYBg3pfqThTAVGVY0kL3I_mytiID1UX4zqy_7uSODmtVX3w7gcJDtZ3wEt2QQVOmDGPWjOQcXblSDTku7T6xz7_J03MEdrmyXag7EQ9iGW3JUEa6mjy2y4Cznr8AJ1i4_66ui_luinOmjnLbXrjNAjqtSkAOfDO8BL07MxR4bkwaRBED5GGh14NlWeeDttZfAuTWXXfZ0da9OUelps09woztUGPwjguCx1Mu1fXZhC1k68K-Lm0rizkhKAzGoTm3OB1WOKGD9G4deENj_vnsrT-3EbwGjBZNB8eHWtCKliybiwowONaJYMuQ"
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
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(×tamp)
|
||||
|
||||
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
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type CardPrinting struct {
|
||||
Id string
|
||||
Name string
|
||||
SetCode string
|
||||
IsFoil bool
|
||||
IsPromo bool
|
||||
CollectorNumber string
|
||||
ImageUrl string
|
||||
Language string
|
||||
}
|
||||
|
||||
func InsertCardPrinting(db *sql.DB, cardPrinting CardPrinting) error {
|
||||
query := `INSERT INTO CardPrinting (
|
||||
Id,
|
||||
Name,
|
||||
SetCode,
|
||||
IsFoil,
|
||||
IsPromo,
|
||||
CollectorNumber,
|
||||
ImageUrl,
|
||||
Language)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?);`
|
||||
|
||||
insert, err := db.Prepare(query)
|
||||
defer insert.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = insert.Exec(cardPrinting.Id, cardPrinting.Name, cardPrinting.SetCode, cardPrinting.IsFoil, cardPrinting.IsPromo, cardPrinting.CollectorNumber, cardPrinting.ImageUrl, cardPrinting.Language)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetAllCardPrintings(db *sql.DB) ([]CardPrinting, error) {
|
||||
var cardPrintings []CardPrinting
|
||||
|
||||
query := `SELECT Id, Name, SetCode, IsFoil, IsPromo, CollectorNumber, Language FROM CardPrinting;`
|
||||
rows, err := db.Query(query)
|
||||
defer rows.Close()
|
||||
if err != nil {
|
||||
return cardPrintings, err
|
||||
}
|
||||
|
||||
var printing CardPrinting
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&printing.Id, &printing.Name, &printing.SetCode, &printing.IsFoil, &printing.IsPromo, &printing.CollectorNumber, &printing.Language)
|
||||
if err != nil {
|
||||
return cardPrintings, err
|
||||
}
|
||||
|
||||
cardPrintings = append(cardPrintings, printing)
|
||||
}
|
||||
|
||||
return cardPrintings, nil
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/mtgban/go-mtgban/cardtrader"
|
||||
)
|
||||
|
||||
func InsertCardtraderBlueprint(db *sql.DB, blueprint cardtrader.Blueprint) error {
|
||||
query := `INSERT INTO CardtraderBlueprint (
|
||||
Id,
|
||||
ScryfallCardId,
|
||||
CardtraderCategoryId,
|
||||
CardtraderExpansionId,
|
||||
Name,
|
||||
CollectorNumber
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?, ?
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
Id = ?,
|
||||
ScryfallCardId = ?,
|
||||
CardtraderCategoryId = ?,
|
||||
CardtraderExpansionId = ?,
|
||||
Name = ?,
|
||||
CollectorNumber = ?;`
|
||||
|
||||
insert, err := db.Prepare(query)
|
||||
defer insert.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = insert.Exec(blueprint.Id,
|
||||
blueprint.ScryfallId,
|
||||
blueprint.CategoryId,
|
||||
blueprint.ExpansionId,
|
||||
blueprint.Name,
|
||||
blueprint.FixedProperties.Number,
|
||||
blueprint.Id,
|
||||
blueprint.ScryfallId,
|
||||
blueprint.CategoryId,
|
||||
blueprint.ExpansionId,
|
||||
blueprint.Name,
|
||||
blueprint.FixedProperties.Number)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetAllCardtraderBlueprints(db *sql.DB) ([]cardtrader.Blueprint, error) {
|
||||
var blueprints []cardtrader.Blueprint
|
||||
|
||||
query := `SELECT * FROM CardtraderBlueprint;`
|
||||
rows, err := db.Query(query)
|
||||
defer rows.Close()
|
||||
if err != nil {
|
||||
return blueprints, err
|
||||
}
|
||||
|
||||
var blueprint cardtrader.Blueprint
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&blueprint.Id, &blueprint.CategoryId, &blueprint.ExpansionId, &blueprint.Name, &blueprint.FixedProperties.Number)
|
||||
if err != nil {
|
||||
return blueprints, err
|
||||
}
|
||||
|
||||
blueprints = append(blueprints, blueprint)
|
||||
}
|
||||
|
||||
return blueprints, nil
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/mtgban/go-mtgban/cardtrader"
|
||||
)
|
||||
|
||||
func InsertCardtraderExpansion(db *sql.DB, expansion cardtrader.Expansion) error {
|
||||
query := `INSERT INTO CardtraderExpansion (Id, Code, Name) VALUES (?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE Id = ?, Code = ?, Name = ?;`
|
||||
|
||||
insert, err := db.Prepare(query)
|
||||
defer insert.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = insert.Exec(expansion.Id, expansion.Code, expansion.Name, expansion.Id, expansion.Code, expansion.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
-- +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
|
|
@ -1,25 +0,0 @@
|
|||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
CREATE TABLE IF NOT EXISTS CardtraderExpansion (
|
||||
Id INT PRIMARY KEY,
|
||||
Code VARCHAR(20) NOT NULL,
|
||||
Name VARCHAR(255) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS CardtraderBlueprint (
|
||||
Id INT PRIMARY KEY,
|
||||
ScryfallCardId VARCHAR(100) NULL,
|
||||
CardtraderCategoryId INT NOT NULL,
|
||||
CardtraderExpansionId INT NOT NULL,
|
||||
FOREIGN KEY (CardtraderExpansionId) REFERENCES CardtraderExpansion(Id),
|
||||
Name VARCHAR(255) NOT NULL,
|
||||
CollectorNumber VARCHAR(50) NOT NULL
|
||||
);
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
DROP TABLE IF EXISTS CardtraderBlueprint;
|
||||
|
||||
DROP TABLE IF EXISTS CardtraderExpansion;
|
||||
-- +goose StatementEnd
|
|
@ -0,0 +1,14 @@
|
|||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
CREATE TABLE IF NOT EXISTS ExpansionSet (
|
||||
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 ExpansionSet;
|
||||
-- +goose StatementEnd
|
|
@ -0,0 +1,19 @@
|
|||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
CREATE TABLE IF NOT EXISTS CardPrinting (
|
||||
Id VARCHAR(37) PRIMARY KEY, -- GUID, plus one character for foil/nonfoil
|
||||
Name VARCHAR(150) NOT NULL,
|
||||
SetCode VARCHAR(6) NOT NULL,
|
||||
FOREIGN KEY (SetCode) REFERENCES ExpansionSet(SetCode),
|
||||
IsFoil 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 CardPrinting;
|
||||
-- +goose StatementEnd
|
|
@ -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') NOT NULL
|
||||
StorageType ENUM('Binder', 'Box')
|
||||
);
|
||||
-- +goose StatementEnd
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
CREATE TABLE IF NOT EXISTS ProductLocation (
|
||||
CREATE TABLE IF NOT EXISTS CardLocation (
|
||||
Id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
CardtraderBlueprintId INT NULL,
|
||||
FOREIGN KEY (CardtraderBlueprintId) REFERENCES CardtraderBlueprint(Id),
|
||||
CardPrintingId VARCHAR(37) NULL,
|
||||
FOREIGN KEY (CardPrintingId) REFERENCES CardPrinting(Id),
|
||||
StorageAreaId INT NOT NULL,
|
||||
FOREIGN KEY (StorageAreaId) REFERENCES StorageArea(Id),
|
||||
Position INT NULL,
|
||||
|
@ -13,5 +13,5 @@ CREATE TABLE IF NOT EXISTS ProductLocation (
|
|||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
DROP TABLE IF EXISTS ProductLocation;
|
||||
DROP TABLE IF EXISTS CardLocation;
|
||||
-- +goose StatementEnd
|
|
@ -1,33 +0,0 @@
|
|||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
INSERT INTO CardtraderExpansion (
|
||||
Id,
|
||||
Code,
|
||||
Name
|
||||
) VALUES (
|
||||
-1,
|
||||
'None',
|
||||
'None'
|
||||
);
|
||||
|
||||
INSERT INTO CardtraderBlueprint (
|
||||
Id,
|
||||
ScryfallCardId,
|
||||
CardtraderCategoryId,
|
||||
CardtraderExpansionId,
|
||||
Name,
|
||||
CollectorNumber
|
||||
) VALUES (
|
||||
-1,
|
||||
'00000000-0000-0000-0000-000000000000',
|
||||
1,
|
||||
-1,
|
||||
'Scanned Card Placeholder',
|
||||
0
|
||||
);
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
DELETE FROM CardtraderBlueprint WHERE Id = -1;
|
||||
-- +goose StatementEnd
|
|
@ -0,0 +1,39 @@
|
|||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
INSERT INTO ExpansionSet (
|
||||
SetCode,
|
||||
Name,
|
||||
CardCount,
|
||||
IconSvgUri
|
||||
) VALUES (
|
||||
'null',
|
||||
'None',
|
||||
0,
|
||||
''
|
||||
);
|
||||
|
||||
INSERT INTO CardPrinting (
|
||||
Id,
|
||||
Name,
|
||||
SetCode,
|
||||
IsFoil,
|
||||
IsPromo,
|
||||
CollectorNumber,
|
||||
ImageUrl,
|
||||
Language
|
||||
) VALUES (
|
||||
'00000000-0000-0000-0000-0000000000000',
|
||||
'Scanned Card Placeholder',
|
||||
'null',
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
'',
|
||||
'en'
|
||||
);
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
DELETE FROM CardPrinting WHERE Id = '00000000-0000-0000-0000-0000000000000';
|
||||
-- +goose StatementEnd
|
|
@ -0,0 +1,43 @@
|
|||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
CREATE TABLE IF NOT EXISTS CardtraderGame (
|
||||
Id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
Name VARCHAR(255) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS CardtraderCategory (
|
||||
Id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
CardtraderGameId INT NOT NULL,
|
||||
FOREIGN KEY (CardtraderGameId) REFERENCES CardtraderGame(Id),
|
||||
Name VARCHAR(255) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS CardtraderExpansion (
|
||||
Id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
CardtraderGameId INT NOT NULL,
|
||||
FOREIGN KEY (CardtraderGameId) REFERENCES CardtraderGame(Id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS CardtraderBlueprint (
|
||||
Id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
CardtraderGameId INT NOT NULL,
|
||||
FOREIGN KEY (CardtraderGameId) REFERENCES CardtraderGame(Id),
|
||||
CardtraderCategoryId INT NOT NULL,
|
||||
FOREIGN KEY (CardtraderCategoryId) REFERENCES CardtraderCategory(Id),
|
||||
CardtraderExpansionId INT NOT NULL,
|
||||
FOREIGN KEY (CardtraderExpansionId) REFERENCES CardtraderExpansion(Id),
|
||||
Name VARCHAR(255) NOT NULL,
|
||||
CollectorNumber VARCHAR(10) NOT NULL
|
||||
);
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
DROP TABLE IF EXISTS CardtraderBlueprint;
|
||||
|
||||
DROP TABLE IF EXISTS CardtraderExpansion;
|
||||
|
||||
DROP TABLE IF EXISTS CardtraderCategory;
|
||||
|
||||
DROP TABLE IF EXISTS CardtraderGame;
|
||||
-- +goose StatementEnd
|
|
@ -0,0 +1,23 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"sevenkeys/logic/scryfall"
|
||||
)
|
||||
|
||||
func InsertSet(db *sql.DB, set scryfall.Set) error {
|
||||
query := `INSERT INTO ExpansionSet (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
|
||||
}
|
|
@ -14,7 +14,7 @@ type DelverLensCard struct {
|
|||
func ParseExportFile(filename string) ([]DelverLensCard, error) {
|
||||
var cards []DelverLensCard
|
||||
|
||||
file, err := os.Open(filename)
|
||||
file, err := os.Open("/home/viciouscirce/dox/sevenkeys_imports/" + filename)
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
return cards, err
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package logic
|
||||
|
||||
import "os"
|
||||
|
||||
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"
|
||||
|
||||
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
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"sevenkeys/database"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func filterPrinting(printing database.CardPrinting, searchCriteria SearchCriteria) bool {
|
||||
if searchCriteria.SetCode != "" && !strings.Contains(printing.SetCode, searchCriteria.SetCode) {
|
||||
return true
|
||||
}
|
||||
|
||||
if searchCriteria.CollectorNumber != "" && !strings.Contains(printing.CollectorNumber, searchCriteria.CollectorNumber) {
|
||||
return true
|
||||
}
|
||||
|
||||
if searchCriteria.Foil == False && printing.IsFoil {
|
||||
return true
|
||||
}
|
||||
if searchCriteria.Foil == True && !printing.IsFoil {
|
||||
return true
|
||||
}
|
||||
|
||||
if searchCriteria.Promo == False && printing.IsPromo {
|
||||
return true
|
||||
}
|
||||
if searchCriteria.Promo == True && !printing.IsPromo {
|
||||
return true
|
||||
}
|
||||
|
||||
if searchCriteria.Language != "" && printing.Language != searchCriteria.Language {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,426 @@
|
|||
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")
|
||||
}
|
||||
}
|
|
@ -41,6 +41,33 @@ 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 {
|
||||
printing := database.CardPrinting{
|
||||
SetCode: result.SetCode,
|
||||
IsFoil: result.IsFoil,
|
||||
IsPromo: result.IsPromo,
|
||||
Language: result.Language,
|
||||
}
|
||||
|
||||
filter := filterPrinting(printing, criteria)
|
||||
if filter {
|
||||
continue
|
||||
}
|
||||
|
||||
filteredResults = append(filteredResults, result)
|
||||
}
|
||||
|
||||
return filteredResults, nil
|
||||
}
|
||||
|
||||
func GetLocationDescription(location database.LocateCardResult) string {
|
||||
var description string
|
||||
|
||||
|
@ -50,6 +77,9 @@ func GetLocationDescription(location database.LocateCardResult) string {
|
|||
location.CollectorNumber,
|
||||
location.Language)
|
||||
|
||||
if location.IsFoil {
|
||||
description += " FOIL"
|
||||
}
|
||||
if location.IsPromo {
|
||||
description += " PROMO"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
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
|
||||
}
|
|
@ -1,16 +1,5 @@
|
|||
package scryfall
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const SCRYFALL_API_CARDS = "/cards/"
|
||||
const CARD_IMAGEURIS_KEY_PNG = "png"
|
||||
|
||||
type Card struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
|
@ -23,38 +12,3 @@ type Card struct {
|
|||
ImageUris map[string]string `json:"image_uris"`
|
||||
Language string `json:"lang"`
|
||||
}
|
||||
|
||||
func (c *ScryfallClient) GetCardById(id string) (Card, error) {
|
||||
var card Card
|
||||
|
||||
response, err := c.httpClient.Get(c.baseURL + SCRYFALL_API_CARDS + id)
|
||||
if err != nil {
|
||||
return card, err
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return card, errors.New("HTTP request failed with code: " + fmt.Sprint(response.StatusCode))
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
cardBytes, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return card, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(cardBytes, &card)
|
||||
if err != nil {
|
||||
return card, err
|
||||
}
|
||||
|
||||
return card, nil
|
||||
}
|
||||
|
||||
func (c *ScryfallClient) GetCardImageUrlById(id string) (string, error) {
|
||||
card, err := c.GetCardById(id)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return card.ImageUris[CARD_IMAGEURIS_KEY_PNG], nil
|
||||
}
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
package scryfall
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const TEST_ID = "56ebc372-aabd-4174-a943-c7bf59e5028d"
|
||||
const TEST_ENDPOINT = "/cards/" + TEST_ID
|
||||
const TESTDATA_DIRECTORY = "testdata/"
|
||||
const TEST_GOOD_CARD_DATA_FILENAME = "card.json"
|
||||
|
||||
var (
|
||||
mux *http.ServeMux
|
||||
server *httptest.Server
|
||||
client *ScryfallClient
|
||||
)
|
||||
|
||||
func setup() func() {
|
||||
mux = http.NewServeMux()
|
||||
server = httptest.NewServer(mux)
|
||||
|
||||
client, _ = NewScryfallClient(BaseURL(server.URL))
|
||||
|
||||
return func() {
|
||||
server.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func fixture(filename string) string {
|
||||
bytes, err := ioutil.ReadFile(TESTDATA_DIRECTORY + filename)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return string(bytes)
|
||||
}
|
||||
|
||||
func Test_GetCardImageUrlById_ReturnsUrl_ForSuccessfulRequest(t *testing.T) {
|
||||
teardown := setup()
|
||||
defer teardown()
|
||||
|
||||
mux.HandleFunc(TEST_ENDPOINT, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, fixture(TEST_GOOD_CARD_DATA_FILENAME))
|
||||
})
|
||||
|
||||
url, err := client.GetCardImageUrlById(TEST_ID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if url != "https://cards.scryfall.io/png/front/5/6/56ebc372-aabd-4174-a943-c7bf59e5028d.png?1562629953" {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package scryfall
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const SCRYFALL_API_URL string = "https://api.scryfall.com/"
|
||||
|
||||
type ScryfallClient struct {
|
||||
baseURL string
|
||||
httpClient *http.Client
|
||||
}
|
||||
type ScryfallClientOption func(*ScryfallClient) error
|
||||
|
||||
func NewScryfallClient(options ...ScryfallClientOption) (*ScryfallClient, error) {
|
||||
client := &ScryfallClient{
|
||||
baseURL: SCRYFALL_API_URL,
|
||||
httpClient: &http.Client{
|
||||
Timeout: time.Second * 30,
|
||||
},
|
||||
}
|
||||
|
||||
if err := client.parseOptions(options...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func BaseURL(baseURL string) ScryfallClientOption {
|
||||
return func(c *ScryfallClient) error {
|
||||
c.baseURL = baseURL
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ScryfallClient) parseOptions(options ...ScryfallClientOption) error {
|
||||
for _, option := range options {
|
||||
err := option(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
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
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
|
||||
{
|
||||
"object": "card",
|
||||
"id": "56ebc372-aabd-4174-a943-c7bf59e5028d",
|
||||
"oracle_id": "e43e06fb-52b7-4f38-8fac-f31973b043f7",
|
||||
"multiverse_ids": [
|
||||
37113
|
||||
],
|
||||
"mtgo_id": 17622,
|
||||
"mtgo_foil_id": 17623,
|
||||
"tcgplayer_id": 10190,
|
||||
"cardmarket_id": 2266,
|
||||
"name": "Phantom Nishoba",
|
||||
"lang": "en",
|
||||
"released_at": "2002-05-27",
|
||||
"uri": "https://api.scryfall.com/cards/56ebc372-aabd-4174-a943-c7bf59e5028d",
|
||||
"scryfall_uri": "https://scryfall.com/card/jud/140/phantom-nishoba?utm_source=api",
|
||||
"layout": "normal",
|
||||
"highres_image": true,
|
||||
"image_status": "highres_scan",
|
||||
"image_uris": {
|
||||
"small": "https://cards.scryfall.io/small/front/5/6/56ebc372-aabd-4174-a943-c7bf59e5028d.jpg?1562629953",
|
||||
"normal": "https://cards.scryfall.io/normal/front/5/6/56ebc372-aabd-4174-a943-c7bf59e5028d.jpg?1562629953",
|
||||
"large": "https://cards.scryfall.io/large/front/5/6/56ebc372-aabd-4174-a943-c7bf59e5028d.jpg?1562629953",
|
||||
"png": "https://cards.scryfall.io/png/front/5/6/56ebc372-aabd-4174-a943-c7bf59e5028d.png?1562629953",
|
||||
"art_crop": "https://cards.scryfall.io/art_crop/front/5/6/56ebc372-aabd-4174-a943-c7bf59e5028d.jpg?1562629953",
|
||||
"border_crop": "https://cards.scryfall.io/border_crop/front/5/6/56ebc372-aabd-4174-a943-c7bf59e5028d.jpg?1562629953"
|
||||
},
|
||||
"mana_cost": "{5}{G}{W}",
|
||||
"cmc": 7,
|
||||
"type_line": "Creature — Cat Beast Spirit",
|
||||
"oracle_text": "Trample\nPhantom Nishoba enters with seven +1/+1 counters on it.\nWhenever Phantom Nishoba deals damage, you gain that much life.\nIf damage would be dealt to Phantom Nishoba, prevent that damage. Remove a +1/+1 counter from Phantom Nishoba.",
|
||||
"power": "0",
|
||||
"toughness": "0",
|
||||
"colors": [
|
||||
"G",
|
||||
"W"
|
||||
],
|
||||
"color_identity": [
|
||||
"G",
|
||||
"W"
|
||||
],
|
||||
"keywords": [
|
||||
"Trample"
|
||||
],
|
||||
"legalities": {
|
||||
"standard": "not_legal",
|
||||
"future": "not_legal",
|
||||
"historic": "not_legal",
|
||||
"timeless": "not_legal",
|
||||
"gladiator": "not_legal",
|
||||
"pioneer": "not_legal",
|
||||
"explorer": "not_legal",
|
||||
"modern": "not_legal",
|
||||
"legacy": "legal",
|
||||
"pauper": "not_legal",
|
||||
"vintage": "legal",
|
||||
"penny": "not_legal",
|
||||
"commander": "legal",
|
||||
"oathbreaker": "legal",
|
||||
"standardbrawl": "not_legal",
|
||||
"brawl": "not_legal",
|
||||
"alchemy": "not_legal",
|
||||
"paupercommander": "not_legal",
|
||||
"duel": "legal",
|
||||
"oldschool": "not_legal",
|
||||
"premodern": "legal",
|
||||
"predh": "legal"
|
||||
},
|
||||
"games": [
|
||||
"paper",
|
||||
"mtgo"
|
||||
],
|
||||
"reserved": false,
|
||||
"foil": true,
|
||||
"nonfoil": true,
|
||||
"finishes": [
|
||||
"nonfoil",
|
||||
"foil"
|
||||
],
|
||||
"oversized": false,
|
||||
"promo": false,
|
||||
"reprint": false,
|
||||
"variation": false,
|
||||
"set_id": "cd82de1a-36fd-4618-bfe8-b45532a582d9",
|
||||
"set": "jud",
|
||||
"set_name": "Judgment",
|
||||
"set_type": "expansion",
|
||||
"set_uri": "https://api.scryfall.com/sets/cd82de1a-36fd-4618-bfe8-b45532a582d9",
|
||||
"set_search_uri": "https://api.scryfall.com/cards/search?order=set&q=e%3Ajud&unique=prints",
|
||||
"scryfall_set_uri": "https://scryfall.com/sets/jud?utm_source=api",
|
||||
"rulings_uri": "https://api.scryfall.com/cards/56ebc372-aabd-4174-a943-c7bf59e5028d/rulings",
|
||||
"prints_search_uri": "https://api.scryfall.com/cards/search?order=released&q=oracleid%3Ae43e06fb-52b7-4f38-8fac-f31973b043f7&unique=prints",
|
||||
"collector_number": "140",
|
||||
"digital": false,
|
||||
"rarity": "rare",
|
||||
"card_back_id": "0aeebaf5-8c7d-4636-9e82-8c27447861f7",
|
||||
"artist": "Arnie Swekel",
|
||||
"artist_ids": [
|
||||
"af10ecf2-eb82-4100-97b2-6c236b0fa644"
|
||||
],
|
||||
"illustration_id": "2bdc53d2-1f4b-4f6d-b59d-985ff2a01268",
|
||||
"border_color": "black",
|
||||
"frame": "1997",
|
||||
"full_art": false,
|
||||
"textless": false,
|
||||
"booster": true,
|
||||
"story_spotlight": false,
|
||||
"edhrec_rank": 10936,
|
||||
"penny_rank": 4165,
|
||||
"prices": {
|
||||
"usd": "3.99",
|
||||
"usd_foil": "29.06",
|
||||
"usd_etched": null,
|
||||
"eur": "4.24",
|
||||
"eur_foil": "28.93",
|
||||
"tix": "0.53"
|
||||
},
|
||||
"related_uris": {
|
||||
"gatherer": "https://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=37113&printed=false",
|
||||
"tcgplayer_infinite_articles": "https://partner.tcgplayer.com/c/4931599/1830156/21018?subId1=api&trafcat=infinite&u=https%3A%2F%2Finfinite.tcgplayer.com%2Fsearch%3FcontentMode%3Darticle%26game%3Dmagic%26partner%3Dscryfall%26q%3DPhantom%2BNishoba",
|
||||
"tcgplayer_infinite_decks": "https://partner.tcgplayer.com/c/4931599/1830156/21018?subId1=api&trafcat=infinite&u=https%3A%2F%2Finfinite.tcgplayer.com%2Fsearch%3FcontentMode%3Ddeck%26game%3Dmagic%26partner%3Dscryfall%26q%3DPhantom%2BNishoba",
|
||||
"edhrec": "https://edhrec.com/route/?cc=Phantom+Nishoba"
|
||||
},
|
||||
"purchase_uris": {
|
||||
"tcgplayer": "https://partner.tcgplayer.com/c/4931599/1830156/21018?subId1=api&u=https%3A%2F%2Fwww.tcgplayer.com%2Fproduct%2F10190%3Fpage%3D1",
|
||||
"cardmarket": "https://www.cardmarket.com/en/Magic/Products/Singles/Judgment/Phantom-Nishoba?referrer=scryfall&utm_campaign=card_prices&utm_medium=text&utm_source=scryfall",
|
||||
"cardhoarder": "https://www.cardhoarder.com/cards/17622?affiliate_id=scryfall&ref=card-profile&utm_campaign=affiliate&utm_medium=card&utm_source=scryfall"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package scryfall
|
||||
|
||||
const ScryfallTimestampFormat = "2006-01-02T15:04:05.999-07:00"
|
|
@ -2,6 +2,7 @@ package logic
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -20,12 +21,45 @@ const (
|
|||
type SearchCriteria struct {
|
||||
SetCode string
|
||||
CollectorNumber string
|
||||
Foil Triadic
|
||||
Promo Triadic
|
||||
Language string
|
||||
}
|
||||
|
||||
type CardPrintingSearchOptions map[string]string
|
||||
|
||||
func GetAllCardPrintingSearchOptions(db *sql.DB, searchCriteria SearchCriteria) (CardPrintingSearchOptions, error) {
|
||||
var searchOptions CardPrintingSearchOptions = make(CardPrintingSearchOptions)
|
||||
|
||||
cardPrintings, err := database.GetAllCardPrintings(db)
|
||||
if err != nil {
|
||||
return searchOptions, err
|
||||
}
|
||||
|
||||
for _, printing := range cardPrintings {
|
||||
// Filter based on search criteria
|
||||
filter := filterPrinting(printing, searchCriteria)
|
||||
if filter {
|
||||
continue
|
||||
}
|
||||
|
||||
// Construct search option string
|
||||
searchString := fmt.Sprintf("%s (%s %s) [%s]", printing.Name, printing.SetCode, printing.CollectorNumber, printing.Language)
|
||||
|
||||
if printing.IsFoil {
|
||||
searchString += " FOIL"
|
||||
}
|
||||
|
||||
if printing.IsPromo {
|
||||
searchString += " PROMO"
|
||||
}
|
||||
|
||||
searchOptions[searchString] = printing.Id
|
||||
}
|
||||
|
||||
return searchOptions, err
|
||||
}
|
||||
|
||||
type StorageSearchOptions map[string]int
|
||||
|
||||
func GetAllStorageSearchOptions(db *sql.DB) (StorageSearchOptions, error) {
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"sevenkeys/database"
|
||||
"sevenkeys/logic/scryfall"
|
||||
"time"
|
||||
|
||||
sqlerr "github.com/go-mysql/errors"
|
||||
)
|
||||
|
||||
const GAME_PAPER = "paper"
|
||||
|
||||
func CheckForUpdates(db *sql.DB, bulkData scryfall.BulkData) (bool, error) {
|
||||
// TODO: We also want to update if:
|
||||
// - there is no cached version of the all-cards.json file or it is empty
|
||||
// - The set icon files are missing
|
||||
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)
|
||||
}
|
||||
|
||||
fmt.Println("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 getCardPrintings(card scryfall.Card) []database.CardPrinting {
|
||||
var printings []database.CardPrinting
|
||||
|
||||
if card.Foil {
|
||||
printings = append(printings, database.CardPrinting{
|
||||
Id: card.Id + "f",
|
||||
Name: card.Name,
|
||||
SetCode: card.Set,
|
||||
IsFoil: true,
|
||||
IsPromo: card.Promo,
|
||||
CollectorNumber: card.CollectorNumber,
|
||||
ImageUrl: card.ImageUris["png"],
|
||||
Language: card.Language,
|
||||
})
|
||||
}
|
||||
|
||||
if card.NonFoil {
|
||||
printings = append(printings, database.CardPrinting{
|
||||
Id: card.Id + "n",
|
||||
Name: card.Name,
|
||||
SetCode: card.Set,
|
||||
IsFoil: false,
|
||||
IsPromo: card.Promo,
|
||||
CollectorNumber: card.CollectorNumber,
|
||||
Language: card.Language,
|
||||
})
|
||||
}
|
||||
|
||||
return printings
|
||||
}
|
||||
|
||||
func UpdateCards(db *sql.DB, bulkData scryfall.BulkData) error {
|
||||
log.Println("Caching bulk cards file")
|
||||
err := cacheBulkCardsFile(db, bulkData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("Cached bulk cards file")
|
||||
|
||||
log.Println("Reading cached file")
|
||||
cardsBytes, err := ioutil.ReadFile(ALL_CARDS_CACHE_FILENAME)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("Read cached file")
|
||||
|
||||
log.Println("Unmarshaling JSON")
|
||||
var cards []scryfall.Card
|
||||
err = json.Unmarshal(cardsBytes, &cards)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println("Unmarshaled JSON")
|
||||
|
||||
log.Println("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
|
||||
}
|
||||
|
||||
cardPrintings := getCardPrintings(card)
|
||||
|
||||
for _, cardPrinting := range cardPrintings {
|
||||
err := database.InsertCardPrinting(db, cardPrinting)
|
||||
// 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
|
||||
}
|
||||
}
|
||||
log.Println("INSERTed cards")
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"sevenkeys/config"
|
||||
|
@ -11,11 +13,13 @@ import (
|
|||
"sevenkeys/delverlens"
|
||||
"sevenkeys/logic"
|
||||
"sevenkeys/update"
|
||||
|
||||
"github.com/mtgban/go-mtgban/cardtrader"
|
||||
)
|
||||
|
||||
const (
|
||||
UpdateSubcommand string = "update"
|
||||
CreateStorageAreaSubcommand string = "create-storage"
|
||||
CreateStorageAreaSubcommand string = "createstorage"
|
||||
StoreSubcommand string = "store"
|
||||
ImportSubcommand string = "import"
|
||||
SearchPrintingsSubcommand string = "search-printings"
|
||||
|
@ -23,6 +27,9 @@ const (
|
|||
AddSubcommand string = "add"
|
||||
RemoveSubcommand string = "remove"
|
||||
ReplaceSubcommand string = "replace"
|
||||
DeckSubcommand string = "deck"
|
||||
|
||||
GetProductIdSubcommand string = "products"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -41,8 +48,8 @@ func main() {
|
|||
|
||||
switch flag.Args()[0] {
|
||||
case UpdateSubcommand:
|
||||
err := update.UpdateCardtraderData(db, config.CardtraderToken)
|
||||
logic.Check(err)
|
||||
update.UpdateScryfallData(db)
|
||||
//update.UpdateCardtraderData(config.CardtraderToken)
|
||||
break
|
||||
case CreateStorageAreaSubcommand:
|
||||
createStorageCmd := flag.NewFlagSet(CreateStorageAreaSubcommand, flag.ExitOnError)
|
||||
|
@ -81,10 +88,9 @@ func main() {
|
|||
|
||||
fmt.Printf("%d\n", cardLocationId)
|
||||
break
|
||||
|
||||
case ImportSubcommand:
|
||||
importCmd := flag.NewFlagSet(ImportSubcommand, flag.ExitOnError)
|
||||
storageArea := importCmd.String("storage-area", "",
|
||||
storageArea := importCmd.String("storagearea", "",
|
||||
"The name of the StorageArea where cards should be imported.")
|
||||
|
||||
importCmd.Parse(flag.Args()[1:])
|
||||
|
@ -96,22 +102,18 @@ func main() {
|
|||
}
|
||||
logic.Check(err)
|
||||
|
||||
filename := importCmd.Args()[0]
|
||||
delverLensCards, err := delverlens.ParseExportFile(filename)
|
||||
delverLensCards, err := delverlens.ParseExportFile(importCmd.Args()[0])
|
||||
logic.Check(err)
|
||||
|
||||
err = logic.ImportDelverLensCards(db, delverLensCards, storageAreaId)
|
||||
logic.Check(err)
|
||||
break
|
||||
|
||||
/*
|
||||
TODO: Rewrite this to search Blueprints
|
||||
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.")
|
||||
foil := searchPrintingsCmd.String("foil", "E", "Whether the card we're searching for is foil.")
|
||||
|
||||
searchPrintingsCmd.Parse(flag.Args()[1:])
|
||||
|
||||
|
@ -136,7 +138,6 @@ func main() {
|
|||
logic.Check(err)
|
||||
fmt.Println(id)
|
||||
break
|
||||
*/
|
||||
case SearchStorageSubcommand:
|
||||
searchOptions, err := logic.GetAllStorageSearchOptions(db)
|
||||
logic.Check(err)
|
||||
|
@ -202,6 +203,51 @@ func main() {
|
|||
err := logic.Replace(db, *cardLocationId, *cardPrintingId)
|
||||
logic.Check(err)
|
||||
break
|
||||
case DeckSubcommand:
|
||||
deckCmd := flag.NewFlagSet(DeckSubcommand, flag.ExitOnError)
|
||||
deckCmd.Parse(flag.Args()[1:])
|
||||
|
||||
//filename := deckCmd.Args()[0]
|
||||
break
|
||||
case GetProductIdSubcommand:
|
||||
blbBlueprintsBytes, err := ioutil.ReadFile("blb_blueprints.json")
|
||||
logic.Check(err)
|
||||
|
||||
var blbBlueprints []cardtrader.Blueprint
|
||||
err = json.Unmarshal(blbBlueprintsBytes, &blbBlueprints)
|
||||
logic.Check(err)
|
||||
|
||||
productsBytes, err := ioutil.ReadFile("products.json")
|
||||
logic.Check(err)
|
||||
|
||||
var products []cardtrader.Product
|
||||
err = json.Unmarshal(productsBytes, &products)
|
||||
logic.Check(err)
|
||||
|
||||
for _, product := range products {
|
||||
var productBlueprint cardtrader.Blueprint
|
||||
for _, blueprint := range blbBlueprints {
|
||||
if blueprint.Id == product.BlueprintId {
|
||||
productBlueprint = blueprint
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("%s %s %d ",
|
||||
productBlueprint.Name,
|
||||
product.Properties.Number,
|
||||
product.Id,
|
||||
)
|
||||
|
||||
if product.Properties.MTGFoil {
|
||||
fmt.Printf("FOIL ")
|
||||
} else {
|
||||
fmt.Printf("NONFOIL ")
|
||||
}
|
||||
|
||||
fmt.Printf("x%d\n", product.Quantity)
|
||||
}
|
||||
break
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Unrecognized subcommand: %s\n", os.Args[1])
|
||||
break
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
package update
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"sevenkeys/database"
|
||||
|
||||
"github.com/mtgban/go-mtgban/cardtrader"
|
||||
)
|
||||
|
||||
func UpdateCardtraderData(db *sql.DB, token string) error {
|
||||
client := cardtrader.NewCTAuthClient(token)
|
||||
|
||||
expansions, err := client.Expansions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, expansion := range expansions {
|
||||
if expansion.GameId != cardtrader.GameIdMagic {
|
||||
continue
|
||||
}
|
||||
|
||||
err = database.InsertCardtraderExpansion(db, expansion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blueprints, err := client.Blueprints(expansion.Id)
|
||||
if err != nil {
|
||||
log.Printf("cardtrader: Error getting blueprints for Expansion ID %d: %v\n", expansion.Id, err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, blueprint := range blueprints {
|
||||
err = database.InsertCardtraderBlueprint(db, blueprint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package update
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"sevenkeys/logic"
|
||||
"sevenkeys/logic/scryfall"
|
||||
)
|
||||
|
||||
func UpdateScryfallData(db *sql.DB) {
|
||||
fmt.Println("Checking for updates...")
|
||||
bulkData, err := scryfall.GetBulkDataByType(scryfall.BulkDataTypeAllCards)
|
||||
logic.Check(err)
|
||||
|
||||
needsUpdate, err := logic.CheckForUpdates(db, bulkData)
|
||||
logic.Check(err)
|
||||
|
||||
if !needsUpdate {
|
||||
fmt.Println("No update required.")
|
||||
return
|
||||
}
|
||||
|
||||
logic.CreateCacheDirectories()
|
||||
|
||||
err = logic.UpdateSets(db)
|
||||
logic.Check(err)
|
||||
|
||||
err = logic.UpdateCards(db, bulkData)
|
||||
logic.Check(err)
|
||||
|
||||
fmt.Println("Update finished.")
|
||||
}
|
Loading…
Reference in New Issue