Compare commits

...

9 Commits

14 changed files with 418 additions and 63 deletions

View File

@ -1,2 +1,8 @@
dbup:
go run cmd/database/main.go
removedb:
mysql --user=root --password=$(shell pass show sevenkeys/mysql) <sql/removedb.sql
createdb:
mysql --user=root --password=$(shell pass show sevenkeys/mysql) <sql/createdb.sql
dbup: createdb
go run cmd/dbup/main.go
importdata: dbup
go run cmd/importdata/main.go

View File

@ -0,0 +1,10 @@
package main
import (
"sevenkeys/database"
)
func main() {
db := database.GetDatabaseFromConfig("config.json")
database.CreateDatabaseSchema(db)
}

View File

@ -0,0 +1,29 @@
package main
import (
"database/sql"
"log"
"sevenkeys/database"
"sevenkeys/database/imports"
"sevenkeys/scryfall"
)
func importSets(db *sql.DB) {
setList, err := scryfall.GetSets()
if err != nil {
log.Fatal(err)
}
err = imports.InsertSets(db, setList)
if err != nil {
log.Fatal(err)
}
}
func main() {
db := database.GetDatabaseFromConfig("config.json")
//imports.InsertColors(db)
//importSets(db)
// Import artists
// Import gamepieces and printings
}

View File

@ -0,0 +1,18 @@
package entities
type ExpansionSet struct {
Id int
SetCode string
Name string
SetType string
ReleasedAt string
BlockCode string
Block string
ParentSetCode string
CardCount int
PrintedSize int
Digital bool
FoilOnly bool
NonfoilOnly bool
IconSvgUri string
}

View File

@ -1,12 +1,11 @@
package main
package database
import (
"database/sql"
"encoding/json"
"log"
"os"
"sevenkeys/database"
"github.com/go-sql-driver/mysql"
)
@ -25,8 +24,17 @@ func readConfigFromFile(filename string) mysql.Config {
return config
}
func main() {
config := readConfigFromFile("config.json")
db := database.GetDatabase(config)
database.CreateDatabaseSchema(db)
func GetDatabaseFromConfig(filename string) *sql.DB {
config := readConfigFromFile(filename)
db, err := sql.Open("mysql", config.FormatDSN())
if err != nil {
log.Fatal(err)
}
if err = db.Ping(); err != nil {
log.Fatal(err)
}
return db
}

View File

@ -0,0 +1,64 @@
package imports
import (
"database/sql"
"log"
)
func checkColorExists(db *sql.DB, abbrev string) bool {
query := "SELECT Id FROM Color WHERE Abbreviation = ?;"
var colorId int
row := db.QueryRow(query, abbrev)
err := row.Scan(&colorId)
if err == sql.ErrNoRows {
return false
} else if err != nil {
log.Fatal(err)
}
return true
}
func insertColor(db *sql.DB, color string, abbrev string) error {
query := `INSERT INTO Color (Name, Abbreviation) VALUES (?, ?);`
insert, err := db.Prepare(query)
defer insert.Close()
if err != nil {
return err
}
result, err := insert.Exec(color, abbrev)
rowsAffected, err := result.RowsAffected()
if err != nil || rowsAffected != 1 {
return err
}
return nil
}
func InsertColors(db *sql.DB) error {
colors := map[string]string{
"White": "W",
"Blue": "U",
"Black": "B",
"Red": "R",
"Green": "G",
}
for name, abbrev := range colors {
if checkColorExists(db, abbrev) {
log.Println("[Color] Skipping " + name + ", already in database")
continue
}
err := insertColor(db, name, abbrev)
if err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,59 @@
package imports
import (
"database/sql"
"log"
"sevenkeys/scryfall"
)
func CheckSetExists(db *sql.DB, setCode string) bool {
query := "SELECT Id FROM ExpansionSet WHERE SetCode = ?;"
var setId int
row := db.QueryRow(query, setCode)
err := row.Scan(&setId)
if err == sql.ErrNoRows {
return false
} else if err != nil {
log.Fatal(err)
}
return true
}
func insertSet(db *sql.DB, set scryfall.Set) error {
query := "INSERT INTO ExpansionSet (SetCode, Name, SetType, ReleasedAt, BlockCode, Block, ParentSetCode, CardCount, PrintedSize, Digital, FoilOnly, NonfoilOnly, IconSvgUri) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"
insert, err := db.Prepare(query)
defer insert.Close()
if err != nil {
return err
}
if CheckSetExists(db, set.Code) {
log.Println("Skipping " + set.Code + ", already in database")
return nil
}
result, err := insert.Exec(set.Code, set.Name, set.SetType, set.ReleasedAt, set.BlockCode, set.Block, set.ParentSetCode, set.CardCount, set.PrintedSize, set.Digital, set.FoilOnly, set.NonfoilOnly, set.IconSvgUri)
rowsAffected, err := result.RowsAffected()
if err != nil || rowsAffected != 1 {
return err
}
return nil
}
func InsertSets(db *sql.DB, setList scryfall.SetList) error {
for index := range setList.Data {
set := setList.Data[index]
err := insertSet(db, set)
if err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1 @@
package imports

View File

@ -3,25 +3,8 @@ package database
import (
"database/sql"
"log"
"github.com/go-sql-driver/mysql"
)
var db *sql.DB
func GetDatabase(config mysql.Config) *sql.DB {
db, err := sql.Open("mysql", config.FormatDSN())
if err != nil {
log.Fatal(err)
}
if err = db.Ping(); err != nil {
log.Fatal(err)
}
return db
}
func createGamepieceTable(db *sql.DB) {
query := `CREATE TABLE IF NOT EXISTS Gamepiece (
Id INT AUTO_INCREMENT PRIMARY KEY,
@ -81,31 +64,6 @@ func createColorTable(db *sql.DB) {
}
}
func populateColorTable(db *sql.DB) {
query := `INSERT INTO Color (Name, Abbreviation) VALUES (?, ?);`
insert, err := db.Prepare(query)
defer insert.Close()
if err != nil {
log.Fatal(err)
}
colors := map[string]string{
"White": "W",
"Blue": "U",
"Black": "B",
"Red": "R",
"Green": "G",
}
for name, abbrev := range colors {
result, err := insert.Exec(name, abbrev)
rowsAffected, err := result.RowsAffected()
if err != nil || rowsAffected != 1 {
log.Fatal(err)
}
}
}
func createGamepieceColorTable(db *sql.DB) {
query := `CREATE TABLE IF NOT EXISTS GamepieceColor (
ColorId INT,
@ -209,9 +167,19 @@ func createGamepieceFormatLegalityTable(db *sql.DB) {
func createSetTable(db *sql.DB) {
query := `CREATE TABLE IF NOT EXISTS ExpansionSet (
Id INT AUTO_INCREMENT PRIMARY KEY,
SetCode VARCHAR(4) NOT NULL,
Name VARCHAR(40) NOT NULL,
Digital BOOLEAN NOT NULL
SetCode VARCHAR(6) NOT NULL,
Name VARCHAR(60) NOT NULL,
SetType VARCHAR(20) NOT NULL,
ReleasedAt DATETIME NOT NULL,
BlockCode VARCHAR(10) NULL,
Block VARCHAR(40) NULL,
ParentSetCode VARCHAR(5) NULL,
CardCount INT NOT NULL,
PrintedSize INT NULL,
Digital BOOLEAN NOT NULL,
FoilOnly BOOLEAN NOT NULL,
NonfoilOnly BOOLEAN NOT NULL,
IconSvgUri VARCHAR(60) NOT NULL
);`
_, err := db.Exec(query)
@ -269,7 +237,6 @@ func createCardPrintingTable(db *sql.DB) {
func CreateDatabaseSchema(db *sql.DB) {
createGamepieceTable(db)
createColorTable(db)
populateColorTable(db)
createGamepieceColorTable(db)
createGamepieceColorIdentityTable(db)
createGamepieceColorIndicatorTable(db)

130
sevenkeys/scryfall/card.go Normal file
View File

@ -0,0 +1,130 @@
package scryfall
type Colors []rune
type RelatedCard struct {
Id string `json:"id"`
Object string `json:"object"`
Component string `json:"component"`
Name string `json:"name"`
TypeLine string `json:"type_line"`
ApiUri string `json:"uri"`
}
type CardFace struct {
Artist string `json:"artist"`
ArtistId string `json:"artist_id"`
ManaValue float32 `json:"cmc"`
ColorIndicator Colors `json:"color_indicator"`
Colors Colors `json:"colors"`
Defense string `json:"defense"`
FlavorText string `json:"flavor_text"`
IllustrationId string `json:"illustration_id"`
ImageUris interface{} `json:"image_uris"` // TODO: Find out the structure of this object
Layout string `json:"layout"`
Loyalty string `json:"loyalty"`
ManaCost string `json:"mana_cost"`
Name string `json:"name"`
Object string `json:"object"`
OracleId string `json:"oracle_id"`
Power string `json:"power"`
PrintedName string `json:"printed_name"`
PrintedText string `json:"printed_text"`
PrintedTypeLine string `json:"printed_type_line"`
Toughness string `json:"toughness"`
TypeLine string `json:"type_line"`
Watermark string `json:"watermark"`
}
type Card struct {
// Core fields
ArenaId int `json:"arena_id"`
Id string `json:"id"`
Language string `json:"lang"`
MtgoId int `json:"mtgo_id"`
MtgoFoilid int `json:"mtgo_foil_id"`
MultiverseIds []int `json:"multiverse_ids"`
TcgplayerId int `json:"tcgplayer_id"`
TcgplayerEtchedId int `json:"tcgplayer_etched_id"`
Object string `json:"object"`
Layout string `json:"layout"`
OracleId string `json:"oracle_id"`
PrintsSearchUri string `json:"prints_search_uri"`
RulingsUri string `json:"rulings_uri"`
ScryfallUri string `json:"scryfall_uri"`
ApiUri string `json:"uri"`
// Gameplay fields
AllParts []RelatedCard `json:"all_parts"`
CardFaces []CardFace `json:"card_faces"`
ManaValue float32 `json:"cmc"`
ColorIdentity Colors `json:"color_identity"`
ColorIndicator Colors `json:"color_indicator"`
Colors Colors `json:"colors"`
Defense string `json:"defense"`
EdhrecRank int `json:"edhrec_rank"`
HandModifier string `json:"hand_modifier"`
Keywords []string `json:"keywords"`
Legalities map[string]string `json:"legalities"`
LifeModifier string `json:"life_modifier"`
Loyalty string `json:"loyalty"`
ManaCost string `json:"mana_cost"`
Name string `json:"name"`
OracleText string `json:"oracle_text"`
PennyDreadfulRank int `json:"penny_rank"`
Power string `json:"power"`
ProducedMana Colors `json:"produced_mana"`
ReserveList bool `json:"reserved"`
Toughness string `json:"toughness"`
TypeLine string `json:"type_line"`
// Printing fields
Artist string `json:"artist"`
ArtistIds []string `json:"artist_ids"`
AttractionLights []interface{} `json:"attraction_lights"` // TODO: Figure out schema
Booster bool `json:"booster"`
BorderColor string `json:"border_color"`
CardBackId string `json:"card_back_id"`
CollectorNumber string `json:"collector_number"`
ContentWarning bool `json:"content_warning"`
Digital bool `json:"digital"`
Finishes []interface{} `json:"finishes"` // TODO: Find out how flags are formatted
FlavorName string `json:"flavor_name"`
FlavorText string `json:"flavor_text"`
FrameEffects []string `json:"frame_effects"`
Frame []string `json:"frame"`
FullArt bool `json:"full_art"`
Games []string `json:"games"`
HighresImage bool `json:"highres_image"`
IllustrationId string `json:"illustration_id"`
ImageStatus string `json:"image_status"`
ImageUris interface{} `json:"image_uris"` // TODO: Find out shape of object
Oversized bool `json:"oversized"`
Prices map[string]string `json:"prices"`
PrintedName string `json:"printed_name"`
PrintedText string `json:"printed_text"`
PrintedTypeLine string `json:"printed_type_line"`
Promo bool `json:"promo"`
PromoTypes bool `json:"promo_types"`
PurchaseUris interface{} `json:"purchase_uris"` // TODO: Find out shape of object
Rarity string `json:"rarity"`
RelatedUris interface{} `json:"related_uris"` // TODO: Find out shape of object
ReleasedAt string `json:"released_at"` // TODO: Datetime type?
Reprint bool `json:"reprint"`
ScryfallSetUri string `json:"scryfall_set_uri"`
SetName string `json:"set_name"`
SetSearchUri string `json:"set_search_uri"`
SetType string `json:"set_type"`
SetUri string `json:"set_uri"`
Set string `json:"set"`
SetId string `json:"set_id"`
StorySpotlight string `json:"story_spotlight"`
Textless bool `json:"textless"`
Variation bool `json:"variation"`
VariationOf string `json:"variation_of"`
SecurityStamp string `json:"security_stamp"`
Watermark string `json:"watermark"`
PreviewedAt string `json:"preview.previewed_at"`
PreviewSourceUri string `json:"preview.source_uri"`
PreviewSource string `json:"preview.source"`
}

65
sevenkeys/scryfall/set.go Normal file
View File

@ -0,0 +1,65 @@
package scryfall
import (
"encoding/json"
"errors"
"io"
"net/http"
)
type Set struct {
Object string `json:"object"`
Id string `json:"id"` // Use UUID package?
Code string `json:"code"`
MtgoCode string `json:"mtgo_code"`
ArenaCode string `json:"arena_code"`
TcgplayerId int `json:"tcgplayer_id"`
Name string `json:"name"`
SetType string `json:"set_type"`
ReleasedAt string `json:"released_at"`
BlockCode string `json:"block_code"`
Block string `json:"block"`
ParentSetCode string `json:"parent_set_code"`
CardCount int `json:"card_count"`
PrintedSize int `json:"printed_size"`
Digital bool `json:"digital"`
FoilOnly bool `json:"foil_only"`
NonfoilOnly bool `json:"nonfoil_only"`
ScryfallUri string `json:"scryfall_uri"`
Uri string `json:"uri"`
IconSvgUri string `json:"icon_svg_uri"`
SearchUri string `json:"search_uri"`
}
type SetList struct {
Object string `json:"object"`
HasMore bool `json:"has_more"`
Data []Set `json:"data"`
}
var SETS_API string = "https://api.scryfall.com/sets"
func GetSets() (SetList, error) {
response, err := http.Get(SETS_API)
if err != nil {
return SetList{}, err
}
defer response.Body.Close()
if response.StatusCode == http.StatusOK {
setsBytes, err := io.ReadAll(response.Body)
if err != nil {
return SetList{}, err
}
var setList SetList
err = json.Unmarshal(setsBytes, &setList)
if err != nil {
return SetList{}, err
}
return setList, nil
}
return SetList{}, errors.New("scryfall: failed to get all sets")
}

View File

@ -36,7 +36,7 @@ CREATE TABLE IF NOT EXISTS Gamepiece (
OracleText VARCHAR(800) NOT NULL,
Power VARCHAR(5) NULL,
Toughness VARCHAR(5) NULL,
ReserveList BOOLEAN NOT NULL,
ReserveList BOOLEAN NOT NULL
);
CREATE TABLE IF NOT EXISTS Color (
@ -44,11 +44,6 @@ CREATE TABLE IF NOT EXISTS Color (
Name VARCHAR(5) NOT NULL,
Abbreviation VARCHAR(1) NOT NULL
);
INSERT INTO Color (Name, Abbreviation) VALUES ("White", "W");
INSERT INTO Color (Name, Abbreviation) VALUES ("Blue", "U");
INSERT INTO Color (Name, Abbreviation) VALUES ("Black", "B");
INSERT INTO Color (Name, Abbreviation) VALUES ("Red", "R");
INSERT INTO Color (Name, Abbreviation) VALUES ("Green", "G");
CREATE TABLE IF NOT EXISTS GamepieceColor (
ColorId INT,
@ -103,9 +98,10 @@ CREATE TABLE IF NOT EXISTS GamepieceFormatLegality (
CREATE TABLE IF NOT EXISTS ExpansionSet (
Id INT AUTO_INCREMENT NOT NULL,
SetCode VARCHAR(4) NOT NULL,
SetCode VARCHAR(5) NOT NULL,
Name VARCHAR(40) NOT NULL,
Digital BOOLEAN NOT NULL,
SetType VARCHAR(20) NOT NULL
);
CREATE TABLE IF NOT EXISTS Artist (
@ -137,5 +133,5 @@ CREATE TABLE IF NOT EXISTS CardPrinting (
DigitalOnly BOOLEAN NOT NULL,
Variation BOOLEAN NOT NULL,
VariationId VARCHAR(36) NULL,
SecurityStamp ENUM("oval", "triangle", "acorn", "circle", "arena", "heart") NULL,
SecurityStamp ENUM("oval", "triangle", "acorn", "circle", "arena", "heart") NULL
);

View File

@ -0,0 +1 @@
CREATE DATABASE IF NOT EXISTS sevenkeys;

View File

@ -0,0 +1 @@
DROP DATABASE IF EXISTS sevenkeys;