Created
June 30, 2018 07:21
-
-
Save 5minho/525d5258804179ec3e1efb8433ea8eea to your computer and use it in GitHub Desktop.
Book id SqliteCache
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import sqlite3 | |
import logging | |
import re | |
from functools import wraps | |
from datetime import datetime, timedelta | |
from book_review_scraper.exceptions import BookIdCacheMissError, BookIdCacheExpiredError, ISBNError | |
class SqliteCache: | |
connection = None | |
def __init__(self): | |
self.logger = logging.getLogger(__name__) | |
root_dir = os.path.dirname(os.path.abspath(__file__)) | |
self.path = os.path.join(root_dir, '.cache') | |
self.logger.debug('Instantiated with cache_db path as {path}'.format(path=self.path)) | |
self.expire_time = timedelta(days=7) | |
# prepare the directory for the cache sqlite db | |
if not os.path.exists(self.path): | |
os.mkdir(self.path) | |
self.logger.debug('Successfully created the storage path for {path}'.format(path=self.path)) | |
def _get_conn(self, cache_table): | |
if self.connection: | |
return self.connection | |
cache_db_path = os.path.join(self.path, 'cache.sqlite') | |
conn = sqlite3.connect(cache_db_path, timeout=60, | |
detect_types=sqlite3.PARSE_DECLTYPES | sqlite3.PARSE_COLNAMES) | |
self.logger.debug('Connected to {path}'.format(path=cache_db_path)) | |
with conn: | |
cur = conn.cursor() | |
cur.execute("CREATE TABLE IF NOT EXISTS {} " | |
"(isbn INTEGER PRIMARY KEY, " | |
"book_id INTEGER NOT NULL, " | |
"updated TIMESTAMP )".format(cache_table)) | |
self.logger.debug('Ran the create table') | |
self.connection = conn | |
return self.connection | |
def get(self, cache_table, isbn): | |
with self._get_conn(cache_table) as conn: | |
cur = conn.cursor() | |
cur.execute('SELECT book_id, updated ' | |
'FROM {} ' | |
'WHERE isbn = ?'.format(cache_table), (isbn,)) | |
rows = cur.fetchone() | |
if not rows: | |
self.logger.debug(f'{cache_table}, {isbn} cache miss ') | |
raise BookIdCacheMissError(table=cache_table, isbn=isbn) | |
updated = rows[1] | |
now = datetime.now() | |
if updated + self.expire_time < now: | |
raise BookIdCacheExpiredError(table=cache_table, isbn=isbn) | |
return rows[0] | |
def set(self, cache_table, isbn, book_id): | |
with self._get_conn(cache_table) as conn: | |
cur = conn.cursor() | |
try: | |
cur.execute('INSERT INTO {} ' | |
'(isbn, book_id, updated) ' | |
'VALUES (?, ?, ?)'.format(cache_table), (isbn, book_id, datetime.now())) | |
self.logger.debug(f'{cache_table}, insert {isbn} : {book_id}') | |
except sqlite3.IntegrityError: | |
self.update(cache_table, isbn, book_id) | |
def remove(self, cache_table, isbn): | |
with self._get_conn(cache_table) as conn: | |
conn.cursor().execute('DELETE FROM {}' | |
' WHERE isbn = ?'.format(cache_table), (isbn,)) | |
self.logger.debug(f'{cache_table}, delete {isbn}') | |
def update(self, cache_table, isbn, book_id): | |
with self._get_conn(cache_table) as conn: | |
cur = conn.cursor() | |
cur.execute('UPDATE {} ' | |
'SET book_id=?, updated=? ' | |
'WHERE isbn = ?'.format(cache_table), (book_id, datetime.now(), isbn)) | |
self.logger.debug(f'{cache_table}, update {isbn} : {book_id}') | |
def clear(self, cache_table): | |
with self._get_conn(cache_table) as conn: | |
conn.cursor().execute('DELETE FROM {}'.format(cache_table)) | |
self.logger.debug(f'{cache_table}, clear') | |
def __del__(self): | |
self.logger.debug('Cleans up the object by destroying the sqlite connection') | |
if self.connection: | |
self.connection.close() | |
cache = SqliteCache() | |
def book_id(cache_table): | |
def decorator(fn): | |
@wraps(fn) | |
def wrapped(*args, **kwargs): | |
isbn13 = args[1] | |
if re.match('[\d+]{13}', str(isbn13)) is None: | |
raise ISBNError(bookstore=cache_table, isbn13=isbn13) | |
try: | |
book_id = cache.get(cache_table, isbn13) | |
return book_id | |
except (BookIdCacheMissError, BookIdCacheExpiredError): | |
review_info = fn(*args, **kwargs) | |
cache.set(cache_table, isbn13, review_info.bid) | |
return review_info | |
return wrapped | |
return decorator |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment