Compare commits

..

No commits in common. "99ba02ef059ea8e5d041e5176cddbf6ca39af2be" and "827a71f6d3cc5c546c8e3278258f44d7ab9c40b4" have entirely different histories.

34 changed files with 1240 additions and 551 deletions

View File

@ -1,24 +1,17 @@
build:
go build
connect:
mysql --user=root --password=$(shell pass show sevenkeys/mysql)
dump:
mysqldump --user=root --password=$(shell pass show sevenkeys/mysql) sevenkeys >sevenkeys.sql
dev_up:
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_dev?parseTime=true&multiStatements=true" up
dev_down:
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_dev?parseTime=true&multiStatements=true" down
dev_reset:
dev_create:
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_development?parseTime=true&multiStatements=true" up
dev_rollback:
rm -rf cache/
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_dev?parseTime=true&multiStatements=true" reset
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_development?parseTime=true&multiStatements=true" reset
prod_up:
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_prod?parseTime=true&multiStatements=true" up
prod_down:
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_prod?parseTime=true&multiStatements=true" down
prod_reset:
prod_create:
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys?parseTime=true&multiStatements=true" up
prod_rollback:
rm -rf cache/
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys_prod?parseTime=true&multiStatements=true" reset
goose -dir database/migrations/ mysql "root:$(shell pass show sevenkeys/mysql)@/sevenkeys?parseTime=true&multiStatements=true" reset

View File

@ -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"
}

View File

@ -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(&timestamp)
return timestamp, err
}
func InsertOrUpdateCacheTimestampByType(db *sql.DB, cacheType string, stamp time.Time) error {
query := `INSERT INTO CacheTimestamp (CacheType, Stamp)
VALUES (?, ?)
ON DUPLICATE KEY
UPDATE Stamp = ?;`
insertOrUpdate, err := db.Prepare(query)
defer insertOrUpdate.Close()
if err != nil {
return err
}
_, err = insertOrUpdate.Exec(cacheType, stamp, stamp)
if err != nil {
return err
}
return nil
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

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

View File

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

View File

@ -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

View File

@ -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

View File

@ -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

23
sevenkeys/database/set.go Normal file
View File

@ -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
}

View File

@ -14,7 +14,7 @@ type DelverLensCard struct {
func ParseExportFile(filename string) ([]DelverLensCard, error) {
var cards []DelverLensCard
file, err := os.Open(filename)
file, err := os.Open("/home/viciouscirce/dox/sevenkeys_imports/" + filename)
defer file.Close()
if err != nil {
return cards, err

22
sevenkeys/logic/cache.go Normal file
View File

@ -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
}

36
sevenkeys/logic/filter.go Normal file
View File

@ -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
}

View File

@ -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")
}
}

View File

@ -41,6 +41,33 @@ func GetCardAtLocation(db *sql.DB, cardLocationId int) (database.LocateCardResul
return result, nil
}
func LocateCards(db *sql.DB, cardNames []string, criteria SearchCriteria) ([]database.LocateCardResult, error) {
results, err := database.GetLocateResults(db, cardNames)
if err != nil {
return results, err
}
var filteredResults []database.LocateCardResult
for _, result := range results {
printing := database.CardPrinting{
SetCode: result.SetCode,
IsFoil: result.IsFoil,
IsPromo: result.IsPromo,
Language: result.Language,
}
filter := filterPrinting(printing, criteria)
if filter {
continue
}
filteredResults = append(filteredResults, result)
}
return filteredResults, nil
}
func GetLocationDescription(location database.LocateCardResult) string {
var description string
@ -50,6 +77,9 @@ func GetLocationDescription(location database.LocateCardResult) string {
location.CollectorNumber,
location.Language)
if location.IsFoil {
description += " FOIL"
}
if location.IsPromo {
description += " PROMO"
}

View File

@ -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
}

View File

@ -1,16 +1,5 @@
package scryfall
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
)
const SCRYFALL_API_CARDS = "/cards/"
const CARD_IMAGEURIS_KEY_PNG = "png"
type Card struct {
Id string `json:"id"`
Name string `json:"name"`
@ -23,38 +12,3 @@ type Card struct {
ImageUris map[string]string `json:"image_uris"`
Language string `json:"lang"`
}
func (c *ScryfallClient) GetCardById(id string) (Card, error) {
var card Card
response, err := c.httpClient.Get(c.baseURL + SCRYFALL_API_CARDS + id)
if err != nil {
return card, err
}
if response.StatusCode != http.StatusOK {
return card, errors.New("HTTP request failed with code: " + fmt.Sprint(response.StatusCode))
}
defer response.Body.Close()
cardBytes, err := io.ReadAll(response.Body)
if err != nil {
return card, err
}
err = json.Unmarshal(cardBytes, &card)
if err != nil {
return card, err
}
return card, nil
}
func (c *ScryfallClient) GetCardImageUrlById(id string) (string, error) {
card, err := c.GetCardById(id)
if err != nil {
return "", err
}
return card.ImageUris[CARD_IMAGEURIS_KEY_PNG], nil
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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"
}
}

View File

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

View File

