Compare commits
23 Commits
827a71f6d3
...
99ba02ef05
Author | SHA1 | Date |
---|---|---|
|
99ba02ef05 | |
|
eed44deddc | |
|
556ef2dd78 | |
|
0a52dde4f6 | |
|
f0758a2cce | |
|
9076edb6f6 | |
|
540a1df24e | |
|
50e8f0c692 | |
|
de1b7e15b2 | |
|
94b8dfb6b9 | |
|
f32f64b6b3 | |
|
c2380b51cb | |
|
5021ac3835 | |
|
9a1f306aec | |
|
fd078f09a5 | |
|
d107a92b54 | |
|
ec6e6b7f6a | |
|
6814b23018 | |
|
8c683002be | |
|
96b9e0ad71 | |
|
ceceb4722f | |
|
848754b509 | |
|
3e5f0082f9 |
|
@ -1,17 +1,24 @@
|
||||||
|
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_create:
|
dev_up:
|
||||||
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_development?parseTime=true&multiStatements=true" up
|
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_dev?parseTime=true&multiStatements=true" up
|
||||||
dev_rollback:
|
dev_down:
|
||||||
|
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_development?parseTime=true&multiStatements=true" reset
|
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_dev?parseTime=true&multiStatements=true" reset
|
||||||
|
|
||||||
prod_create:
|
prod_up:
|
||||||
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys?parseTime=true&multiStatements=true" up
|
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_prod?parseTime=true&multiStatements=true" up
|
||||||
prod_rollback:
|
prod_down:
|
||||||
|
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?parseTime=true&multiStatements=true" reset
|
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_prod?parseTime=true&multiStatements=true" reset
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
|
@ -1,37 +0,0 @@
|
||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const CacheTypeAllCardsBulkData = "AllCardsBulkData"
|
|
||||||
|
|
||||||
func GetCacheTimestampByType(db *sql.DB, cacheType string) (string, error) {
|
|
||||||
var timestamp string
|
|
||||||
|
|
||||||
query := "SELECT Stamp FROM CacheTimestamp WHERE CacheType = ?;"
|
|
||||||
err := db.QueryRow(query, cacheType).Scan(×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
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -1,12 +0,0 @@
|
||||||
-- +goose Up
|
|
||||||
-- +goose StatementBegin
|
|
||||||
CREATE TABLE IF NOT EXISTS CacheTimestamp (
|
|
||||||
CacheType ENUM('AllCardsBulkData') PRIMARY KEY,
|
|
||||||
Stamp DATETIME NOT NULL
|
|
||||||
);
|
|
||||||
-- +goose StatementEnd
|
|
||||||
|
|
||||||
-- +goose Down
|
|
||||||
-- +goose StatementBegin
|
|
||||||
DROP TABLE IF EXISTS CacheTimestamp;
|
|
||||||
-- +goose StatementEnd
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
-- +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
|
|
@ -1,14 +0,0 @@
|
||||||
-- +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
|
|
|
@ -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')
|
StorageType ENUM('Binder', 'Box') NOT NULL
|
||||||
);
|
);
|
||||||
-- +goose StatementEnd
|
-- +goose StatementEnd
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
-- +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
|
|
|
@ -1,9 +1,9 @@
|
||||||
-- +goose Up
|
-- +goose Up
|
||||||
-- +goose StatementBegin
|
-- +goose StatementBegin
|
||||||
CREATE TABLE IF NOT EXISTS CardLocation (
|
CREATE TABLE IF NOT EXISTS ProductLocation (
|
||||||
Id INT AUTO_INCREMENT PRIMARY KEY,
|
Id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
CardPrintingId VARCHAR(37) NULL,
|
CardtraderBlueprintId INT NULL,
|
||||||
FOREIGN KEY (CardPrintingId) REFERENCES CardPrinting(Id),
|
FOREIGN KEY (CardtraderBlueprintId) REFERENCES CardtraderBlueprint(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 CardLocation (
|
||||||
|
|
||||||
-- +goose Down
|
-- +goose Down
|
||||||
-- +goose StatementBegin
|
-- +goose StatementBegin
|
||||||
DROP TABLE IF EXISTS CardLocation;
|
DROP TABLE IF EXISTS ProductLocation;
|
||||||
-- +goose StatementEnd
|
-- +goose StatementEnd
|
|
@ -0,0 +1,33 @@
|
||||||
|
-- +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
|
|
@ -1,39 +0,0 @@
|
||||||
-- +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
|
|
|
@ -1,43 +0,0 @@
|
||||||
-- +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
|
|
|
@ -1,23 +0,0 @@
|
||||||
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("/home/viciouscirce/dox/sevenkeys_imports/" + filename)
|
file, err := os.Open(filename)
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cards, err
|
return cards, err
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,426 +0,0 @@
|
||||||
package logic
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sevenkeys/database"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsTrue_IfSetCodeDoesNotMatch(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: false,
|
|
||||||
IsPromo: false,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "otj",
|
|
||||||
Foil: False,
|
|
||||||
Promo: False,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != true {
|
|
||||||
t.Errorf("filter was false")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsFalse_IfSetCodeDoesMatch(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: false,
|
|
||||||
IsPromo: false,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
Foil: False,
|
|
||||||
Promo: False,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != false {
|
|
||||||
t.Errorf("filter was true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsFalse_IfSetCodeNotSet(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: false,
|
|
||||||
IsPromo: false,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "",
|
|
||||||
Foil: False,
|
|
||||||
Promo: False,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != false {
|
|
||||||
t.Errorf("filter was true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsTrue_IfFoilCardInNonFoilSearch(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: true,
|
|
||||||
IsPromo: false,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
Foil: False,
|
|
||||||
Promo: False,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != true {
|
|
||||||
t.Errorf("filter was false")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsTrue_IfNonFoilCardInFoilSearch(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: false,
|
|
||||||
IsPromo: false,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
Foil: True,
|
|
||||||
Promo: False,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != true {
|
|
||||||
t.Errorf("filter was false")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsFalse_IfNonFoilCardInNonFoilSearch(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: false,
|
|
||||||
IsPromo: false,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
Foil: False,
|
|
||||||
Promo: False,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != false {
|
|
||||||
t.Errorf("filter was true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsFalse_IfFoilCardInFoilSearch(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: true,
|
|
||||||
IsPromo: false,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
Foil: True,
|
|
||||||
Promo: False,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != false {
|
|
||||||
t.Errorf("filter was true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsFalse_IfFoilCardInEitherFoilSearch(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: true,
|
|
||||||
IsPromo: false,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
Foil: Either,
|
|
||||||
Promo: False,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != false {
|
|
||||||
t.Errorf("filter was true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsFalse_IfNonFoilCardInEitherFoilSearch(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: false,
|
|
||||||
IsPromo: false,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
Foil: Either,
|
|
||||||
Promo: False,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != false {
|
|
||||||
t.Errorf("filter was true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsTrue_IfPromoCardInNonPromoSearch(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: false,
|
|
||||||
IsPromo: true,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
Foil: False,
|
|
||||||
Promo: False,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != true {
|
|
||||||
t.Errorf("filter was false")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsTrue_IfNonPromoCardInPromoSearch(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: false,
|
|
||||||
IsPromo: false,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
Foil: False,
|
|
||||||
Promo: True,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != true {
|
|
||||||
t.Errorf("filter was false")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsFalse_IfNonPromoCardInNonPromoSearch(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: false,
|
|
||||||
IsPromo: false,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
Foil: False,
|
|
||||||
Promo: False,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != false {
|
|
||||||
t.Errorf("filter was true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsFalse_IfPromoCardInPromoSearch(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: false,
|
|
||||||
IsPromo: true,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
Foil: False,
|
|
||||||
Promo: True,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != false {
|
|
||||||
t.Errorf("filter was true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsFalse_IfPromoCardInEitherPromoSearch(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: false,
|
|
||||||
IsPromo: true,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
Foil: False,
|
|
||||||
Promo: Either,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != false {
|
|
||||||
t.Errorf("filter was true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsFalse_IfNonPromoCardInEitherPromoSearch(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: false,
|
|
||||||
IsPromo: false,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
Foil: False,
|
|
||||||
Promo: Either,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != false {
|
|
||||||
t.Errorf("filter was true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsTrue_IfLanguageDoesNotMatch(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: false,
|
|
||||||
IsPromo: false,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
Foil: False,
|
|
||||||
Promo: False,
|
|
||||||
Language: "de",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != true {
|
|
||||||
t.Errorf("filter was false")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsFalse_IfLanguageDoesMatch(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: false,
|
|
||||||
IsPromo: false,
|
|
||||||
Language: "de",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
Foil: False,
|
|
||||||
Promo: False,
|
|
||||||
Language: "de",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != false {
|
|
||||||
t.Errorf("filter was true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsFalse_IfLanguageNotSet(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
IsFoil: false,
|
|
||||||
IsPromo: false,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
Foil: False,
|
|
||||||
Promo: False,
|
|
||||||
Language: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != false {
|
|
||||||
t.Errorf("filter was true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFilterPrinting_ReturnsTrue_IfSetCodeMatchesButCollectorNumberDoesnt(t *testing.T) {
|
|
||||||
printing := database.CardPrinting{
|
|
||||||
SetCode: "rtr",
|
|
||||||
CollectorNumber: "301",
|
|
||||||
IsFoil: false,
|
|
||||||
IsPromo: false,
|
|
||||||
Language: "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
searchCriteria := SearchCriteria{
|
|
||||||
SetCode: "rtr",
|
|
||||||
CollectorNumber: "299",
|
|
||||||
Foil: False,
|
|
||||||
Promo: False,
|
|
||||||
Language: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := filterPrinting(printing, searchCriteria)
|
|
||||||
|
|
||||||
if filter != true {
|
|
||||||
t.Errorf("filter was false")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -41,33 +41,6 @@ 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
|
||||||
|
|
||||||
|
@ -77,9 +50,6 @@ 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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
package scryfall
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type BulkData struct {
|
|
||||||
Id string `json:"id"`
|
|
||||||
Uri string `json:"uri"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
DownloadUri string `json:"download_uri"`
|
|
||||||
UpdatedAt string `json:"updated_at"`
|
|
||||||
UpdatedAtTime time.Time `json:"ignore"`
|
|
||||||
Size int `json:"size"`
|
|
||||||
ContentType string `json:"content_type"`
|
|
||||||
ContentEncoding string `json:"content_encoding"`
|
|
||||||
}
|
|
||||||
|
|
||||||
const BULK_DATA_URI = "https://api.scryfall.com/bulk-data"
|
|
||||||
|
|
||||||
const BulkDataTypeOracleCards string = "oracle_cards"
|
|
||||||
const BulkDataTypeUniqueArtwork string = "unique_artwork"
|
|
||||||
const BulkDataTypeDefaultCards string = "default_cards"
|
|
||||||
const BulkDataTypeAllCards string = "all_cards"
|
|
||||||
const BulkDataTypeRulings string = "rulings"
|
|
||||||
|
|
||||||
func GetBulkDataByType(bulkDataType string) (BulkData, error) {
|
|
||||||
response, err := http.Get(BULK_DATA_URI + "/" + bulkDataType)
|
|
||||||
if err != nil {
|
|
||||||
return BulkData{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.StatusCode != http.StatusOK {
|
|
||||||
return BulkData{}, errors.New("HTTP request failed with code: " + string(response.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
defer response.Body.Close()
|
|
||||||
bulkDataBytes, err := io.ReadAll(response.Body)
|
|
||||||
if err != nil {
|
|
||||||
return BulkData{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var bulkData BulkData
|
|
||||||
err = json.Unmarshal(bulkDataBytes, &bulkData)
|
|
||||||
if err != nil {
|
|
||||||
return BulkData{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
bulkData.UpdatedAtTime, err = time.Parse(ScryfallTimestampFormat, bulkData.UpdatedAt)
|
|
||||||
if err != nil {
|
|
||||||
return BulkData{}, err
|
|
||||||
}
|
|
||||||
// Round to the nearest second; this is so that comparison with the timestamp stored in the database works as intended
|
|
||||||
bulkData.UpdatedAtTime = bulkData.UpdatedAtTime.Truncate(time.Second)
|
|
||||||
|
|
||||||
return bulkData, nil
|
|
||||||
}
|
|
|
@ -1,5 +1,16 @@
|
||||||
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"`
|
||||||
|
@ -12,3 +23,38 @@ 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
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -1,49 +0,0 @@
|
||||||
package scryfall
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SetList struct {
|
|
||||||
Object string `json:"object"`
|
|
||||||
HasMore bool `json:"has_more"`
|
|
||||||
Data []Set `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Set struct {
|
|
||||||
Code string `json:"code"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
CardCount int `json:"card_count"`
|
|
||||||
IconSvgUri string `json:"icon_svg_uri"`
|
|
||||||
Digital bool `json:"digital"`
|
|
||||||
}
|
|
||||||
|
|
||||||
const SETS_API_URL string = "https://api.scryfall.com/sets"
|
|
||||||
|
|
||||||
func GetAllSets() ([]Set, error) {
|
|
||||||
response, err := http.Get(SETS_API_URL)
|
|
||||||
if err != nil {
|
|
||||||
return []Set{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.StatusCode != http.StatusOK {
|
|
||||||
return []Set{}, errors.New("HTTP request failed with code: " + string(response.StatusCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
defer response.Body.Close()
|
|
||||||
setsBytes, err := io.ReadAll(response.Body)
|
|
||||||
if err != nil {
|
|
||||||
return []Set{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var setList SetList
|
|
||||||
err = json.Unmarshal(setsBytes, &setList)
|
|
||||||
if err != nil {
|
|
||||||
return []Set{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return setList.Data, nil
|
|
||||||
}
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
|
||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +0,0 @@
|
||||||
package scryfall
|
|
||||||
|
|
||||||
const ScryfallTimestampFormat = "2006-01-02T15:04:05.999-07:00"
|
|
|
@ -2,7 +2,6 @@ package logic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
@ -21,45 +20,12 @@ 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) {
|
||||||
|
|
|
@ -1,197 +0,0 @@
|
||||||
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,11 +1,9 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"sevenkeys/config"
|
"sevenkeys/config"
|
||||||
|
@ -13,13 +11,11 @@ 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 = "createstorage"
|
CreateStorageAreaSubcommand string = "create-storage"
|
||||||
StoreSubcommand string = "store"
|
StoreSubcommand string = "store"
|
||||||
ImportSubcommand string = "import"
|
ImportSubcommand string = "import"
|
||||||
SearchPrintingsSubcommand string = "search-printings"
|
SearchPrintingsSubcommand string = "search-printings"
|
||||||
|
@ -27,9 +23,6 @@ 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() {
|
||||||
|
@ -48,8 +41,8 @@ func main() {
|
||||||
|
|
||||||
switch flag.Args()[0] {
|
switch flag.Args()[0] {
|
||||||
case UpdateSubcommand:
|
case UpdateSubcommand:
|
||||||
update.UpdateScryfallData(db)
|
err := update.UpdateCardtraderData(db, config.CardtraderToken)
|
||||||
//update.UpdateCardtraderData(config.CardtraderToken)
|
logic.Check(err)
|
||||||
break
|
break
|
||||||
case CreateStorageAreaSubcommand:
|
case CreateStorageAreaSubcommand:
|
||||||
createStorageCmd := flag.NewFlagSet(CreateStorageAreaSubcommand, flag.ExitOnError)
|
createStorageCmd := flag.NewFlagSet(CreateStorageAreaSubcommand, flag.ExitOnError)
|
||||||
|
@ -88,9 +81,10 @@ 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("storagearea", "",
|
storageArea := importCmd.String("storage-area", "",
|
||||||
"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:])
|
||||||
|
@ -102,18 +96,22 @@ func main() {
|
||||||
}
|
}
|
||||||
logic.Check(err)
|
logic.Check(err)
|
||||||
|
|
||||||
delverLensCards, err := delverlens.ParseExportFile(importCmd.Args()[0])
|
filename := 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
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO: Rewrite this to search Blueprints
|
||||||
case SearchPrintingsSubcommand:
|
case SearchPrintingsSubcommand:
|
||||||
searchPrintingsCmd := flag.NewFlagSet(SearchPrintingsSubcommand, flag.ExitOnError)
|
searchPrintingsCmd := flag.NewFlagSet(SearchPrintingsSubcommand, flag.ExitOnError)
|
||||||
|
|
||||||
setCode := searchPrintingsCmd.String("set-code", "", "The code for the set the card we're searching for belongs to.")
|
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.")
|
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:])
|
searchPrintingsCmd.Parse(flag.Args()[1:])
|
||||||
|
|
||||||
|
@ -138,6 +136,7 @@ func main() {
|
||||||
logic.Check(err)
|
logic.Check(err)
|
||||||
fmt.Println(id)
|
fmt.Println(id)
|
||||||
break
|
break
|
||||||
|
*/
|
||||||
case SearchStorageSubcommand:
|
case SearchStorageSubcommand:
|
||||||
searchOptions, err := logic.GetAllStorageSearchOptions(db)
|
searchOptions, err := logic.GetAllStorageSearchOptions(db)
|
||||||
logic.Check(err)
|
logic.Check(err)
|
||||||
|
@ -203,51 +202,6 @@ 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
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
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
|
||||||
|
}
|
|
@ -1,32 +0,0 @@
|
||||||
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