diff --git a/.gitignore b/.gitignore index 1dbc687..1a70026 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +*.sqlite +*.db + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/README.rst b/README.rst index 3d4975f..292390d 100644 --- a/README.rst +++ b/README.rst @@ -2,6 +2,7 @@ marvelous ========= Marvel API python wrapper. +[Read the project documentation](http://marvelous.readthedocs.org/en/latest/) Contributing ------------ @@ -41,23 +42,49 @@ complete Marvel pull list, excluding any series the user doesn't want. 'format': "comic", 'formatType': "comic", 'noVariants': True, - 'dateDescriptor': "thisWeek"}), + 'dateDescriptor': "thisWeek", + 'limit': 100}), key=lambda comic: comic.title) - # Grab the sale date of any of the comics for the folder name - directory = pulls[0].dates.on_sale.strftime('%m-%d') - - # If there's no folder by that name, create one - if not os.path.exists(directory): - os.makedirs(directory) - - # Create a pulls.txt file in that folder - with open(directory + '/pulls.txt', 'w') as pull_checklist: - # Check each comic that came out this week - for comic in pulls: - # If this series isn't in my ignore list - if comic.series.id not in IGNORE: - # Write a line to the file with the name of the issue, and the - # id of the series incase I want to add it to my ignore list - pull_checklist.write('{} (series #{})\n'.format( - comic.title.encode('utf-8'), comic.series.id)) + # Grab the sale date of any of the comics for the current week + week = pulls[0].dates.on_sale.strftime('%m/%d') + + print("New comics for the week of {}:".format(week)) + # Check each comic that came out this week + for comic in pulls: + # If this series isn't in my ignore list + if comic.series.id not in IGNORE: + # Write a line to the file with the name of the issue, and the + # id of the series incase I want to add it to my ignore list + print('- {} (series #{})'.format(comic.title, comic.series.id)) + + +Example output:: + + New comics for the week of 11/09: + - All-New X-Men (2015) #15 (series #20622) + - Amazing Spider-Man: Renew Your Vows (2016) #1 (series #22545) + - Black Panther: World of Wakanda (2016) #1 (series #22549) + - Captain America: Steve Rogers (2016) #7 (series #21098) + - Daredevil (2015) #13 (series #20780) + - Dark Tower: The Drawing of the Three - The Sailor (2016) #2 (series #19377) + - Deadpool: Back in Black (2016) #3 (series #21489) + - Doctor Strange And The Sorcerers Supreme (2016) #2 (series #22560) + - Gwenpool (2016) #8 (series #21490) + - Han Solo (2016) #5 (series #19711) + - Invincible Iron Man (2016) #1 (series #22928) + - Max Ride: Final Flight (2016) #3 (series #22197) + - Mosaic (2016) #2 (series #20818) + - Ms. Marvel (2015) #13 (series #20615) + - Old Man Logan (2016) #13 (series #20617) + - Power Man and Iron Fist (2016) #10 (series #21122) + - Prowler (2016) #2 (series #22535) + - Solo (2016) #2 (series #22441) + - Spider-Gwen (2015) #14 (series #20505) + - Spider-Man/Deadpool (2016) #11 (series #19679) + - Star Wars: The Force Awakens Adaptation (2016) #6 (series #21493) + - The Avengers (2016) #1.1 (series #22966) + - The Clone Conspiracy (2016) #2 (series #22654) + - Thunderbolts (2016) #7 (series #20884) + - Uncanny Avengers (2015) #16 (series #20621) + - Uncanny X-Men (2016) #15 (series #20612) diff --git a/marvelous/__init__.py b/marvelous/__init__.py index 84f4857..130206a 100644 --- a/marvelous/__init__.py +++ b/marvelous/__init__.py @@ -1,35 +1,12 @@ -from session import Session -from exceptions import AuthenticationError, LibraryError +from . import session, exceptions +from .sqlite_cache import SqliteCache -def api(public_key=None, private_key=None, cache=False): +def api(public_key=None, private_key=None, cache=None): if public_key is None: - raise AuthenticationError("Missing public_key.") + raise exceptions.AuthenticationError("Missing public_key.") if private_key is None: - raise AuthenticationError("Missing private_key.") + raise exceptions.AuthenticationError("Missing private_key.") - if cache: - try: - from requests_cache import CachedSession - - cache_defaults = { - 'backend': 'sqlite', - 'expire_after': 60*60*24, # 24 hours - 'ignored_parameters': ['hash', 'ts', 'apikey'] - } - - # Override the name and kwargs of the cache by passing a dict into - # the cache kwarg - if isinstance(cache, dict): - cache_defaults.update(cache) - - cache = CachedSession( - cache_defaults.get('name', 'marvelous'), **cache_defaults) - except: - raise LibraryError( - "Marvelous only supports cache with requests-cache >= 0.4.11, " - "not yet on pypi. To install the newest requests-cache, run " - "`pip install requests-cache`.") - - return Session(public_key, private_key, cached_requests=cache) + return session.Session(public_key, private_key, cache=cache) diff --git a/marvelous/comic.py b/marvelous/comic.py index 00b30cd..998a65b 100644 --- a/marvelous/comic.py +++ b/marvelous/comic.py @@ -1,11 +1,11 @@ from marshmallow import Schema, fields, pre_load, post_load -from dates import DatesSchema -from series import SeriesSchema + +from . import dates, series class Comic(): def __init__(self, **kwargs): - for k, v in kwargs.iteritems(): + for k, v in kwargs.items(): setattr(self, k, v) @@ -27,11 +27,11 @@ class ComicSchema(Schema): # textObjects # resourceURI # urls - series = fields.Nested(SeriesSchema) + series = fields.Nested(series.SeriesSchema) # variants # collections # collectedIssues - dates = fields.Nested(DatesSchema) + dates = fields.Nested(dates.DatesSchema) # prices # thumbnail # images diff --git a/marvelous/comics_list.py b/marvelous/comics_list.py index bd6d50d..dd15f5e 100644 --- a/marvelous/comics_list.py +++ b/marvelous/comics_list.py @@ -1,7 +1,6 @@ import itertools -import comic -import exceptions +from . import comic, exceptions class ComicsList(): diff --git a/marvelous/series.py b/marvelous/series.py index 5dd79a8..74edb15 100644 --- a/marvelous/series.py +++ b/marvelous/series.py @@ -1,6 +1,6 @@ from marshmallow import Schema, fields, pre_load, post_load -import comics_list +from . import comics_list class Series(): @@ -8,7 +8,7 @@ def __init__(self, **kwargs): if 'response' not in kwargs: kwargs['response'] = None - for k, v in kwargs.iteritems(): + for k, v in kwargs.items(): setattr(self, k, v) def comics(self, params): diff --git a/marvelous/session.py b/marvelous/session.py index 714c6a4..16b54af 100644 --- a/marvelous/session.py +++ b/marvelous/session.py @@ -2,26 +2,20 @@ import hashlib import requests -import exceptions -import comics_list -import series +from . import exceptions, comics_list, series class Session(): api_url = "http://gateway.marvel.com:80/v1/public/{}" def __init__( - self, public_key, private_key, cached_requests=None, + self, public_key, private_key, cache=None, print_calls=False): self.public_key = public_key self.private_key = private_key self.print_calls = print_calls - - if cached_requests: - self.requests = cached_requests - else: - self.requests = requests + self.cache = cache def call(self, endpoint, params=None): if params is None: @@ -29,9 +23,9 @@ def call(self, endpoint, params=None): now_string = datetime.datetime.now().strftime('%Y-%m-%d%H:%M:%S') auth_hash = hashlib.md5() - auth_hash.update(now_string) - auth_hash.update(self.private_key) - auth_hash.update(self.public_key) + auth_hash.update(now_string.encode('utf-8')) + auth_hash.update(self.private_key.encode('utf-8')) + auth_hash.update(self.public_key.encode('utf-8')) params['hash'] = auth_hash.hexdigest() params['apikey'] = self.public_key @@ -39,17 +33,26 @@ def call(self, endpoint, params=None): url = self.api_url.format('/'.join([str(e) for e in endpoint])) - response = self.requests.get(url, params=params) + if self.cache: + cached_response = self.cache.get(url) + + if cached_response: + return cached_response + + response = requests.get(url, params=params) if self.print_calls: - print response.url + print(response.url) - response = response.json() + data = response.json() - if 'message' in response: + if 'message' in data: raise exceptions.ApiError(response['message']) - return response + if self.cache and response.status_code == 200: + self.cache.store(url, data) + + return data def comics(self, params=None): if params is None: diff --git a/marvelous/sqlite_cache.py b/marvelous/sqlite_cache.py new file mode 100644 index 0000000..906dd8f --- /dev/null +++ b/marvelous/sqlite_cache.py @@ -0,0 +1,24 @@ +import json +import sqlite3 + + +class SqliteCache: + def __init__(self, db_name="marvelous_cache.db"): + self.con = sqlite3.connect(db_name) + self.cur = self.con.cursor() + self.cur.execute("CREATE TABLE IF NOT EXISTS responses (key, json)") + + def get(self, key): + self.cur.execute("SELECT json FROM responses WHERE key = ?", (key,)) + result = self.cur.fetchone() + + if result: + return json.loads(result[0]) + + return None + + def store(self, key, value): + self.cur.execute( + "INSERT INTO responses(key, json) VALUES(?, ?)", + (key, json.dumps(value))) + self.con.commit() diff --git a/requirements.txt b/requirements.txt index 0c6a3b0..dcee63c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ marshmallow==2.6.0 nose==1.3.7 requests==2.9.1 -requests-cache==0.4.12 sphinx-rtd-theme==0.1.9 diff --git a/tests/comic_test.py b/tests/comic_test.py index 0f6a983..ad24692 100644 --- a/tests/comic_test.py +++ b/tests/comic_test.py @@ -9,8 +9,8 @@ def setUp(self): pub = os.getenv('PUBLIC_KEY', 'pub') priv = os.getenv('PRIVATE_KEY', 'priv') self.m = marvelous.api( - public_key=pub, private_key=priv, cache={ - 'name': 'testing_mock', 'expire_after': None}) + public_key=pub, private_key=priv, + cache=marvelous.SqliteCache("testing_mock.db")) def test_pulls_verbose(self): week = self.m.comics({