Add cv_engine package.
This commit is contained in:
0
cv_engine/__init__.py
Normal file
0
cv_engine/__init__.py
Normal file
536
cv_engine/database.py
Normal file
536
cv_engine/database.py
Normal file
@@ -0,0 +1,536 @@
|
||||
import sqlite3
|
||||
import ast
|
||||
|
||||
from cv_engine.models import Card, Set
|
||||
from cv_engine.util import EngineConfig, EngineConstants, MTGConstants
|
||||
|
||||
|
||||
class CardvaultDB:
|
||||
"""Data access class for sqlite3"""
|
||||
|
||||
def __init__(self, db_file: str):
|
||||
self.db_file = db_file
|
||||
self.connection = sqlite3.connect(self.db_file)
|
||||
self.db_create()
|
||||
|
||||
# Database operations ##############################################################################################
|
||||
|
||||
def db_create(self):
|
||||
"""Create initial database"""
|
||||
con = sqlite3.connect(self.db_file)
|
||||
|
||||
with con:
|
||||
# Create library table
|
||||
con.execute('CREATE TABLE IF NOT EXISTS cards ('
|
||||
'multiverseid INT, name TEXT, layout TEXT, manaCost TEXT, fcolor TEXT, cmc INT, '
|
||||
'rarity TEXT, text TEXT, flavor TEXT, artist TEXT, number INTEGER, power TEXT, '
|
||||
'toughness TEXT, loyalty INT, watermark TEXT, border TEXT, timeshifted INT, '
|
||||
'hand TEXT, life TEXT, releaseDate TEXT, starter TEXT, originalText TEXT, originalType TEXT, '
|
||||
'source TEXT, imageUrl TEXT, `set` TEXT, setName TEXT, id TEXT)')
|
||||
|
||||
con.execute("CREATE TABLE IF NOT EXISTS library ( multiverseid INT PRIMARY KEY, copies INT )")
|
||||
con.execute("CREATE TABLE IF NOT EXISTS tags ( tag TEXT, multiverseid INT )")
|
||||
con.execute("CREATE TABLE IF NOT EXISTS wants ( listName TEXT, multiverseid INT )")
|
||||
con.execute("CREATE TABLE IF NOT EXISTS sets ( code TEXT PRIMARY KEY , name TEXT, type TEXT, border TEXT, "
|
||||
"mkmid INT, mkmname TEXT, releasedate TEXT, gatherercode TEXT, magiccardsinfocode TEXT, "
|
||||
"booster TEXT, oldcode TEXT)")
|
||||
|
||||
con.execute('CREATE TABLE IF NOT EXISTS card_names ('
|
||||
'multiverseid INT NOT NULL,'
|
||||
'name TEXT)')
|
||||
|
||||
con.execute('CREATE TABLE IF NOT EXISTS card_types ('
|
||||
'multiverseid INT NOT NULL,'
|
||||
'type TEXT)')
|
||||
|
||||
con.execute('CREATE TABLE IF NOT EXISTS card_subtypes ('
|
||||
'multiverseid INT NOT NULL,'
|
||||
'subtype TEXT)')
|
||||
|
||||
con.execute('CREATE TABLE IF NOT EXISTS card_supertypes ('
|
||||
'multiverseid INT NOT NULL,'
|
||||
'supertype TEXT)')
|
||||
|
||||
con.execute('CREATE TABLE IF NOT EXISTS card_printings ('
|
||||
'multiverseid INT NOT NULL,'
|
||||
'code TEXT)')
|
||||
|
||||
con.execute('CREATE TABLE IF NOT EXISTS card_variations ('
|
||||
'multiverseid INT NOT NULL,'
|
||||
'variation INT)')
|
||||
|
||||
con.execute('CREATE TABLE IF NOT EXISTS card_colors ('
|
||||
'multiverseid INT NOT NULL,'
|
||||
'color TEXT)')
|
||||
|
||||
con.execute('CREATE TABLE IF NOT EXISTS card_rulings ('
|
||||
'multiverseid INT NOT NULL,'
|
||||
'date TEXT,'
|
||||
'`text` TEXT)')
|
||||
|
||||
con.execute('CREATE TABLE IF NOT EXISTS card_legalities ('
|
||||
'multiverseid INT NOT NULL,'
|
||||
'format TEXT,'
|
||||
'legality TEXT)')
|
||||
|
||||
con.execute('CREATE TABLE IF NOT EXISTS card_foreign_names ('
|
||||
'multiverseid INT NOT NULL,'
|
||||
'language TEXT,'
|
||||
'name TEXT)')
|
||||
|
||||
def db_get_all(self):
|
||||
"""Return data of all cards in database"""
|
||||
sql = 'SELECT * FROM cards'
|
||||
cur = self.connection.cursor()
|
||||
cur.row_factory = sqlite3.Row
|
||||
cur.execute(sql)
|
||||
rows = cur.fetchall()
|
||||
output = []
|
||||
for row in rows:
|
||||
card = self.map_row_to_card(row)
|
||||
output.append(card)
|
||||
return output
|
||||
|
||||
def db_all_multiverse_ids(self) -> list:
|
||||
"""
|
||||
Load all multiverse_ids from the database
|
||||
:return: list of integers
|
||||
"""
|
||||
cur = self.connection.cursor()
|
||||
cur.row_factory = sqlite3.Row
|
||||
cur.execute('SELECT multiverseid FROM cards')
|
||||
return [x['multiverseid'] for x in cur.fetchall()]
|
||||
|
||||
def db_clear_data_card(self):
|
||||
"""Delete all resource data from database"""
|
||||
con = sqlite3.connect(self.db_file)
|
||||
try:
|
||||
with con:
|
||||
tables = ['cards', 'sets', 'card_colors', 'card_foreign_names', 'card_legalities', 'card_names',
|
||||
'card_printings', 'card_rulings', 'card_subtypes', 'card_supertypes', 'card_types',
|
||||
'card_variations']
|
||||
for table in tables:
|
||||
con.execute("DELETE FROM {}".format(table))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def db_clear_data_user(self):
|
||||
"""Delete all user data from database"""
|
||||
self.db_operation('DELETE FROM library')
|
||||
self.db_operation('DELETE FROM wants')
|
||||
self.db_operation('DELETE FROM tags')
|
||||
|
||||
# Card operations ##################################################################################################
|
||||
|
||||
def card_insert_many(self, card_list: list):
|
||||
"""
|
||||
Shorthand for inserting many cards at once
|
||||
Uses a single database connection for all commits
|
||||
:param card_list: list of cv_engine.models.Card objects
|
||||
"""
|
||||
con = sqlite3.connect(self.db_file)
|
||||
with con:
|
||||
for card in card_list:
|
||||
self.card_insert(card, con)
|
||||
|
||||
def card_insert(self, card, connection=None):
|
||||
"""
|
||||
Insert a single card into the database
|
||||
:param card: An cv_engine.models.Card object
|
||||
:param connection: (Optional) supply a database connection to use. It will not be closed after the function
|
||||
ends
|
||||
"""
|
||||
if not card.multiverse_id:
|
||||
return
|
||||
if not connection:
|
||||
con = sqlite3.connect(self.db_file)
|
||||
else:
|
||||
con = connection
|
||||
try:
|
||||
# List attributes of card object are written in connection tables
|
||||
mapping = {'card_names': card.names, 'card_types': card.types, 'card_subtypes': card.subtypes,
|
||||
'card_supertypes': card.supertypes, 'card_printings': card.printings,
|
||||
'card_variations': card.variations, 'card_colors': card.colors}
|
||||
|
||||
for table_name, values in mapping.items():
|
||||
if not values:
|
||||
continue
|
||||
db_values = [(card.multiverse_id, value) for value in values]
|
||||
sql_string = "INSERT INTO {} VALUES (?, ?)".format(table_name)
|
||||
con.executemany(sql_string, db_values)
|
||||
|
||||
# Insert dict attributes into separate tables
|
||||
mapping = {'card_rulings': [card.rulings, 'date', 'text'],
|
||||
'card_legalities': [card.legalities, 'format', 'legality'],
|
||||
'card_foreign_names': [card.foreign_names, 'language', 'name']}
|
||||
|
||||
for table_name, data in mapping.items():
|
||||
if not data[0]:
|
||||
continue
|
||||
db_values = [(card.multiverse_id, x.get(data[1]), x.get(data[2])) for x in data[0]]
|
||||
sql_string = "INSERT INTO {} VALUES (?, ?, ?)".format(table_name)
|
||||
con.executemany(sql_string, db_values)
|
||||
|
||||
# Write card attributes to database
|
||||
card_row = self.map_card_to_row(card)
|
||||
sql_string = "INSERT INTO cards VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,"\
|
||||
"?, ?, ?, ?, ?, ?, ?)"
|
||||
con.execute(sql_string, card_row)
|
||||
except sqlite3.OperationalError as e:
|
||||
print(e)
|
||||
except sqlite3.IntegrityError as e:
|
||||
print(e)
|
||||
finally:
|
||||
if not connection:
|
||||
con.close()
|
||||
|
||||
def card_load(self, card_id: int):
|
||||
"""
|
||||
Load a single card from database
|
||||
:param card_id: multiverse_id of the card
|
||||
:return: an cv_engine.models.Card object
|
||||
"""
|
||||
cur = self.connection.cursor()
|
||||
cur.row_factory = sqlite3.Row
|
||||
|
||||
# Fetch card row
|
||||
cur.execute('SELECT * FROM `cards` WHERE `multiverseid` = ?', (card_id,))
|
||||
card_dict = dict(cur.fetchall()[0])
|
||||
|
||||
# Fetch list attributes of card
|
||||
attrs_list = {'card_names', 'card_types', 'card_subtypes', 'card_supertypes', 'card_printings',
|
||||
'card_variations', 'card_colors'}
|
||||
for attr in attrs_list:
|
||||
cur.execute('SELECT * FROM {} WHERE `multiverseid` = ?'.format(attr), (card_id,))
|
||||
rows = cur.fetchall()
|
||||
card_dict[attr.split('_')[1]] = [row[1] for row in rows]
|
||||
|
||||
# Fetch dict attributes of card
|
||||
attrs_list = {'card_rulings': ['date', 'text'],
|
||||
'card_legalities': ['format', 'legality'],
|
||||
'card_foreign_names': ['language', 'name']}
|
||||
for table_name, attrs in attrs_list.items():
|
||||
cur.execute('SELECT {}, {} FROM {} WHERE `multiverseid` = ?'.format(attrs[0], attrs[1], table_name),
|
||||
(card_id,))
|
||||
rows = cur.fetchall()
|
||||
attr_name = '_'.join(table_name.split('_')[1:])
|
||||
card_dict[attr_name] = {row[0]: row[1] for row in rows}
|
||||
return Card(card_dict)
|
||||
|
||||
def card_search_by_name(self, search_term):
|
||||
"""
|
||||
Search for card by their name.
|
||||
Search results are limited to 50 results.
|
||||
:param search_term: Search String
|
||||
:return: List of 'cv_engine.models.Card' objects
|
||||
"""
|
||||
cur = self.connection.cursor()
|
||||
cur.row_factory = sqlite3.Row
|
||||
cur.execute("SELECT `multiverseid` FROM `cards` WHERE `name` LIKE ? LIMIT 50", ('%' + search_term + '%',))
|
||||
return [self.card_load(row[0]) for row in cur.fetchall()]
|
||||
|
||||
# Library operations ###############################################################################################
|
||||
|
||||
def lib_get_all(self) -> list:
|
||||
"""
|
||||
Load all cards in library from database in alphabetical order
|
||||
:return: A list containing all cards in library as 'cv_engine.models.Card'
|
||||
"""
|
||||
cur = self.connection.cursor()
|
||||
cur.row_factory = sqlite3.Row
|
||||
cur.execute('SELECT * FROM `library` INNER JOIN `cards` ON library.multiverseid = cards.multiverseid')
|
||||
rows = cur.fetchall()
|
||||
return sorted([self.map_row_to_card(row) for row in rows], key=lambda x: x.name)
|
||||
|
||||
def lib_card_add(self, card: Card):
|
||||
"""Insert card into library"""
|
||||
self.db_operation("INSERT INTO `library` (`copies`, `multiverseid`) VALUES (?, ?)", (1, card.multiverse_id))
|
||||
|
||||
def lib_card_remove(self, card: Card):
|
||||
"""Remove a from the library"""
|
||||
self.db_operation("DELETE FROM `library` WHERE `multiverseid` = ?", (card.multiverse_id,))
|
||||
|
||||
# Category operations ##############################################################################################
|
||||
|
||||
def category_get_all(self) -> dict:
|
||||
"""Loads a dict from database with all categories and the card ids they contain"""
|
||||
cur = self.connection.cursor()
|
||||
cur.row_factory = sqlite3.Row
|
||||
|
||||
# First load all tags
|
||||
cur.execute("SELECT `tag` FROM tags GROUP BY `tag`")
|
||||
# Create dict with the fetched categories as key
|
||||
cats = {row['tag']: [] for row in cur.fetchall()}
|
||||
|
||||
# Go trough all categories an load the card ids
|
||||
for cat in cats.keys():
|
||||
cur.execute('SELECT `multiverseid` FROM `tags` WHERE tags.tag = ? AND multiverseid NOT NULL', (cat,))
|
||||
rows = cur.fetchall()
|
||||
for row in rows:
|
||||
cats[cat].append(row["multiverseid"])
|
||||
return cats
|
||||
|
||||
def category_new(self, name: str):
|
||||
"""Add a new category to the database"""
|
||||
self.db_operation("INSERT INTO `tags` VALUES (?, NULL)", (name,))
|
||||
|
||||
def category_delete(self, name: str):
|
||||
"""Remove a category with all entries"""
|
||||
self.db_operation("DELETE FROM `tags` WHERE `tag` = ?", (name,))
|
||||
|
||||
def category_rename(self, name_old: str, name_new: str):
|
||||
"""Rename a category"""
|
||||
self.db_operation('UPDATE `tags` SET `tag`=? WHERE `tag` = ?;', (name_new, name_old))
|
||||
|
||||
def category_card_add(self, name: str, card_id: int):
|
||||
"""Add an entry for a categorized card"""
|
||||
self.db_operation("INSERT INTO `tags` VALUES (?, ?)", (name, card_id))
|
||||
|
||||
def category_card_remove(self, name: str, card_id: int):
|
||||
"""Remove a card from a category"""
|
||||
self.db_operation("DELETE FROM `tags` WHERE `tag` = ? AND `multiverseid` = ?", (name, card_id))
|
||||
|
||||
def category_get_for_card(self, card) -> list:
|
||||
"""Return a list with all categories that contain the supplied card."""
|
||||
cur = self.connection.cursor()
|
||||
cur.row_factory = sqlite3.Row
|
||||
cur.execute('SELECT `tag` FROM `tags` WHERE tags.multiverseid = ? ', (card.multiverse_id,))
|
||||
rows = cur.fetchall()
|
||||
return [row['tag'] for row in rows]
|
||||
|
||||
# Wants operations #################################################################################################
|
||||
|
||||
def wants_get_all(self) -> dict:
|
||||
"""Load all wants lists from database"""
|
||||
cur = self.connection.cursor()
|
||||
cur.row_factory = sqlite3.Row
|
||||
|
||||
# First load all lists
|
||||
cur.execute("SELECT `listName` FROM wants GROUP BY `listName`")
|
||||
wants = {row['listName']: [] for row in cur.fetchall()}
|
||||
|
||||
# Go trough all tags an load the card ids
|
||||
for list_name in wants.keys():
|
||||
cur.execute('SELECT * FROM '
|
||||
'(SELECT `multiverseid` FROM `wants` WHERE `listName` = ? AND multiverseid NOT NULL) tagged '
|
||||
'INNER JOIN `cards` ON tagged.multiverseid = cards.multiverseid', (list_name,))
|
||||
rows = cur.fetchall()
|
||||
for row in rows:
|
||||
wants[list_name].append(self.map_row_to_card(row))
|
||||
return wants
|
||||
|
||||
def wants_new(self, name: str):
|
||||
"""Add a new wants list to the database"""
|
||||
self.db_operation("INSERT INTO `wants` VALUES (?, NULL)", (name,))
|
||||
|
||||
def wants_delete(self, name: str):
|
||||
"""Remove a tag with all entries"""
|
||||
self.db_operation("DELETE FROM `wants` WHERE `listName` = ?", (name,))
|
||||
|
||||
def wants_rename(self, name_old: str, name_new: str):
|
||||
"""Rename a tag"""
|
||||
self.db_operation('UPDATE `wants` SET `listName`=? WHERE `listName` = ?;', (name_new, name_old))
|
||||
|
||||
def wants_card_add(self, list_name: str, card_id: int):
|
||||
"""Add a card entry to a wants list"""
|
||||
self.db_operation("INSERT INTO `wants` VALUES (?, ?)", (list_name, card_id))
|
||||
|
||||
def wants_card_remove(self, list_name: str, card_id: int):
|
||||
"""Remove a card from a want list """
|
||||
self.db_operation("DELETE FROM `wants` WHERE `listName` = ? AND `multiverseid` = ?", (list_name, card_id))
|
||||
|
||||
# Query operations #################################################################################################
|
||||
|
||||
def search_by_name_filtered(self, term: str, filters: dict, list_size: int) -> list:
|
||||
"""Search for cards based on the cards name with filter constrains"""
|
||||
filter_rarity = filters["rarity"]
|
||||
filer_type = filters["type"]
|
||||
filter_set = filters["set"]
|
||||
filter_mana = filters["mana"]
|
||||
filter_mana.sort(key=lambda x: MTGConstants.mana_order.index(x))
|
||||
|
||||
sql = 'SELECT * FROM cards WHERE `name` LIKE ?'
|
||||
parameters = ['%' + term + '%']
|
||||
if filter_rarity != "":
|
||||
sql += ' AND `rarity` = ?'
|
||||
parameters.append(filter_rarity)
|
||||
if filer_type != "":
|
||||
sql += ' AND `types` LIKE ?'
|
||||
parameters.append('%' + filer_type + '%')
|
||||
if filter_set != "":
|
||||
sql += ' AND `set` = ?'
|
||||
parameters.append(filter_set)
|
||||
if len(filter_mana) != 0:
|
||||
sql += ' AND `fcolor` = ?'
|
||||
parameters.append(self.filter_colors_list(filter_mana))
|
||||
sql += ' LIMIT ?'
|
||||
parameters.append(str(list_size))
|
||||
|
||||
con = sqlite3.connect(self.db_file)
|
||||
cur = con.cursor()
|
||||
cur.row_factory = sqlite3.Row
|
||||
cur.execute(sql, parameters)
|
||||
rows = cur.fetchall()
|
||||
con.close()
|
||||
|
||||
output = []
|
||||
for row in rows:
|
||||
card = self.map_row_to_card(row)
|
||||
output.append(card)
|
||||
return output
|
||||
|
||||
def search_by_name(self, term: str) -> dict:
|
||||
"""Search for cards based on the cards name"""
|
||||
con = sqlite3.connect(self.db_file)
|
||||
cur = con.cursor()
|
||||
cur.row_factory = sqlite3.Row
|
||||
cur.execute("SELECT * FROM cards WHERE `name` LIKE ? LIMIT 50", ('%' + term + '%',))
|
||||
rows = cur.fetchall()
|
||||
con.close()
|
||||
|
||||
return self.rows_to_card_dict(rows)
|
||||
|
||||
def set_get_all(self):
|
||||
con = sqlite3.connect(self.db_file)
|
||||
cur = con.cursor()
|
||||
cur.row_factory = sqlite3.Row
|
||||
|
||||
cur.execute("SELECT * FROM sets")
|
||||
rows = cur.fetchall()
|
||||
sets = []
|
||||
for row in rows:
|
||||
sets.append(self.map_row_to_set(row))
|
||||
|
||||
return sets
|
||||
|
||||
# DB internal functions ############################################################################################
|
||||
|
||||
def rows_to_card_dict(self, rows):
|
||||
"""Convert database rows to a card dict"""
|
||||
output = {}
|
||||
for row in rows:
|
||||
card = self.map_row_to_card(row)
|
||||
output[card.multiverse_id] = card
|
||||
return output
|
||||
|
||||
def db_operation(self, sql: str, args: tuple = ()):
|
||||
"""Perform an arbitrary sql operation on the database"""
|
||||
cur = self.connection.cursor()
|
||||
try:
|
||||
cur.execute(sql, args)
|
||||
except sqlite3.OperationalError:
|
||||
# TODO Log
|
||||
pass
|
||||
|
||||
def db_save_changes(self):
|
||||
try:
|
||||
self.connection.commit()
|
||||
except sqlite3.Error:
|
||||
self.connection.rollback()
|
||||
# TODO Log
|
||||
pass
|
||||
|
||||
def db_unsaved_changes(self) -> bool:
|
||||
"""Checks if database is currently in transaction"""
|
||||
return self.connection.in_transaction
|
||||
|
||||
@staticmethod
|
||||
def filter_colors_list(mana) -> str:
|
||||
symbols = set(mana)
|
||||
output = [s for s in symbols if (s in MTGConstants.mana_order)]
|
||||
return "-".join(output)
|
||||
|
||||
@staticmethod
|
||||
def filter_colors(card) -> str:
|
||||
"""Extracts the colors of a card for filtering."""
|
||||
output = []
|
||||
if card.colors is not None:
|
||||
for color in card.colors:
|
||||
output.append(MTGConstants.color_shorthands[color])
|
||||
else:
|
||||
output.append("C")
|
||||
# TODO extract symbols from card text
|
||||
|
||||
return "-".join(output)
|
||||
|
||||
def map_card_to_row(self, card):
|
||||
"""Return the database representation of a card object"""
|
||||
return (card.multiverse_id, card.name, card.layout, card.mana_cost, self.filter_colors(card), card.cmc,
|
||||
card.rarity, card.text, card.flavor, card.artist, card.number, card.power, card.toughness, card.loyalty,
|
||||
card.watermark, card.border, card.timeshifted, card.hand, card.life, card.release_date, card.starter,
|
||||
card.original_text, card.original_type, card.source, card.image_url, card.set, card.set_name, card.id)
|
||||
|
||||
@staticmethod
|
||||
def map_row_to_card(row):
|
||||
"""Return card object representation of a table row"""
|
||||
card = Card()
|
||||
card.multiverse_id = row["multiverseid"]
|
||||
tmp = row["name"]
|
||||
card.name = tmp
|
||||
card.layout = row["layout"]
|
||||
card.mana_cost = row["manacost"]
|
||||
card.cmc = row["cmc"]
|
||||
card.colors = row["colors"]
|
||||
card.type = row["type"]
|
||||
card.rarity = row["rarity"]
|
||||
card.text = row["text"]
|
||||
card.flavor = row["flavor"]
|
||||
card.artist = row["artist"]
|
||||
card.number = row["number"]
|
||||
card.power = row["power"]
|
||||
card.toughness = row["toughness"]
|
||||
card.loyalty = row["loyalty"]
|
||||
card.watermark = row["watermark"]
|
||||
card.border = row["border"]
|
||||
card.hand = row["hand"]
|
||||
card.life = row["life"]
|
||||
card.release_date = row["releaseDate"]
|
||||
card.starter = row["starter"]
|
||||
card.original_text = row["originalText"]
|
||||
card.original_type = row["originalType"]
|
||||
card.source = row["source"]
|
||||
card.image_url = row["imageUrl"]
|
||||
card.set = row["set"]
|
||||
card.set_name = row["setName"]
|
||||
card.id = row["id"]
|
||||
|
||||
# # Bool attributes
|
||||
# card.timeshifted = ast.literal_eval(row["timeshifted"])
|
||||
#
|
||||
# # List attributes
|
||||
# card.names = ast.literal_eval(row["names"])
|
||||
# card.supertypes = ast.literal_eval(row["supertypes"])
|
||||
# card.subtypes = ast.literal_eval(row["subtypes"])
|
||||
# card.types = ast.literal_eval(row["types"])
|
||||
# card.printings = ast.literal_eval(row["printings"])
|
||||
# card.variations = ast.literal_eval(row["variations"])
|
||||
#
|
||||
# # Dict attributes
|
||||
# card.legalities = ast.literal_eval(row["legalities"])
|
||||
# card.rulings = ast.literal_eval(row["rulings"])
|
||||
# card.foreign_names = ast.literal_eval(row["foreignNames"])
|
||||
|
||||
return card
|
||||
|
||||
@staticmethod
|
||||
def map_set_to_row(set):
|
||||
"""Convert Set object to a table row"""
|
||||
return (set.code, set.name, set.type, set.border, set.mkm_id, set.mkm_name, set.release_date, set.gatherer_code,
|
||||
set.magic_cards_info_code, str(set.booster), set.old_code)
|
||||
|
||||
@staticmethod
|
||||
def map_row_to_set(row):
|
||||
"""Return Set object representation of a table row"""
|
||||
set = Set()
|
||||
set.code = row['code']
|
||||
set.name = row['name']
|
||||
set.type = row['type']
|
||||
set.border = row['border']
|
||||
set.mkm_id = row['mkmid']
|
||||
set.mkm_name = row['mkmname']
|
||||
set.release_date = row['releasedate']
|
||||
set.gatherer_code = row['gatherercode']
|
||||
set.magic_cards_info_code = row['magiccardsinfocode']
|
||||
set.booster = ast.literal_eval(row['booster'])
|
||||
set.old_code = row['oldcode']
|
||||
return set
|
||||
73
cv_engine/engine.py
Normal file
73
cv_engine/engine.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import json
|
||||
import os
|
||||
import itertools
|
||||
|
||||
from jsondiff import diff
|
||||
|
||||
from cv_engine.database import CardvaultDB
|
||||
from cv_engine.util import EngineConfig, Utilities
|
||||
|
||||
|
||||
class CardvaultEngine:
|
||||
def __init__(self, config_file=False):
|
||||
"""
|
||||
Create a new cv_engine instance
|
||||
:param config_file: File path of the configuration file
|
||||
"""
|
||||
if config_file:
|
||||
Utilities.apply_config(config_file)
|
||||
db_file_path = os.path.join(EngineConfig.config_path, EngineConfig.db_file)
|
||||
self.database = CardvaultDB(db_file_path)
|
||||
|
||||
def get_card(self, card_id):
|
||||
"""
|
||||
Load a card object from database
|
||||
:param card_id: multiverse id of a card
|
||||
:return: an cv_engine.model.Card object
|
||||
"""
|
||||
return self.database.card_load(card_id)
|
||||
|
||||
def get_library(self) -> list:
|
||||
"""
|
||||
Get the complete library of cards
|
||||
:return: Alphabetically ordered list of all cards in library
|
||||
"""
|
||||
return self.database.lib_get_all()
|
||||
|
||||
def get_all_categories(self) -> dict:
|
||||
"""
|
||||
Get all categories an the cards that are contained within them
|
||||
:return: A dict with the category names and cv_engine.models.Card objects as values
|
||||
"""
|
||||
categories = self.database.category_get_all()
|
||||
all_ids = set(itertools.chain.from_iterable(categories.values()))
|
||||
card_objects = {card_id: self.database.card_load(card_id) for card_id in all_ids}
|
||||
for _, card_id_list in categories:
|
||||
for card_id in card_id_list:
|
||||
card_id_list[card_id] = card_objects[card_id]
|
||||
return categories
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
engine = CardvaultEngine()
|
||||
|
||||
# Insert Data into Datasbase
|
||||
# print("Database insert test:")
|
||||
# engine.database.db_clear_data_card()
|
||||
# cards = Utilities.parse_mtgjson_cards(json.load(open("/home/luxick/Downloads/AllSets-x.json")))
|
||||
# engine.database.card_insert_many(cards)
|
||||
|
||||
# Compare JSON Data to Data in Database
|
||||
# for card in Utilities.parse_mtgjson_cards(json.load(open("/home/luxick/Downloads/AllSets-x.json"))):
|
||||
# if card.multiverse_id:
|
||||
# print('From JSON: {}'.format(card.names))
|
||||
# print('From DB: {}\n'.format(engine.database.card_load(card.multiverse_id).names))
|
||||
|
||||
# Search test
|
||||
# term = 'fire'
|
||||
# for result in engine.database.card_search_by_name(term):
|
||||
# print(str(result), end='\n\n')
|
||||
# Fast load test
|
||||
# engine.database.card_fast_load()
|
||||
# all_ids = engine.database.db_all_multiverse_ids()
|
||||
# print('Loaded IDs: {}'.format(len(all_ids)))
|
||||
66
cv_engine/models.py
Normal file
66
cv_engine/models.py
Normal file
@@ -0,0 +1,66 @@
|
||||
import pprint
|
||||
|
||||
class Card:
|
||||
"""
|
||||
Model for an MTG card
|
||||
"""
|
||||
def __init__(self, card_dict={}):
|
||||
self.name = card_dict.get('name')
|
||||
self.layout = card_dict.get('layout')
|
||||
self.mana_cost = card_dict.get('manaCost')
|
||||
self.cmc = card_dict.get('cmc')
|
||||
self.colors = card_dict.get('colors')
|
||||
self.color_identity = card_dict.get('colorIdentity')
|
||||
self.names = card_dict.get('names')
|
||||
self.type = card_dict.get('type')
|
||||
self.supertypes = card_dict.get('supertypes')
|
||||
self.subtypes = card_dict.get('subtypes')
|
||||
self.types = card_dict.get('types')
|
||||
self.rarity = card_dict.get('rarity')
|
||||
self.text = card_dict.get('text')
|
||||
self.flavor = card_dict.get('flavor')
|
||||
self.artist = card_dict.get('artist')
|
||||
self.number = card_dict.get('number')
|
||||
self.power = card_dict.get('power')
|
||||
self.toughness = card_dict.get('toughness')
|
||||
self.loyalty = card_dict.get('loyalty')
|
||||
self.multiverse_id = card_dict.get('multiverseid')
|
||||
self.variations = card_dict.get('variations')
|
||||
self.watermark = card_dict.get('watermark')
|
||||
self.border = card_dict.get('border')
|
||||
self.timeshifted = card_dict.get('timeshifted')
|
||||
self.hand = card_dict.get('hand')
|
||||
self.life = card_dict.get('life')
|
||||
self.release_date = card_dict.get('releaseDate')
|
||||
self.starter = card_dict.get('starter')
|
||||
self.printings = card_dict.get('printings')
|
||||
self.original_text = card_dict.get('originalText')
|
||||
self.original_type = card_dict.get('originalType')
|
||||
self.source = card_dict.get('source')
|
||||
self.image_url = card_dict.get('imageUrl')
|
||||
self.set = card_dict.get('set')
|
||||
self.set_name = card_dict.get('setName')
|
||||
self.id = card_dict.get('id')
|
||||
self.legalities = card_dict.get('legalities')
|
||||
self.rulings = card_dict.get('rulings')
|
||||
self.foreign_names = card_dict.get('foreign_names')
|
||||
|
||||
|
||||
class Set:
|
||||
"""
|
||||
Model for an MTG expansion set
|
||||
"""
|
||||
def __init__(self, set_dict={}):
|
||||
self.code = set_dict.get('code')
|
||||
self.name = set_dict.get('name')
|
||||
self.type = set_dict.get('type')
|
||||
self.border = set_dict.get('border')
|
||||
self.mkm_id = set_dict.get('mkm_id')
|
||||
self.mkm_name = set_dict.get('mkm_name')
|
||||
self.release_date = set_dict.get('releaseDate')
|
||||
self.gatherer_code = set_dict.get('gathererCode')
|
||||
self.magic_cards_info_code = set_dict.get('magicCardsInfoCode')
|
||||
self.booster = set_dict.get('booster')
|
||||
self.old_code = set_dict.get('oldCode')
|
||||
self.block = set_dict.get('block')
|
||||
self.online_only = set_dict.get('onlineOnly')
|
||||
90
cv_engine/util.py
Normal file
90
cv_engine/util.py
Normal file
@@ -0,0 +1,90 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from cv_engine.models import Card
|
||||
|
||||
|
||||
class EngineConfig:
|
||||
"""
|
||||
Configuration class for the Cardvault engine
|
||||
Defines default values for all settings
|
||||
Should be changed at runtime to load customized settings
|
||||
"""
|
||||
# Should cv_engine show duplicate names in searches
|
||||
duplicate_names_in_search = True
|
||||
# Log level for cv_engine
|
||||
log_level = 0
|
||||
# Default path of cardvault configuration file
|
||||
config_path = os.path.join(os.path.expanduser('~'), '.config', 'cardvault')
|
||||
# Name of the database file
|
||||
db_file = 'cardvault.db'
|
||||
# Default path to store temporary files
|
||||
cache_path = os.path.join(os.path.expanduser('~'), '.cache', 'cardvault')
|
||||
|
||||
|
||||
class EngineConstants:
|
||||
"""
|
||||
Constants of cv_engine
|
||||
Contains version number, application infos, etc.
|
||||
"""
|
||||
# Version of the cardvault engine
|
||||
engine_version = 0.1
|
||||
# Location of manual wiki
|
||||
manual_location = 'https://github.com/luxick/cardvault'
|
||||
|
||||
|
||||
class MTGConstants:
|
||||
"""
|
||||
This class contains constants that can be used within the whole program
|
||||
Included are for example the the official color order or rarities
|
||||
"""
|
||||
# Color order for mana symbols
|
||||
mana_order = ('W', 'U', 'B', 'R', 'G')
|
||||
# All card types that can be used by the application
|
||||
card_types = ("Creature", "Artifact", "Instant", "Enchantment", "Sorcery", "Land", "Planeswalker")
|
||||
# Order of card rarities
|
||||
rarities = ('special', 'common', 'uncommon', 'rare', 'mythic rare')
|
||||
# URL for card images. Insert card.multiverse_id.
|
||||
card_image_url_base = 'http://gatherer.wizards.com/Handlers/Image.ashx?multiverseid={}&type=card'
|
||||
# Abbreviated version of all card colors
|
||||
color_shorthands = {
|
||||
'White': 'W',
|
||||
'Blue': 'U',
|
||||
'Black': 'B',
|
||||
'Red': 'R',
|
||||
'Green': 'G'
|
||||
}
|
||||
|
||||
|
||||
class Utilities:
|
||||
"""
|
||||
The class offers methods for general usage thorough the program.
|
||||
"""
|
||||
@staticmethod
|
||||
def apply_config(filename):
|
||||
"""
|
||||
Load the supplied configuration and apply it to the EngineConfig class.
|
||||
:param filename: Path to a json formatted cardvault config file.
|
||||
"""
|
||||
with open(filename) as config_file:
|
||||
config = json.load(config_file)
|
||||
for setting, value in config.items():
|
||||
EngineConfig.__dict__[setting] = value
|
||||
|
||||
@staticmethod
|
||||
def parse_mtgjson_cards(json_data):
|
||||
"""
|
||||
Parse a json object to
|
||||
:param json_data:
|
||||
"""
|
||||
output = []
|
||||
for data in json_data.values():
|
||||
cards = []
|
||||
for raw in data["cards"]:
|
||||
c = Card(raw)
|
||||
c.image_url = MTGConstants.card_image_url_base.format(c.multiverse_id)
|
||||
c.set = data["code"]
|
||||
c.set_name = data["name"]
|
||||
cards.append(c)
|
||||
output = output + cards
|
||||
return output
|
||||
Reference in New Issue
Block a user