diff --git a/simyan/sqlite_cache.py b/simyan/sqlite_cache.py index 70a5bda..ce87c93 100644 --- a/simyan/sqlite_cache.py +++ b/simyan/sqlite_cache.py @@ -7,6 +7,8 @@ __all__ = ["SQLiteCache"] import json import sqlite3 +from collections.abc import Generator +from contextlib import contextmanager from datetime import datetime, timedelta, timezone from pathlib import Path from typing import Any, Optional @@ -23,13 +25,37 @@ class SQLiteCache: """ def __init__(self, path: Optional[Path] = None, expiry: Optional[int] = 14): - self.expiry = expiry - self.connection = sqlite3.connect(path or get_cache_root() / "cache.sqlite") - self.connection.row_factory = sqlite3.Row - - self.connection.execute("CREATE TABLE IF NOT EXISTS queries (query, response, query_date);") + self._db_path = path or (get_cache_root() / "cache.sqlite") + self._expiry = expiry + self.initialize() self.cleanup() + @contextmanager + def _connect(self) -> Generator[sqlite3.Connection]: + conn = None + try: + conn = sqlite3.connect(self._db_path) + conn.row_factory = sqlite3.Row + conn.execute("PRAGMA foreign_keys = ON") + yield conn + finally: + if conn: + conn.close() + + def initialize(self) -> None: + """Create the cache table if it doesn't exist.""" + with self._connect() as conn: + conn.execute( + """ + CREATE TABLE IF NOT EXISTS cache ( + query TEXT NOT NULL PRIMARY KEY, + response TEXT, + timestamp TIMESTAMP + ); + """ + ) + conn.commit() + def select(self, query: str) -> dict[str, Any]: """Retrieve data from the cache database. @@ -39,17 +65,16 @@ def select(self, query: str) -> dict[str, Any]: Returns: Empty dict or select results. """ - if self.expiry: - expiry = datetime.now(tz=timezone.utc).astimezone().date() - timedelta(days=self.expiry) - cursor = self.connection.execute( - "SELECT * FROM queries WHERE query = ? and query_date > ?;", - (query, expiry.isoformat()), - ) - else: - cursor = self.connection.execute("SELECT * FROM queries WHERE query = ?;", (query,)) - if results := cursor.fetchone(): - return json.loads(results["response"]) - return {} + with self._connect() as conn: + if self._expiry: + expiry = datetime.now(tz=timezone.utc) - timedelta(days=self._expiry) + row = conn.execute( + "SELECT * FROM cache WHERE query = ? and timestamp > ?;", + (query, expiry.isoformat()), + ).fetchone() + else: + row = conn.execute("SELECT * FROM cache WHERE query = ?;", (query,)).fetchone() + return json.loads(row["response"]) if row else {} def insert(self, query: str, response: dict[str, Any]) -> None: """Insert data into the cache database. @@ -58,15 +83,12 @@ def insert(self, query: str, response: dict[str, Any]) -> None: query: Url string used as key. response: Response dict from url. """ - self.connection.execute( - "INSERT INTO queries (query, response, query_date) VALUES (?, ?, ?);", - ( - query, - json.dumps(response), - datetime.now(tz=timezone.utc).astimezone().date().isoformat(), - ), - ) - self.connection.commit() + with self._connect() as conn: + conn.execute( + "INSERT INTO cache (query, response, timestamp) VALUES (?, ?, ?);", + (query, json.dumps(response), datetime.now(tz=timezone.utc).isoformat()), + ) + conn.commit() def delete(self, query: str) -> None: """Remove entry from the cache with the provided url. @@ -74,13 +96,15 @@ def delete(self, query: str) -> None: Args: query: Url string used as key. """ - self.connection.execute("DELETE FROM queries WHERE query = ?;", (query,)) - self.connection.commit() + with self._connect() as conn: + conn.execute("DELETE FROM cache WHERE query = ?;", (query,)) + conn.commit() def cleanup(self) -> None: """Remove all expired entries from the cache database.""" - if not self.expiry: + if not self._expiry: return - expiry = datetime.now(tz=timezone.utc).astimezone().date() - timedelta(days=self.expiry) - self.connection.execute("DELETE FROM queries WHERE query_date < ?;", (expiry.isoformat(),)) - self.connection.commit() + expiry = datetime.now(tz=timezone.utc) - timedelta(days=self._expiry) + with self._connect() as conn: + conn.execute("DELETE FROM cache WHERE timestamp < ?;", (expiry.isoformat(),)) + conn.commit() diff --git a/tests/cache.sqlite b/tests/cache.sqlite index 0ea6a00..61e9de6 100644 Binary files a/tests/cache.sqlite and b/tests/cache.sqlite differ diff --git a/tests/characters_test.py b/tests/characters_test.py index 3c4a95e..6edb25a 100644 --- a/tests/characters_test.py +++ b/tests/characters_test.py @@ -23,8 +23,8 @@ def test_get_character(session: Comicvine) -> None: assert len(result.enemies) == 150 assert len(result.enemy_teams) == 25 assert len(result.friendly_teams) == 17 - assert len(result.friends) == 233 - assert len(result.issues) == 1714 + assert len(result.friends) == 232 + assert len(result.issues) == 1732 assert len(result.powers) == 28 assert len(result.story_arcs) == 0 assert len(result.teams) == 21 @@ -49,7 +49,7 @@ def test_list_characters(session: Comicvine) -> None: assert result.date_of_birth is None assert result.first_issue.id == 38445 assert result.gender == 1 - assert result.issue_count == 1714 + assert result.issue_count == 1732 assert result.name == "Kyle Rayner" assert result.origin.id == 4 assert result.publisher.id == 10 diff --git a/tests/concepts_test.py b/tests/concepts_test.py index d9e3bd8..cb6fedf 100644 --- a/tests/concepts_test.py +++ b/tests/concepts_test.py @@ -18,7 +18,7 @@ def test_get_concept(session: Comicvine) -> None: assert result is not None assert result.id == 41148 - assert len(result.issues) == 2589 + assert len(result.issues) == 2653 assert len(result.volumes) == 1 @@ -38,7 +38,7 @@ def test_list_concepts(session: Comicvine) -> None: assert str(result.api_url) == "https://comicvine.gamespot.com/api/concept/4015-41148/" assert result.date_added == datetime(2008, 6, 6, 11, 27, 52) assert result.first_issue.id == 144069 - assert result.issue_count == 2589 + assert result.issue_count == 2653 assert result.name == "Green Lantern" assert str(result.site_url) == "https://comicvine.gamespot.com/green-lantern/4015-41148/" assert result.start_year == 1940 diff --git a/tests/creators_test.py b/tests/creators_test.py index b173ff1..f54275e 100644 --- a/tests/creators_test.py +++ b/tests/creators_test.py @@ -18,10 +18,10 @@ def test_get_creator(session: Comicvine) -> None: assert result is not None assert result.id == 40439 - assert len(result.characters) == 309 - assert len(result.issues) == 1608 - assert len(result.story_arcs) == 23 - assert len(result.volumes) == 595 + assert len(result.characters) == 320 + assert len(result.issues) == 1640 + assert len(result.story_arcs) == 27 + assert len(result.volumes) == 604 def test_get_creator_fail(session: Comicvine) -> None: diff --git a/tests/items_test.py b/tests/items_test.py index 01f7d77..8d80825 100644 --- a/tests/items_test.py +++ b/tests/items_test.py @@ -18,9 +18,9 @@ def test_get_item(session: Comicvine) -> None: assert result is not None assert result.id == 41361 - assert len(result.issues) == 3308 - assert len(result.story_arcs) == 454 - assert len(result.volumes) == 993 + assert len(result.issues) == 3336 + assert len(result.story_arcs) == 457 + assert len(result.volumes) == 1007 def test_get_item_fail(session: Comicvine) -> None: @@ -39,7 +39,7 @@ def test_list_items(session: Comicvine) -> None: assert str(result.api_url) == "https://comicvine.gamespot.com/api/object/4055-41361/" assert result.date_added == datetime(2008, 6, 6, 11, 27, 50) assert result.first_issue.id == 123898 - assert result.issue_count == 3308 + assert result.issue_count == 3336 assert result.name == "Green Power Ring" assert str(result.site_url) == "https://comicvine.gamespot.com/green-power-ring/4055-41361/" assert result.start_year == 1940 diff --git a/tests/origins_test.py b/tests/origins_test.py index 5e11224..787a9c5 100644 --- a/tests/origins_test.py +++ b/tests/origins_test.py @@ -17,7 +17,7 @@ def test_get_origin(session: Comicvine) -> None: assert result.id == 1 assert result.character_set is None - assert len(result.characters) == 4399 + assert len(result.characters) == 4480 assert len(result.profiles) == 0 diff --git a/tests/powers_test.py b/tests/powers_test.py index dbbf99c..e0d1d69 100644 --- a/tests/powers_test.py +++ b/tests/powers_test.py @@ -18,7 +18,7 @@ def test_get_power(session: Comicvine) -> None: assert result is not None assert result.id == 1 - assert len(result.characters) == 8310 + assert len(result.characters) == 8367 def test_get_power_fail(session: Comicvine) -> None: diff --git a/tests/publishers_test.py b/tests/publishers_test.py index aedcc5f..c3fa1f1 100644 --- a/tests/publishers_test.py +++ b/tests/publishers_test.py @@ -14,14 +14,14 @@ def test_get_publisher(session: Comicvine) -> None: """Test the get_publisher function with a valid id.""" - result = session.get_publisher(publisher_id=10) + result = session.get_publisher(publisher_id=364) assert result is not None - assert result.id == 10 + assert result.id == 364 - assert len(result.characters) == 24149 - assert len(result.story_arcs) == 895 - assert len(result.teams) == 1869 - assert len(result.volumes) == 9416 + assert len(result.characters) == 775 + assert len(result.story_arcs) == 38 + assert len(result.teams) == 42 + assert len(result.volumes) == 4692 def test_get_publisher_fail(session: Comicvine) -> None: diff --git a/tests/teams_test.py b/tests/teams_test.py index 4cf11ec..a16932b 100644 --- a/tests/teams_test.py +++ b/tests/teams_test.py @@ -22,7 +22,7 @@ def test_get_team(session: Comicvine) -> None: assert len(result.friends) == 10 assert len(result.issues) == 120 assert len(result.issues_disbanded_in) == 1 - assert len(result.members) == 18 + assert len(result.members) == 19 assert len(result.story_arcs) == 0 assert len(result.volumes) == 65 @@ -42,7 +42,7 @@ def test_list_teams(session: Comicvine) -> None: assert str(result.api_url) == "https://comicvine.gamespot.com/api/team/4060-50163/" assert result.issue_count == 0 - assert result.member_count == 18 + assert result.member_count == 19 assert result.date_added == datetime(2008, 6, 6, 11, 27, 45) assert result.first_issue.id == 119950 assert result.name == "Blue Lantern Corps" diff --git a/tests/volumes_test.py b/tests/volumes_test.py index 5d8a791..a98d79c 100644 --- a/tests/volumes_test.py +++ b/tests/volumes_test.py @@ -19,7 +19,7 @@ def test_get_volume(session: Comicvine) -> None: assert result.id == 18216 assert len(result.characters) == 368 - assert len(result.concepts) == 19 + assert len(result.concepts) == 10 assert len(result.creators) == 95 assert len(result.issues) == 67 assert len(result.locations) == 48