diff --git a/cv_engine/__init__.py b/cv_engine/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cv_engine/database.py b/cv_engine/database.py new file mode 100644 index 0000000..7c0c5f1 --- /dev/null +++ b/cv_engine/database.py @@ -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 diff --git a/cv_engine/engine.py b/cv_engine/engine.py new file mode 100644 index 0000000..2466274 --- /dev/null +++ b/cv_engine/engine.py @@ -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))) diff --git a/cv_engine/models.py b/cv_engine/models.py new file mode 100644 index 0000000..e3f8a5c --- /dev/null +++ b/cv_engine/models.py @@ -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') \ No newline at end of file diff --git a/cv_engine/util.py b/cv_engine/util.py new file mode 100644 index 0000000..d97d29c --- /dev/null +++ b/cv_engine/util.py @@ -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