Compare commits
No commits in common. "99ba02ef059ea8e5d041e5176cddbf6ca39af2be" and "827a71f6d3cc5c546c8e3278258f44d7ab9c40b4" have entirely different histories.
99ba02ef05
...
827a71f6d3
|
@ -1,24 +1,17 @@
|
||||||
build:
|
|
||||||
go build
|
|
||||||
|
|
||||||
connect:
|
connect:
|
||||||
mysql --user=root --password=$(shell pass show sevenkeys/mysql)
|
mysql --user=root --password=$(shell pass show sevenkeys/mysql)
|
||||||
|
|
||||||
dump:
|
dump:
|
||||||
mysqldump --user=root --password=$(shell pass show sevenkeys/mysql) sevenkeys >sevenkeys.sql
|
mysqldump --user=root --password=$(shell pass show sevenkeys/mysql) sevenkeys >sevenkeys.sql
|
||||||
|
|
||||||
dev_up:
|
dev_create:
|
||||||
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_dev?parseTime=true&multiStatements=true" up
|
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_development?parseTime=true&multiStatements=true" up
|
||||||
dev_down:
|
dev_rollback:
|
||||||
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_dev?parseTime=true&multiStatements=true" down
|
|
||||||
dev_reset:
|
|
||||||
rm -rf cache/
|
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:
|
prod_create:
|
||||||
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_prod?parseTime=true&multiStatements=true" up
|
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys?parseTime=true&multiStatements=true" up
|
||||||
prod_down:
|
prod_rollback:
|
||||||
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_prod?parseTime=true&multiStatements=true" down
|
|
||||||
prod_reset:
|
|
||||||
rm -rf cache/
|
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 (
|
CREATE TABLE IF NOT EXISTS StorageArea (
|
||||||
Id INT AUTO_INCREMENT PRIMARY KEY,
|
Id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
Name VARCHAR(100) NOT NULL,
|
Name VARCHAR(100) NOT NULL,
|
||||||
StorageType ENUM('Binder', 'Box') NOT NULL
|
StorageType ENUM('Binder', 'Box')
|
||||||
);
|
);
|
||||||
-- +goose StatementEnd
|
-- +goose StatementEnd
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
-- +goose Up
|
-- +goose Up
|
||||||
-- +goose StatementBegin
|
-- +goose StatementBegin
|
||||||
CREATE TABLE IF NOT EXISTS ProductLocation (
|
CREATE TABLE IF NOT EXISTS CardLocation (
|
||||||
Id INT AUTO_INCREMENT PRIMARY KEY,
|
Id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
CardtraderBlueprintId INT NULL,
|
CardPrintingId VARCHAR(37) NULL,
|
||||||
FOREIGN KEY (CardtraderBlueprintId) REFERENCES CardtraderBlueprint(Id),
|
FOREIGN KEY (CardPrintingId) REFERENCES CardPrinting(Id),
|
||||||
StorageAreaId INT NOT NULL,
|
StorageAreaId INT NOT NULL,
|
||||||
FOREIGN KEY (StorageAreaId) REFERENCES StorageArea(Id),
|
FOREIGN KEY (StorageAreaId) REFERENCES StorageArea(Id),
|
||||||
Position INT NULL,
|
Position INT NULL,
|
||||||
|
@ -13,5 +13,5 @@ CREATE TABLE IF NOT EXISTS ProductLocation (
|
||||||
|
|
||||||
-- +goose Down
|
-- +goose Down
|
||||||
-- +goose StatementBegin
|
-- +goose StatementBegin
|
||||||
DROP TABLE IF EXISTS ProductLocation;
|
DROP TABLE IF EXISTS CardLocation;
|
||||||
-- +goose StatementEnd
|
-- +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) {
|
func ParseExportFile(filename string) ([]DelverLensCard, error) {
|
||||||
var cards []DelverLensCard
|
var cards []DelverLensCard
|
||||||
|
|
||||||
file, err := os.Open(filename)
|
file, err := os.Open("/home/viciouscirce/dox/sevenkeys_imports/" + filename)
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cards, err
|
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
|
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 {
|
func GetLocationDescription(location database.LocateCardResult) string {
|
||||||
var description string
|
var description string
|
||||||
|
|
||||||
|
@ -50,6 +77,9 @@ func GetLocationDescription(location database.LocateCardResult) string {
|
||||||
location.CollectorNumber,
|
location.CollectorNumber,
|
||||||
location.Language)
|
location.Language)
|
||||||
|
|
||||||
|
if location.IsFoil {
|
||||||
|
description += " FOIL"
|
||||||
|
}
|
||||||
if location.IsPromo {
|
if location.IsPromo {
|
||||||
description += " PROMO"
|
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
|
package scryfall
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
const SCRYFALL_API_CARDS = "/cards/"
|
|
||||||
const CARD_IMAGEURIS_KEY_PNG = "png"
|
|
||||||
|
|
||||||
type Card struct {
|
type Card struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
@ -23,38 +12,3 @@ type Card struct {
|
||||||
ImageUris map[string]string `json:"image_uris"`
|
ImageUris map[string]string `json:"image_uris"`
|
||||||
Language string `json:"lang"`
|
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 (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
@ -20,12 +21,45 @@ const (
|
||||||
type SearchCriteria struct {
|
type SearchCriteria struct {
|
||||||
SetCode string
|
SetCode string
|
||||||
CollectorNumber string
|
CollectorNumber string
|
||||||
|
Foil Triadic
|
||||||
Promo Triadic
|
Promo Triadic
|
||||||
Language string
|
Language string
|
||||||
}
|
}
|
||||||
|
|
||||||
type CardPrintingSearchOptions map[string]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
|
type StorageSearchOptions map[string]int
|
||||||
|
|
||||||
func GetAllStorageSearchOptions(db *sql.DB) (StorageSearchOptions, error) {
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"sevenkeys/config"
|
"sevenkeys/config"
|
||||||
|
@ -11,11 +13,13 @@ import (
|
||||||
"sevenkeys/delverlens"
|
"sevenkeys/delverlens"
|
||||||
"sevenkeys/logic"
|
"sevenkeys/logic"
|
||||||
"sevenkeys/update"
|
"sevenkeys/update"
|
||||||
|
|
||||||
|
"github.com/mtgban/go-mtgban/cardtrader"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
UpdateSubcommand string = "update"
|
UpdateSubcommand string = "update"
|
||||||
CreateStorageAreaSubcommand string = "create-storage"
|
CreateStorageAreaSubcommand string = "createstorage"
|
||||||
StoreSubcommand string = "store"
|
StoreSubcommand string = "store"
|
||||||
ImportSubcommand string = "import"
|
ImportSubcommand string = "import"
|
||||||
SearchPrintingsSubcommand string = "search-printings"
|
SearchPrintingsSubcommand string = "search-printings"
|
||||||
|
@ -23,6 +27,9 @@ const (
|
||||||
AddSubcommand string = "add"
|
AddSubcommand string = "add"
|
||||||
RemoveSubcommand string = "remove"
|
RemoveSubcommand string = "remove"
|
||||||
ReplaceSubcommand string = "replace"
|
ReplaceSubcommand string = "replace"
|
||||||
|
DeckSubcommand string = "deck"
|
||||||
|
|
||||||
|
GetProductIdSubcommand string = "products"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -41,8 +48,8 @@ func main() {
|
||||||
|
|
||||||
switch flag.Args()[0] {
|
switch flag.Args()[0] {
|
||||||
case UpdateSubcommand:
|
case UpdateSubcommand:
|
||||||
err := update.UpdateCardtraderData(db, config.CardtraderToken)
|
update.UpdateScryfallData(db)
|
||||||
logic.Check(err)
|
//update.UpdateCardtraderData(config.CardtraderToken)
|
||||||
break
|
break
|
||||||
case CreateStorageAreaSubcommand:
|
case CreateStorageAreaSubcommand:
|
||||||
createStorageCmd := flag.NewFlagSet(CreateStorageAreaSubcommand, flag.ExitOnError)
|
createStorageCmd := flag.NewFlagSet(CreateStorageAreaSubcommand, flag.ExitOnError)
|
||||||
|
@ -81,10 +88,9 @@ func main() {
|
||||||
|
|
||||||
fmt.Printf("%d\n", cardLocationId)
|
fmt.Printf("%d\n", cardLocationId)
|
||||||
break
|
break
|
||||||
|
|
||||||
case ImportSubcommand:
|
case ImportSubcommand:
|
||||||
importCmd := flag.NewFlagSet(ImportSubcommand, flag.ExitOnError)
|
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.")
|
"The name of the StorageArea where cards should be imported.")
|
||||||
|
|
||||||
importCmd.Parse(flag.Args()[1:])
|
importCmd.Parse(flag.Args()[1:])
|
||||||
|
@ -96,47 +102,42 @@ func main() {
|
||||||
}
|
}
|
||||||
logic.Check(err)
|
logic.Check(err)
|
||||||
|
|
||||||
filename := importCmd.Args()[0]
|
delverLensCards, err := delverlens.ParseExportFile(importCmd.Args()[0])
|
||||||
delverLensCards, err := delverlens.ParseExportFile(filename)
|
|
||||||
logic.Check(err)
|
logic.Check(err)
|
||||||
|
|
||||||
err = logic.ImportDelverLensCards(db, delverLensCards, storageAreaId)
|
err = logic.ImportDelverLensCards(db, delverLensCards, storageAreaId)
|
||||||
logic.Check(err)
|
logic.Check(err)
|
||||||
break
|
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.")
|
||||||
TODO: Rewrite this to search Blueprints
|
collectorNumber := searchPrintingsCmd.String("collector-number", "", "The collector number of the card we're searching for.")
|
||||||
case SearchPrintingsSubcommand:
|
foil := searchPrintingsCmd.String("foil", "E", "Whether the card we're searching for is foil.")
|
||||||
searchPrintingsCmd := flag.NewFlagSet(SearchPrintingsSubcommand, flag.ExitOnError)
|
|
||||||
|
|
||||||
setCode := searchPrintingsCmd.String("set-code", "", "The code for the set the card we're searching for belongs to.")
|
searchPrintingsCmd.Parse(flag.Args()[1:])
|
||||||
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",
|
||||||
|
}
|
||||||
|
|
||||||
searchCriteria := logic.SearchCriteria{
|
if *foil == "Y" {
|
||||||
SetCode: *setCode,
|
searchCriteria.Foil = logic.True
|
||||||
CollectorNumber: *collectorNumber,
|
} else if *foil == "N" {
|
||||||
Promo: logic.Either,
|
searchCriteria.Foil = logic.False
|
||||||
Language: "en",
|
} else {
|
||||||
}
|
searchCriteria.Foil = logic.Either
|
||||||
|
}
|
||||||
|
|
||||||
if *foil == "Y" {
|
searchOptions, err := logic.GetAllCardPrintingSearchOptions(db, searchCriteria)
|
||||||
searchCriteria.Foil = logic.True
|
logic.Check(err)
|
||||||
} else if *foil == "N" {
|
id, _, err := logic.GenericSearch(searchOptions)
|
||||||
searchCriteria.Foil = logic.False
|
logic.Check(err)
|
||||||
} else {
|
fmt.Println(id)
|
||||||
searchCriteria.Foil = logic.Either
|
break
|
||||||
}
|
|
||||||
|
|
||||||
searchOptions, err := logic.GetAllCardPrintingSearchOptions(db, searchCriteria)
|
|
||||||
logic.Check(err)
|
|
||||||
id, _, err := logic.GenericSearch(searchOptions)
|
|
||||||
logic.Check(err)
|
|
||||||
fmt.Println(id)
|
|
||||||
break
|
|
||||||
*/
|
|
||||||
case SearchStorageSubcommand:
|
case SearchStorageSubcommand:
|
||||||
searchOptions, err := logic.GetAllStorageSearchOptions(db)
|
searchOptions, err := logic.GetAllStorageSearchOptions(db)
|
||||||
logic.Check(err)
|
logic.Check(err)
|
||||||
|
@ -202,6 +203,51 @@ func main() {
|
||||||
err := logic.Replace(db, *cardLocationId, *cardPrintingId)
|
err := logic.Replace(db, *cardLocationId, *cardPrintingId)
|
||||||
logic.Check(err)
|
logic.Check(err)
|
||||||
break
|
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:
|
default:
|
||||||
fmt.Fprintf(os.Stderr, "Unrecognized subcommand: %s\n", os.Args[1])
|
fmt.Fprintf(os.Stderr, "Unrecognized subcommand: %s\n", os.Args[1])
|
||||||
break
|
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