Skip to content

Commit f1379cb

Browse files
committed
(Issue seanboyce#59 and Issue seanboyce#54) Allow scanning a book without Author
Two Major Changes: * Allow adding using Scan or by ISBN for books without Author seanboyce#59 * ISBN auto-add feature makes two calls to openLibrary, only one is necessary seanboyce#54
1 parent d87b8fc commit f1379cb

2 files changed

Lines changed: 89 additions & 61 deletions

File tree

tests/routers/books/test_book_metadata_client.py

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from pytest import MonkeyPatch
44
import responses
5+
import pytest
56

67
from ubiblio.routers.books.book_metadata_client import BookMetadataClient
78

@@ -100,30 +101,26 @@ def test_open_library_by_isbn_should_return_book_metadata(self, monkeypatch: Mon
100101

101102
responses.add(
102103
responses.GET,
103-
f"{BookMetadataClient.OPEN_LIBRARY_API}isbn/{isbn}.json",
104+
f"{BookMetadataClient.OPEN_LIBRARY_API}/api/books?bibkeys=9780123456789&format=json&jscmd=details",
104105
json={
105-
"title": "Test Title",
106-
"authors": [
107-
{
108-
"key": "/authors/OL1607920A"
106+
f"{isbn}": {
107+
"details": {
108+
"title": "Test Title",
109+
"authors": [
110+
{
111+
"key": "/authors/OL1607920A",
112+
"name": "A. Reader"
113+
}
114+
],
115+
"description": {
116+
"value": "A fine book.",
117+
}
109118
}
110-
],
111-
"description": {
112-
"value": "A fine book.",
113119
}
114120
},
115121
status=200,
116122
)
117123

118-
responses.add(
119-
responses.GET,
120-
f"{BookMetadataClient.OPEN_LIBRARY_API}/authors/OL1607920A.json",
121-
json={
122-
"personal_name": "A. Reader",
123-
},
124-
status=200,
125-
)
126-
127124
client = BookMetadataClient()
128125

129126
# Act
@@ -135,67 +132,103 @@ def test_open_library_by_isbn_should_return_book_metadata(self, monkeypatch: Mon
135132
"Author": "A. Reader",
136133
"Summary": "A fine book.",
137134
}
138-
assert len(responses.calls) == 2
139-
assert responses.calls[0].request.url == f"{BookMetadataClient.OPEN_LIBRARY_API}isbn/{isbn}.json"
140-
assert responses.calls[1].request.url == f"{BookMetadataClient.OPEN_LIBRARY_API}/authors/OL1607920A.json"
135+
assert len(responses.calls) == 1
136+
assert responses.calls[0].request.url == f"{BookMetadataClient.OPEN_LIBRARY_API}/api/books?bibkeys={isbn}&format=json&jscmd=details"
141137

142138
@responses.activate
143-
def test_open_library_by_isbn_should_return_error_when_response_is_not_ok(self, monkeypatch: MonkeyPatch) -> None:
139+
@pytest.mark.parametrize("arthur_value", [[], [{}], [{"key": "/authors/OL1607920A"}]])
140+
def test_open_library_by_isbn_should_return_book_metadata_even_if_no_author_is_found(self, arthur_value: list[dict[str, str]]) -> None:
144141
# Arrange
145142
isbn = "9780123456789"
143+
146144
responses.add(
147145
responses.GET,
148-
f"{BookMetadataClient.OPEN_LIBRARY_API}isbn/{isbn}.json",
149-
json={},
150-
status=404,
146+
f"{BookMetadataClient.OPEN_LIBRARY_API}/api/books?bibkeys={isbn}&format=json&jscmd=details",
147+
json={
148+
f"{isbn}": {
149+
"details": {
150+
"title": "Test Title",
151+
"authors": arthur_value,
152+
"description": {
153+
"value": "A fine book.",
154+
}
155+
}
156+
}
157+
},
158+
status=200,
151159
)
160+
152161
client = BookMetadataClient()
153162

154163
# Act
155164
book, status = client.open_library_by_isbn(isbn)
156165

157166
# Assert
158-
assert status == 404
159-
assert book == {}
167+
assert status == 200
168+
assert book == {
169+
"Title": "Test Title",
170+
"Summary": "A fine book.",
171+
"Author": "",
172+
}
173+
assert len(responses.calls) == 1
174+
assert responses.calls[0].request.url == f"{BookMetadataClient.OPEN_LIBRARY_API}/api/books?bibkeys={isbn}&format=json&jscmd=details"
160175

161176
@responses.activate
162-
def test_open_library_by_isbn_should_still_return_book_metadata_when_author_response_is_not_ok(self, monkeypatch: MonkeyPatch) -> None:
177+
def test_open_library_by_isbn_should_return_book_metadata_even_if_no_description_is_found(self, monkeypatch: MonkeyPatch) -> None:
163178
# Arrange
164179
isbn = "9780123456789"
180+
165181
responses.add(
166182
responses.GET,
167-
f"{BookMetadataClient.OPEN_LIBRARY_API}isbn/{isbn}.json",
183+
f"{BookMetadataClient.OPEN_LIBRARY_API}/api/books?bibkeys={isbn}&format=json&jscmd=details",
168184
json={
169-
"title": "Test Title",
170-
"authors": [
171-
{
172-
"key": "/authors/OL1607920A"
185+
f"{isbn}": {
186+
"details": {
187+
"title": "Test Title",
173188
}
174-
],
175-
"description": {
176-
"value": "A fine book.",
177189
}
178190
},
179191
status=200,
180192
)
193+
194+
client = BookMetadataClient()
195+
196+
# Act
197+
book, status = client.open_library_by_isbn(isbn)
198+
199+
# Assert
200+
assert status == 200
201+
assert book == {
202+
"Title": "Test Title",
203+
"Summary": "",
204+
"Author": "",
205+
}
206+
assert len(responses.calls) == 1
207+
assert responses.calls[0].request.url == f"{BookMetadataClient.OPEN_LIBRARY_API}/api/books?bibkeys={isbn}&format=json&jscmd=details"
208+
209+
@responses.activate
210+
def test_open_library_by_isbn_should_return_error_when_response_is_not_ok(self) -> None:
211+
# Arrange
212+
isbn = "9780123456789"
181213
responses.add(
182214
responses.GET,
183-
f"{BookMetadataClient.OPEN_LIBRARY_API}/authors/OL1607920A.json",
215+
f"{BookMetadataClient.OPEN_LIBRARY_API}/api/books?bibkeys={isbn}&format=json&jscmd=details",
184216
json={},
185217
status=404,
186218
)
187219
client = BookMetadataClient()
188220

189221
# Act
190222
book, status = client.open_library_by_isbn(isbn)
223+
191224
# Assert
192-
assert status == 200
193-
assert book == {"Summary": "A fine book.", "Title": "Test Title"}
225+
assert status == 404
226+
assert book == {}
194227

195228

196229
class TestBookMetadataClientOpenWikiByIsbn:
197230
@responses.activate
198-
def test_open_wiki_by_isbn_should_return_book_metadata(self, monkeypatch: MonkeyPatch) -> None:
231+
def test_open_wiki_by_isbn_should_return_book_metadata(self) -> None:
199232
# Arrange
200233
isbn = "9780123456789"
201234
responses.add(
@@ -228,9 +261,10 @@ def test_open_wiki_by_isbn_should_return_book_metadata(self, monkeypatch: Monkey
228261
"Summary": "",
229262
}
230263
assert len(responses.calls) == 1
231-
264+
assert responses.calls[0].request.url == f"{BookMetadataClient.OPEN_WIKI_API}isbn/{isbn}.json"
265+
232266
@responses.activate
233-
def test_open_wiki_by_isbn_should_return_error_when_response_is_not_ok(self, monkeypatch: MonkeyPatch) -> None:
267+
def test_open_wiki_by_isbn_should_return_error_when_response_is_not_ok(self) -> None:
234268
# Arrange
235269
isbn = "9780123456789"
236270
responses.add(

ubiblio/routers/books/book_metadata_client.py

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
class BookMetadataClient:
1010
GOOGLE_BOOKS_API = "https://www.googleapis.com/books/v1/volumes"
11-
OPEN_LIBRARY_API = "https://openlibrary.org/"
11+
OPEN_LIBRARY_API = "https://openlibrary.org"
1212
OPEN_WIKI_API = "https://en.wikipedia.org/api/rest_v1/data/citation/mediawiki/"
1313
USER_AGENT = "ubiblio_bot/1.0 (https://github.com/seanboyce/ubiblio;)"
1414

@@ -23,36 +23,30 @@ def google_books_by_isbn(self, isbn: str) -> tuple[dict[str, str], int | None]:
2323
book = {}
2424
book["Title"] = raw_book["title"]
2525
book["Author"] = raw_book["authors"][0]
26-
try:
27-
book["Summary"] = raw_book["description"]
28-
except Exception:
29-
book["Summary"] = ""
26+
book["Summary"] = raw_book.get("description", "")
3027
return book, 200
3128
return {}, response.status_code
3229

3330
def open_library_by_isbn(self, isbn: str) -> tuple[dict[str, str], int]:
34-
url = self.OPEN_LIBRARY_API + "isbn/" + str(isbn) + ".json"
31+
url = self.OPEN_LIBRARY_API + f"/api/books?bibkeys={isbn}&format=json&jscmd=details"
3532
headers = {
3633
"User-Agent": self.USER_AGENT,
3734
"Accept-Encoding": "gzip",
3835
}
3936
response = requests.get(url, headers=headers)
4037
if response.ok:
41-
raw_book = json.loads(response.text)
42-
book = {}
43-
book["Title"] = raw_book["title"]
44-
author_url = str(raw_book["authors"][0]["key"])
45-
time.sleep(1)
46-
response = requests.get(
47-
self.OPEN_LIBRARY_API + author_url + ".json", headers=headers)
48-
if response.ok:
49-
book["Author"] = json.loads(response.text)["personal_name"]
38+
data = response.json()
39+
entry = data.get(isbn, {})
40+
book_details = entry.get("details", {})
41+
book = {
42+
"Title": book_details.get("title", ""),
43+
"Summary": book_details.get("description", {}).get("value", ""),
44+
}
45+
authors = book_details.get("authors", [])
46+
if len(authors) > 0 and "name" in authors[0]:
47+
book["Author"] = authors[0]["name"]
5048
else:
51-
print(response.status_code)
52-
try:
53-
book["Summary"] = raw_book["description"]["value"]
54-
except Exception:
55-
book["Summary"] = ""
49+
book["Author"] = ""
5650
return book, 200
5751
else:
5852
return {}, response.status_code

0 commit comments

Comments
 (0)