@ -2,6 +2,7 @@ package logic
import (
"database/sql"
"fmt"
"io"
"os"
"os/exec"
@ -20,12 +21,45 @@ const (
type SearchCriteria struct {
SetCode string
CollectorNumber string
Foil Triadic
Promo Triadic
Language string
}
type CardPrintingSearchOptions map[string]string
func GetAllCardPrintingSearchOptions(db *sql.DB, searchCriteria SearchCriteria) (CardPrintingSearchOptions, error) {
var searchOptions CardPrintingSearchOptions = make(CardPrintingSearchOptions)
cardPrintings, err := database.GetAllCardPrintings(db)
if err != nil {
return searchOptions, err
}
for _, printing := range cardPrintings {
// Filter based on search criteria
filter := filterPrinting(printing, searchCriteria)
if filter {
continue
}
// Construct search option string
searchString := fmt.Sprintf("%s (%s %s) [%s]", printing.Name, printing.SetCode, printing.CollectorNumber, printing.Language)
if printing.IsFoil {
searchString += " FOIL"
}
if printing.IsPromo {
searchString += " PROMO"
}
searchOptions[searchString] = printing.Id
}
return searchOptions, err
}
type StorageSearchOptions map[string]int
func GetAllStorageSearchOptions(db *sql.DB) (StorageSearchOptions, error) {

197
sevenkeys/logic/update.go Normal file
View File

@ -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
}

View File

@ -1,9 +1,11 @@
package main
import (
"encoding/json"
"errors"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"sevenkeys/config"
@ -11,11 +13,13 @@ import (
"sevenkeys/delverlens"
"sevenkeys/logic"
"sevenkeys/update"
"github.com/mtgban/go-mtgban/cardtrader"
)
const (
UpdateSubcommand string = "update"
CreateStorageAreaSubcommand string = "create-storage"
CreateStorageAreaSubcommand string = "createstorage"
StoreSubcommand string = "store"
ImportSubcommand string = "import"
SearchPrintingsSubcommand string = "search-printings"
@ -23,6 +27,9 @@ const (
AddSubcommand string = "add"
RemoveSubcommand string = "remove"
ReplaceSubcommand string = "replace"
DeckSubcommand string = "deck"
GetProductIdSubcommand string = "products"
)
func main() {
@ -41,8 +48,8 @@ func main() {
switch flag.Args()[0] {
case UpdateSubcommand:
err := update.UpdateCardtraderData(db, config.CardtraderToken)
logic.Check(err)
update.UpdateScryfallData(db)
//update.UpdateCardtraderData(config.CardtraderToken)
break
case CreateStorageAreaSubcommand:
createStorageCmd := flag.NewFlagSet(CreateStorageAreaSubcommand, flag.ExitOnError)
@ -81,10 +88,9 @@ func main() {
fmt.Printf("%d\n", cardLocationId)
break
case ImportSubcommand:
importCmd := flag.NewFlagSet(ImportSubcommand, flag.ExitOnError)
storageArea := importCmd.String("storage-area", "",
storageArea := importCmd.String("storagearea", "",
"The name of the StorageArea where cards should be imported.")
importCmd.Parse(flag.Args()[1:])
@ -96,22 +102,18 @@ func main() {
}
logic.Check(err)
filename := importCmd.Args()[0]
delverLensCards, err := delverlens.ParseExportFile(filename)
delverLensCards, err := delverlens.ParseExportFile(importCmd.Args()[0])
logic.Check(err)
err = logic.ImportDelverLensCards(db, delverLensCards, storageAreaId)
logic.Check(err)
break
/*
TODO: Rewrite this to search Blueprints
case SearchPrintingsSubcommand:
searchPrintingsCmd := flag.NewFlagSet(SearchPrintingsSubcommand, flag.ExitOnError)
setCode := searchPrintingsCmd.String("set-code", "", "The code for the set the card we're searching for belongs to.")
collectorNumber := searchPrintingsCmd.String("collector-number", "", "The collector number of the card we're searching for.")
//foil := searchPrintingsCmd.String("foil", "E", "Whether the card we're searching for is foil.")
foil := searchPrintingsCmd.String("foil", "E", "Whether the card we're searching for is foil.")
searchPrintingsCmd.Parse(flag.Args()[1:])
@ -136,7 +138,6 @@ func main() {
logic.Check(err)
fmt.Println(id)
break
*/
case SearchStorageSubcommand:
searchOptions, err := logic.GetAllStorageSearchOptions(db)
logic.Check(err)
@ -202,6 +203,51 @@ func main() {
err := logic.Replace(db, *cardLocationId, *cardPrintingId)
logic.Check(err)
break
case DeckSubcommand:
deckCmd := flag.NewFlagSet(DeckSubcommand, flag.ExitOnError)
deckCmd.Parse(flag.Args()[1:])
//filename := deckCmd.Args()[0]
break
case GetProductIdSubcommand:
blbBlueprintsBytes, err := ioutil.ReadFile("blb_blueprints.json")
logic.Check(err)
var blbBlueprints []cardtrader.Blueprint
err = json.Unmarshal(blbBlueprintsBytes, &blbBlueprints)
logic.Check(err)
productsBytes, err := ioutil.ReadFile("products.json")
logic.Check(err)
var products []cardtrader.Product
err = json.Unmarshal(productsBytes, &products)
logic.Check(err)
for _, product := range products {
var productBlueprint cardtrader.Blueprint
for _, blueprint := range blbBlueprints {
if blueprint.Id == product.BlueprintId {
productBlueprint = blueprint
break
}
}
fmt.Printf("%s %s %d ",
productBlueprint.Name,
product.Properties.Number,
product.Id,
)
if product.Properties.MTGFoil {
fmt.Printf("FOIL ")
} else {
fmt.Printf("NONFOIL ")
}
fmt.Printf("x%d\n", product.Quantity)
}
break
default:
fmt.Fprintf(os.Stderr, "Unrecognized subcommand: %s\n", os.Args[1])
break

View File

@ -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
}

View File

@ -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.")
}