diff --git a/__init__.py b/__init__.py index e69de29..2855c22 100644 --- a/__init__.py +++ b/__init__.py @@ -0,0 +1,7 @@ +from scrython.cards import Autocomplete +from scrython.cards import Collector +from scrython.cards import Id +from scrython.cards import Mtgo +from scrython.cards import Multiverse +from scrython.cards import Named +from scrython.cards import RandomCard diff --git a/cards/__init__.py b/cards/__init__.py index e69de29..f0f191d 100644 --- a/cards/__init__.py +++ b/cards/__init__.py @@ -0,0 +1,7 @@ +from .autocomplete import Autocomplete +from .collector import Collector +from .cardid import Id +from .mtgo import Mtgo +from .multiverse import Multiverse +from .named import Named +from .randomcard import Random diff --git a/cards/autocomplete.py b/cards/autocomplete.py index 18346d1..99517b7 100644 --- a/cards/autocomplete.py +++ b/cards/autocomplete.py @@ -1,10 +1,10 @@ import asyncio, aiohttp -import json class Autocomplete(object): """ cards/autocomplete Parameters: + query: str The string to autocomplete. format: str The data format to return. Currently only supports JSON. pretty: bool If true, the returned JSON will be prettified. Avoid using for production code. @@ -15,10 +15,10 @@ class Autocomplete(object): """ - def __init__(self, query, pretty=None, _format=None): + def __init__(self, query, **kwargs): self.query = query - self.pretty = pretty - self.format = _format + self.pretty = kwargs.get('pretty') + self.format = kwargs.get('format') loop = asyncio.get_event_loop() self.session = aiohttp.ClientSession(loop=loop) @@ -30,13 +30,32 @@ class Autocomplete(object): url='https://api.scryfall.com/cards/autocomplete?', params={'q':self.query, 'pretty':self.pretty, 'format':self.format})) + if self.scryfallJson['object'] == 'error': + self.session.close() + raise Exception(self.scryfallJson['details']) + self.session.close() + def __checkForKey(self, key): + try: + return self.scryfallJson[key] + except KeyError: + return None + def object(self): + if self.__checkForKey('object') is None: + return KeyError('This card has no associated object key.') + return self.scryfallJson['object'] def total_items(self): + if self.__checkForKey('total_items') is None: + return KeyError('This card has no associated total items key.') + return self.scryfallJson['total_items'] def data(self): + if self.__checkForKey('data') is None: + return KeyError('This card has no associated data key.') + return self.scryfallJson['data'] diff --git a/cards/cardid.py b/cards/cardid.py new file mode 100644 index 0000000..65d4a0b --- /dev/null +++ b/cards/cardid.py @@ -0,0 +1,13 @@ +from .scryfall_object import ScryfallObject + +class Id(ScryfallObject): + """ cards/:id + + Parameters: + id: str The Scryfall id of the card. + """ + + def __init__(self, **kwargs): + self.id = kwargs.get('id') + self.url = 'cards/' + str(self.id) + super(Id, self).__init__(self.url) diff --git a/cards/collector.py b/cards/collector.py new file mode 100644 index 0000000..8fc2bf3 --- /dev/null +++ b/cards/collector.py @@ -0,0 +1,15 @@ +from .scryfall_object import ScryfallObject + +class Collector(ScryfallObject): + """ cards/:code/:collector_number + + Parameters: + code: str The 3 or 4 letter set code. + collector_number: int The collector number of the card. + """ + + def __init__(self, **kwargs): + self.code = kwargs.get('code') + self.collector_number = kwargs.get('collector_number') + self.url = 'cards/{}/{}'.format(self.code, str(self.collector_number)) + super(Collector, self).__init__(self.url) diff --git a/cards/mtgo.py b/cards/mtgo.py new file mode 100644 index 0000000..ef267ed --- /dev/null +++ b/cards/mtgo.py @@ -0,0 +1,15 @@ +from .scryfall_object import ScryfallObject +import urllib.parse + +class Mtgo(ScryfallObject): + """ cards/mtgo/:id + + Parameters: + id: int The mtgo id of the card. + + """ + + def __init__(self, **kwargs): + self.mtgoid = kwargs.get('id') + self.url = 'cards/mtgo/' + str(self.mtgoid) + super(Mtgo, self).__init__(self.url) diff --git a/cards/multiverse.py b/cards/multiverse.py new file mode 100644 index 0000000..28734ab --- /dev/null +++ b/cards/multiverse.py @@ -0,0 +1,12 @@ +from .scryfall_object import ScryfallObject + +class Multiverse(ScryfallObject): + """ + Parameters: + id: int The multiverse id of the card. + """ + + def __init__(self, **kwargs): + self.multiverseid = kwargs.get('id') + self.url = 'cards/multiverse/' + self.multiverseid + super(Multiverse, self).__init__(self.url) diff --git a/cards/named.py b/cards/named.py index 2bb7e31..7d9553f 100644 --- a/cards/named.py +++ b/cards/named.py @@ -1,204 +1,29 @@ -import asyncio, aiohttp -import json - -class Named(object): - """ cards/named +from .scryfall_object import ScryfallObject +import urllib.parse +class Named(ScryfallObject): + """ Parameters: exact: str The exact card name to search for, case insenstive. fuzzy: str A fuzzy card name to search for. set: str A set code to limit the search to one set. - format: str The data format to return: json, text, or image. Defaults to json. - face: str If using the image format and this parameter has the value back, - the back face of the card will be returned. - Will return a 404 if this card has no back face. - version: str The image version to return when using the image - format: small, normal, large, png, art_crop, or border_crop. Defaults to large. - pretty: bool If true, the returned JSON will be prettified. Avoid using for production code. - - Attributes: - object: str Returns the type of object it is. (card, error, etc) - id: str The scryfall id of the card. - multiverse_ids: arr The associated multiverse ids of the card. - mtgo_id: int The Magic Online id of the card. - mtgo_foil_id: int The Magic Online foil id of the card. - name: str The full name of the card. Cards with multiple faces are named with '//' as a seperator. - uri: str The Scryfall API uri for the card. - scryfall_uri: str The full Scryfall page of the card. - layout: str The image layout of the card. (normal, transform, etc) - highres_image: bool Returns True if the card has a high res image. - card.image_uris: dict All image uris of the card in various qualities. - cmc: float A float of the converted mana cost of the card. - type_line: str The full type line of the card. - oracle_text: str The official oracle text of a card. - mana_cost: str The full mana cost using shorthanded mana symbols. - colors: arr An array of strings with all colors found in the mana cost. - color_identity: arr An array of strings with all colors found in on the card itself. - legalities: dict A dictionary of all formats and their legality. - reserved: bool Returns True if the card is on the reserved list. - reprint: bool Returns True if the card has been reprinted before. - set: str The 3 letter code for the set of the card. - set_name: str The full name for the set of the card. - set_uri: str The API uri for the full set list of the card. - set_search_uri: str Same output as set_uri. - scryfall_set_uri: str The full link to the set on Scryfall. - rulings_uri: str The API uri for the rulings of the card. - prints_search_uri: str TODO: Figure out what this does. - collector_number: str The collector number of the card. - """ - def __init__(self, exact=None, fuzzy=None, _set=None, _format=None, face=None, version=None, pretty=None): - self.exact = exact - self.fuzzy = fuzzy - self.set = _set - self.format = _format - self.face = face - self.version = version - self.pretty = pretty - loop = asyncio.get_event_loop() - self.session = aiohttp.ClientSession(loop=loop) + def __init__(self, **kwargs): + self.exact = kwargs.get('exact') + self.fuzzy = kwargs.get('fuzzy') + self.set = kwargs.get('set') + self.dict = {} - async def getRequest(url, **kwargs): - async with self.session.get(url, **kwargs) as response: - return await response.json() + if self.exact is not None: + self.dict['exact'] = self.exact - self.scryfallJson = loop.run_until_complete(getRequest( - url='https://api.scryfall.com/cards/named?', - params={ - 'exact':self.exact, - 'fuzzy':self.fuzzy, - 'set':self.set, - 'format':self.format, - 'face':self.face, - 'version':self.version, - 'pretty':self.pretty - })) + if self.fuzzy is not None: + self.dict['fuzzy'] = self.fuzzy - self.session.close() + if self.set is not None: + self.dict['set'] = self.set - def object(self): - return self.scryfallJson['object'] - - def id(self): - return self.scryfallJson['id'] - - def multiverse_ids(self): - return self.scryfallJson['multiverse_ids'] - - def mtgo_id(self): - return self.scryfallJson['mtgo_id'] - - def mtgo_foil_id(self): - return self.scryfallJson['mtgo_foil_id'] - - def name(self): - return self.scryfallJson['name'] - - def uri(self): - return self.scryfallJson['uri'] - - def scryfall_uri(self): - return self.scryfallJson['scryfall_uri'] - - def layout(self): - return self.scryfallJson['layout'] - - def highres_image(self): - return self.scryfallJson['highres_image'] - - def image_uris(self): - return self.scryfallJson['image_uris'] - - def cmc(self): - return self.scryfallJson['cmc'] - - def type_line(self): - return self.scryfallJson['type_line'] - - def oracle_text(self): - return self.scryfallJson['oracle_text'] - - def mana_cost(self): - return self.scryfallJson['mana_cost'] - - def colors(self): - return self.scryfallJson['colors'] - - def color_identity(self): - return self.scryfallJson['color_identity'] - - def legalities(self): - return self.scryfallJson['legalities'] - - def reserved(self): - return self.scryfallJson['reserved'] - - def reprint(self): - return self.scryfallJson['reprint'] - - def set(self): - return self.scryfallJson['set'] - - def set_name(self): - return self.scryfallJson['set_name'] - - def set_uri(self): - return self.scryfallJson['set_uri'] - - def set_search_uri(self): - return self.scryfallJson['set_search_uri'] - - def scryfall_set_uri(self): - return self.scryfallJson['scryfall_set_uri'] - - def rulings_uri(self): - return self.scryfallJson['rulings_uri'] - - def prints_search_uri(self): - return self.scryfallJson['prints_search_uri'] - - def collector_number(self): - return self.scryfallJson['collector_number'] - - def digital(self): - return self.scryfallJson['digital'] - - def rarity(self): - return self.scryfallJson['rarity'] - - def illustration_id(self): - return self.scryfallJson['illustration_id'] - - def artist(self): - return self.scryfallJson['artist'] - - def frame(self): - return self.scryfallJson['frame'] - - def full_art(self): - return self.scryfallJson['full_art'] - - def border_color(self): - return self.scryfallJson['border_color'] - - def timeshifted(self): - return self.scryfallJson['timeshifted'] - - def colorshifted(self): - return self.scryfallJson['colorshifted'] - - def futureshifted(self): - return self.scryfallJson['futureshifted'] - - def edhrec_rank(self): - return self.scryfallJson['edhrec_rank'] - - def tix(self): - return self.scryfallJson['tix'] - - def related_uris(self): - return self.scryfallJson['related_uris'] - - def purchase_uris(self): - return self.scryfallJson['purchase_uris'] + self.args = urllib.parse.urlencode(self.dict) + self.url = 'cards/named?' + self.args + super(Named, self).__init__(self.url) diff --git a/cards/randomcard.py b/cards/randomcard.py new file mode 100644 index 0000000..ac16cba --- /dev/null +++ b/cards/randomcard.py @@ -0,0 +1,8 @@ +from .scryfall_object import ScryfallObject + + +class Random(ScryfallObject): + """This will return a random card. No parameters are passed while creating.""" + def __init__(self): + self.url = 'cards/random' + super(Random, self).__init__(self.url) diff --git a/cards/scryfall_object.py b/cards/scryfall_object.py new file mode 100644 index 0000000..7148c42 --- /dev/null +++ b/cards/scryfall_object.py @@ -0,0 +1,391 @@ +import aiohttp +import asyncio + +class ScryfallObject(object): + """ + Parameters: + format: str The data format to return: json, text, or image. Defaults to json. + face: str If using the image format and this parameter has the value back, + the back face of the card will be returned. + Will return a 404 if this card has no back face. + version: str The image version to return when using the image + format: small, normal, large, png, art_crop, or border_crop. Defaults to large. + pretty: bool If true, the returned JSON will be prettified. Avoid using for production code. + + Attributes: + object: str Returns the type of object it is. (card, error, etc) + id: str The scryfall id of the card. + multiverse_ids: arr The associated multiverse ids of the card. + mtgo_id: int The Magic Online id of the card. + mtgo_foil_id: int The Magic Online foil id of the card. + name: str The full name of the card. Cards with multiple faces are named with '//' as a seperator. + uri: str The Scryfall API uri for the card. + scryfall_uri: str The full Scryfall page of the card. + layout: str The image layout of the card. (normal, transform, etc) + highres_image: bool Returns True if the card has a high res image. + card_image_uris: dict All image uris of the card in various qualities. + cmc: float A float of the converted mana cost of the card. + type_line: str The full type line of the card. + oracle_text: str The official oracle text of a card. + mana_cost: str The full mana cost using shorthanded mana symbols. + colors: arr An array of strings with all colors found in the mana cost. + color_identity: arr An array of strings with all colors found on the card itself. + legalities: dict A dictionary of all formats and their legality. + reserved: bool Returns True if the card is on the reserved list. + reprint: bool Returns True if the card has been reprinted before. + set: str The 3 letter code for the set of the card. + set_name: str The full name for the set of the card. + set_uri: str The API uri for the full set list of the card. + set_search_uri: str Same output as set_uri. + scryfall_set_uri: str The full link to the set on Scryfall. + rulings_uri: str The API uri for the rulings of the card. + prints_search_uri: str A link to where you can begin paginating all re/prints for this card on Scryfall’s API. + collector_number: str The collector number of the card. + digital: bool Returns True if the card is the digital version. + rarity: str The rarity of the card. + illustration_id: str The related id of the card art. + artist: str The artist of the card. + frame: str The year of the card frame. + full_art: bool Returns True if the card is considered full art. + border_color: str The color of the card border. + timeshifted: bool Returns True if the card is timeshifted. + colorshifted: bool Returns True if the card is colorshifted. + futureshifted: bool Returns True if the card is futureshifted. + edhrec_rank: int The rank of the card on edhrec.com + tix: int The MTGO price of the card + related_uris: dict A dictionary of related websites for this card. + purchase_uris: dict A dictionary of links to purchase the card. + """ + def __init__(self, _url, **kwargs): + self._url = 'https://api.scryfall.com/' + _url + loop = asyncio.get_event_loop() + self.session = aiohttp.ClientSession(loop=loop) + + async def getRequest(url, **kwargs): + async with self.session.get(url, **kwargs) as response: + return await response.json() + + self.scryfallJson = loop.run_until_complete(getRequest( + url = self._url, + params={ + 'format': kwargs.get('format'), + 'face': kwargs.get('face'), + 'version': kwargs.get('version'), + 'pretty': kwargs.get('pretty') + })) + + if self.scryfallJson['object'] == 'error': + self.session.close() + raise Exception(self.scryfallJson['details']) + + self.session.close() + + def __checkForKey(self, key): + try: + return self.scryfallJson[key] + except KeyError: + return None + + def object(self): + if self.__checkForKey('object') is None: + return KeyError("This card has no associated object key.") + + return self.scryfallJson['object'] + + def id(self): + if self.__checkForKey('id') is None: + return KeyError("This card has no associated id key.") + + return self.scryfallJson['id'] + + def multiverse_ids(self): + if self.__checkForKey('multiverse_ids') is None: + return KeyError("This card has no associated multiverse id key.") + + return self.scryfallJson['multiverse_ids'] + + def mtgo_id(self): + if self.__checkForKey('mtgo_id') is None: + return KeyError("This card has no associated mtgo id key.") + + return self.scryfallJson['mtgo_id'] + + def mtgo_foil_id(self): + if self.__checkForKey('mtgo_foil_id') is None: + return KeyError("This card has no associate mtgo foil id key.") + + return self.scryfallJson['mtgo_foil_id'] + + def name(self): + if self.__checkForKey('name') is None: + return KeyError("This card has no associated name key.") + + return self.scryfallJson['name'] + + def uri(self): + if self.__checkForKey('uri') is None: + return KeyError("This card has no associated uri key.") + + return self.scryfallJson['uri'] + + def scryfall_uri(self): + if self.__checkForKey('scryfall_uri') is None: + return KeyError("This card has no associated scryfall uri key.") + + return self.scryfallJson['scryfall_uri'] + + def layout(self): + if self.__checkForKey('layout') is None: + return KeyError("This card has no associated layout key.") + + return self.scryfallJson['layout'] + + def highres_image(self): + if self.__checkForKey('highres_image') is None: + return KeyError("This card has no associated highres image key.") + + return self.scryfallJson['highres_image'] + + def image_uris(self): + if self.__checkForKey('image_uris') is None: + return KeyError("This card has no associated image uris key.") + + return self.scryfallJson['image_uris'] + + def cmc(self): + if self.__checkForKey('cmc') is None: + return KeyError("This card has no associated cmc key.") + + return self.scryfallJson['cmc'] + + def type_line(self): + if self.__checkForKey('type_line') is None: + return KeyError("This card has no associated type line key.") + + return self.scryfallJson['type_line'] + + def oracle_text(self): + if self.__checkForKey('oracle_text') is None: + return KeyError("This card has no associated oracle text key.") + + return self.scryfallJson['oracle_text'] + + def mana_cost(self): + if self.__checkForKey('mana_cost') is None: + return KeyError("This card has no associated mana cost key.") + + return self.scryfallJson['mana_cost'] + + def colors(self): + if self.__checkForKey('colors') is None: + return KeyError("This card has no associated colors key.") + + return self.scryfallJson['colors'] + + def color_identity(self): + if self.__checkForKey('color_identity') is None: + return KeyError("This card has no associated color identity key.") + + return self.scryfallJson['color_identity'] + + def legalities(self): + if self.__checkForKey('legalities') is None: + return KeyError("This card has no associated legalities key.") + + return self.scryfallJson['legalities'] + + def reserved(self): + if self.__checkForKey('reserved') is None: + return KeyError("This card has no associated reserved key key.") + + return self.scryfallJson['reserved'] + + def reprint(self): + if self.__checkForKey('reprint') is None: + return KeyError("This card has no associated reprint key.") + + return self.scryfallJson['reprint'] + + def set(self): + if self.__checkForKey('set') is None: + return KeyError("This card has no associated set key.") + + return self.scryfallJson['set'] + + def set_name(self): + if self.__checkForKey('set_name') is None: + return KeyError("This card has no associated set name key.") + + return self.scryfallJson['set_name'] + + def set_uri(self): + if self.__checkForKey('set_uri') is None: + return KeyError("This card has no associated set uri key.") + + return self.scryfallJson['set_uri'] + + def set_search_uri(self): + if self.__checkForKey('set_search_uri') is None: + return KeyError("This card has no associated set search uri key.") + + return self.scryfallJson['set_search_uri'] + + def scryfall_set_uri(self): + if self.__checkForKey('scryfall_set_uri') is None: + return KeyError("This card has no associated scryfall set uri key.") + + return self.scryfallJson['scryfall_set_uri'] + + def rulings_uri(self): + if self.__checkForKey('rulings_uri') is None: + return KeyError("This card has no associated rulings uri key.") + + return self.scryfallJson['rulings_uri'] + + def prints_search_uri(self): + if self.__checkForKey('prints_search_uri') is None: + return KeyError("This card has no associated prints search uri key.") + + return self.scryfallJson['prints_search_uri'] + + def collector_number(self): + if self.__checkForKey('collector_number') is None: + return KeyError("This card has no associated collector number key.") + + return self.scryfallJson['collector_number'] + + def digital(self): + if self.__checkForKey('digital') is None: + return KeyError("This card has no associated digital key key.") + + return self.scryfallJson['digital'] + + def rarity(self): + if self.__checkForKey('rarity') is None: + return KeyError("This card has no associated rarity key.") + + return self.scryfallJson['rarity'] + + def illustration_id(self): + if self.__checkForKey('illustration_id') is None: + return KeyError("This card has no associated illustration id key.") + + return self.scryfallJson['illustration_id'] + + def artist(self): + if self.__checkForKey('artist') is None: + return KeyError("This card has no associated artist key.") + + return self.scryfallJson['artist'] + + def frame(self): + if self.__checkForKey('frame') is None: + return KeyError("This card has no associated frame key.") + + return self.scryfallJson['frame'] + + def full_art(self): + if self.__checkForKey('') is None: + return KeyError("This card has no associated full art key key.") + + return self.scryfallJson['full_art'] + + def border_color(self): + if self.__checkForKey('border_color') is None: + return KeyError("This card has no associated border color key.") + + return self.scryfallJson['border_color'] + + def timeshifted(self): + if self.__checkForKey('timeshifted') is None: + return KeyError("This card has no associated timeshifted key key.") + + return self.scryfallJson['timeshifted'] + + def colorshifted(self): + if self.__checkForKey('colorshifted') is None: + return KeyError("This card has no associated colorshifted key key.") + + return self.scryfallJson['colorshifted'] + + def futureshifted(self): + if self.__checkForKey('futureshifted') is None: + return KeyError("This card has no associated futureshifted key key.") + + return self.scryfallJson['futureshifted'] + + def edhrec_rank(self): + if self.__checkForKey('edhrec_rank') is None: + return KeyError("This card has no associated edhrec rank key.") + + return self.scryfallJson['edhrec_rank'] + + def currency(self, mode): + modes = ['usd', 'eur', 'tix'] + if mode not in modes: + return KeyError("{} is not a key.".format(mode)) + + if self.__checkForKey(mode) is None: + return KeyError("This card has no associated currency {} key".format(mode)) + + return self.scryfallJson[mode] + + def related_uris(self): + if self.__checkForKey('related_uris') is None: + return KeyError("This card has no associated related uris key.") + + return self.scryfallJson['related_uris'] + + def purchase_uris(self): + if self.__checkForKey('purchase_uris') is None: + return KeyError("This card has no associated purchase uris key.") + + return self.scryfallJson['purchase_uris'] + + def life_modifier(self): + if self.__checkForKey('life_modifier') is None: + return KeyError("This card has no associated life modifier key.") + + return self.scryfallJson['life_modifier'] + + def hand_modifier(self): + if self.__checkForKey('hand_modifier') is None: + return KeyError("This card has no associated hand modifier key.") + + return self.scryfallJson['hand_modifier'] + + def color_indicator(self): + if self.__checkForKey('color_indicator') is None: + return KeyError("This card has no associated color indicator key.") + + return self.scryfallJson['color_indicator'] + + def all_parts(self): + if self.__checkForKey('all_parts') is None: + return KeyError("This card has no associated all parts key.") + + return self.scryfallJson['all_parts'] + + def card_faces(self): + if self.__checkForKey('card_faces') is None: + return KeyError("This card has no associated card faces key.") + + return self.scryfallJson['card_faces'] + + def watermark(self): + if self.__checkForKey('watermark') is None: + return KeyError("This card has no associated watermark key.") + + return self.scryfallJson['watermark'] + + def story_spotlight_number(self): + if self.__checkForKey('story_spotlight_number') is None: + return KeyError("This card has no associated story spotlight number key.") + + return self.scryfallJson['story_spotlight_number'] + + def story_spotlight_uri(self): + if self.__checkForKey('story_spotlight_uri') is None: + return KeyError("This card has no associated story spotlight uri key.") + + return self.scryfallJson['story_spotlight_uri'] diff --git a/requirements.txt b/requirements.txt index e84b373..c47b89f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ +aiohttp #Requirements for PyPI diff --git a/tests/cards_unit_test.py b/tests/cards_unit_test.py new file mode 100644 index 0000000..94fef8a --- /dev/null +++ b/tests/cards_unit_test.py @@ -0,0 +1,7 @@ +import unittest +import cards + +class TestObjectCreation(unittest.TestCase): + + def testRandomCard(self): + pass