2021-04-29 00:20:14 +00:00
|
|
|
""" testing book data connectors """
|
2021-04-29 15:35:37 +00:00
|
|
|
import json
|
|
|
|
import pathlib
|
2021-10-27 17:03:09 +00:00
|
|
|
from unittest.mock import patch
|
|
|
|
|
2021-04-29 00:20:14 +00:00
|
|
|
from django.test import TestCase
|
|
|
|
import responses
|
|
|
|
|
|
|
|
from bookwyrm import models
|
2021-04-30 19:50:35 +00:00
|
|
|
from bookwyrm.connectors.inventaire import Connector, get_language_code
|
2021-10-27 17:03:09 +00:00
|
|
|
from bookwyrm.connectors.connector_manager import ConnectorException
|
2021-04-29 00:20:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Inventaire(TestCase):
|
|
|
|
"""test loading data from inventaire.io"""
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
"""creates the connector we'll use"""
|
|
|
|
models.Connector.objects.create(
|
|
|
|
identifier="inventaire.io",
|
|
|
|
name="Inventaire",
|
|
|
|
connector_file="inventaire",
|
|
|
|
base_url="https://inventaire.io",
|
|
|
|
books_url="https://inventaire.io",
|
|
|
|
covers_url="https://covers.inventaire.io",
|
|
|
|
search_url="https://inventaire.io/search?q=",
|
|
|
|
isbn_search_url="https://inventaire.io/isbn",
|
|
|
|
)
|
|
|
|
self.connector = Connector("inventaire.io")
|
|
|
|
|
|
|
|
@responses.activate
|
|
|
|
def test_get_book_data(self):
|
|
|
|
"""flattens the default structure to make it easier to parse"""
|
|
|
|
responses.add(
|
|
|
|
responses.GET,
|
|
|
|
"https://test.url/ok",
|
|
|
|
json={
|
|
|
|
"entities": {
|
|
|
|
"isbn:9780375757853": {
|
|
|
|
"claims": {
|
|
|
|
"wdt:P31": ["wd:Q3331189"],
|
|
|
|
},
|
|
|
|
"uri": "isbn:9780375757853",
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"redirects": {},
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
result = self.connector.get_book_data("https://test.url/ok")
|
|
|
|
self.assertEqual(result["wdt:P31"], ["wd:Q3331189"])
|
|
|
|
self.assertEqual(result["uri"], "isbn:9780375757853")
|
2021-04-29 15:35:37 +00:00
|
|
|
|
2021-10-27 17:03:09 +00:00
|
|
|
@responses.activate
|
|
|
|
def test_get_book_data_invalid(self):
|
|
|
|
"""error if there isn't any entity data"""
|
|
|
|
responses.add(
|
|
|
|
responses.GET,
|
|
|
|
"https://test.url/ok",
|
|
|
|
json={
|
|
|
|
"entities": {},
|
|
|
|
"redirects": {},
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
with self.assertRaises(ConnectorException):
|
|
|
|
self.connector.get_book_data("https://test.url/ok")
|
|
|
|
|
|
|
|
@responses.activate
|
|
|
|
def test_search(self):
|
|
|
|
"""min confidence filtering"""
|
|
|
|
responses.add(
|
|
|
|
responses.GET,
|
|
|
|
"https://inventaire.io/search?q=hi",
|
|
|
|
json={
|
|
|
|
"results": [
|
|
|
|
{
|
|
|
|
"_score": 200,
|
|
|
|
"label": "hello",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"_score": 100,
|
|
|
|
"label": "hi",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
)
|
|
|
|
results = self.connector.search("hi", min_confidence=0.5)
|
|
|
|
self.assertEqual(len(results), 1)
|
|
|
|
self.assertEqual(results[0].title, "hello")
|
|
|
|
|
2021-04-29 15:35:37 +00:00
|
|
|
def test_format_search_result(self):
|
|
|
|
"""json to search result objs"""
|
|
|
|
search_file = pathlib.Path(__file__).parent.joinpath(
|
|
|
|
"../data/inventaire_search.json"
|
|
|
|
)
|
|
|
|
search_results = json.loads(search_file.read_bytes())
|
|
|
|
|
|
|
|
results = self.connector.parse_search_data(search_results)
|
|
|
|
formatted = self.connector.format_search_result(results[0])
|
|
|
|
|
|
|
|
self.assertEqual(formatted.title, "The Stories of Vladimir Nabokov")
|
|
|
|
self.assertEqual(
|
|
|
|
formatted.key, "https://inventaire.io?action=by-uris&uris=wd:Q7766679"
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
formatted.cover,
|
2021-05-18 19:51:57 +00:00
|
|
|
"https://covers.inventaire.io/img/entities/ddb32",
|
2021-04-29 15:35:37 +00:00
|
|
|
)
|
2021-04-29 16:06:17 +00:00
|
|
|
|
|
|
|
def test_get_cover_url(self):
|
|
|
|
"""figure out where the cover image is"""
|
|
|
|
cover_blob = {"url": "/img/entities/d46a8"}
|
|
|
|
result = self.connector.get_cover_url(cover_blob)
|
|
|
|
self.assertEqual(result, "https://covers.inventaire.io/img/entities/d46a8")
|
|
|
|
|
|
|
|
cover_blob = {
|
2021-05-18 19:51:57 +00:00
|
|
|
"url": "https://commons.wikimedia.org/wiki/d.jpg?width=1000",
|
2021-04-29 16:06:17 +00:00
|
|
|
"file": "The Moonstone 1st ed.jpg",
|
|
|
|
"credits": {
|
|
|
|
"text": "Wikimedia Commons",
|
2021-05-18 19:51:57 +00:00
|
|
|
"url": "https://commons.wikimedia.org/wiki/File:The Moonstone.jpg",
|
2021-04-29 16:06:17 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
result = self.connector.get_cover_url(cover_blob)
|
|
|
|
self.assertEqual(
|
|
|
|
result,
|
2021-05-18 19:51:57 +00:00
|
|
|
"https://commons.wikimedia.org/wiki/d.jpg?width=1000",
|
2021-04-29 16:06:17 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
@responses.activate
|
|
|
|
def test_resolve_keys(self):
|
|
|
|
"""makes an http request"""
|
|
|
|
responses.add(
|
|
|
|
responses.GET,
|
|
|
|
"https://inventaire.io?action=by-uris&uris=wd:Q465821",
|
|
|
|
json={
|
|
|
|
"entities": {
|
|
|
|
"wd:Q465821": {
|
|
|
|
"type": "genre",
|
|
|
|
"labels": {
|
|
|
|
"nl": "briefroman",
|
|
|
|
"en": "epistolary novel",
|
|
|
|
"de-ch": "Briefroman",
|
|
|
|
"en-ca": "Epistolary novel",
|
|
|
|
"nb": "brev- og dagbokroman",
|
|
|
|
},
|
|
|
|
"descriptions": {
|
|
|
|
"en": "novel written as a series of documents",
|
|
|
|
"es": "novela escrita como una serie de documentos",
|
|
|
|
"eo": "romano en la formo de serio de leteroj",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"redirects": {},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
responses.add(
|
|
|
|
responses.GET,
|
|
|
|
"https://inventaire.io?action=by-uris&uris=wd:Q208505",
|
|
|
|
json={
|
|
|
|
"entities": {
|
|
|
|
"wd:Q208505": {
|
|
|
|
"type": "genre",
|
|
|
|
"labels": {
|
|
|
|
"en": "crime novel",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
keys = [
|
|
|
|
"wd:Q465821",
|
|
|
|
"wd:Q208505",
|
|
|
|
]
|
|
|
|
result = self.connector.resolve_keys(keys)
|
|
|
|
self.assertEqual(result, ["epistolary novel", "crime novel"])
|
2021-04-29 16:54:42 +00:00
|
|
|
|
|
|
|
def test_isbn_search(self):
|
2021-04-29 16:56:35 +00:00
|
|
|
"""another search type"""
|
2021-04-29 16:54:42 +00:00
|
|
|
search_file = pathlib.Path(__file__).parent.joinpath(
|
|
|
|
"../data/inventaire_isbn_search.json"
|
|
|
|
)
|
|
|
|
search_results = json.loads(search_file.read_bytes())
|
|
|
|
|
|
|
|
results = self.connector.parse_isbn_search_data(search_results)
|
|
|
|
formatted = self.connector.format_isbn_search_result(results[0])
|
|
|
|
|
|
|
|
self.assertEqual(formatted.title, "L'homme aux cercles bleus")
|
|
|
|
self.assertEqual(
|
2021-04-29 16:56:35 +00:00
|
|
|
formatted.key,
|
|
|
|
"https://inventaire.io?action=by-uris&uris=isbn:9782290349229",
|
2021-04-29 16:54:42 +00:00
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
formatted.cover,
|
|
|
|
"https://covers.inventaire.io/img/entities/12345",
|
|
|
|
)
|
2021-04-30 19:50:35 +00:00
|
|
|
|
2021-10-27 17:03:09 +00:00
|
|
|
def test_isbn_search_empty(self):
|
|
|
|
"""another search type"""
|
|
|
|
search_results = {}
|
|
|
|
results = self.connector.parse_isbn_search_data(search_results)
|
|
|
|
self.assertEqual(results, [])
|
|
|
|
|
|
|
|
def test_isbn_search_no_title(self):
|
|
|
|
"""another search type"""
|
|
|
|
search_file = pathlib.Path(__file__).parent.joinpath(
|
|
|
|
"../data/inventaire_isbn_search.json"
|
|
|
|
)
|
|
|
|
search_results = json.loads(search_file.read_bytes())
|
|
|
|
search_results["entities"]["isbn:9782290349229"]["claims"]["wdt:P1476"] = None
|
|
|
|
|
|
|
|
result = self.connector.format_isbn_search_result(
|
|
|
|
search_results.get("entities")
|
|
|
|
)
|
|
|
|
self.assertIsNone(result)
|
|
|
|
|
|
|
|
def test_is_work_data(self):
|
|
|
|
"""is it a work"""
|
|
|
|
work_file = pathlib.Path(__file__).parent.joinpath(
|
|
|
|
"../data/inventaire_work.json"
|
|
|
|
)
|
|
|
|
work_data = json.loads(work_file.read_bytes())
|
|
|
|
with patch("bookwyrm.connectors.inventaire.get_data") as get_data_mock:
|
|
|
|
get_data_mock.return_value = work_data
|
|
|
|
formatted = self.connector.get_book_data("hi")
|
|
|
|
self.assertTrue(self.connector.is_work_data(formatted))
|
|
|
|
|
|
|
|
edition_file = pathlib.Path(__file__).parent.joinpath(
|
|
|
|
"../data/inventaire_edition.json"
|
|
|
|
)
|
|
|
|
edition_data = json.loads(edition_file.read_bytes())
|
|
|
|
with patch("bookwyrm.connectors.inventaire.get_data") as get_data_mock:
|
|
|
|
get_data_mock.return_value = edition_data
|
|
|
|
formatted = self.connector.get_book_data("hi")
|
|
|
|
self.assertFalse(self.connector.is_work_data(formatted))
|
|
|
|
|
|
|
|
@responses.activate
|
|
|
|
def test_get_edition_from_work_data(self):
|
|
|
|
"""load edition"""
|
|
|
|
responses.add(
|
|
|
|
responses.GET,
|
|
|
|
"https://inventaire.io/?action=by-uris&uris=hello",
|
|
|
|
json={"entities": {}},
|
|
|
|
)
|
|
|
|
data = {"uri": "blah"}
|
|
|
|
with patch(
|
|
|
|
"bookwyrm.connectors.inventaire.Connector.load_edition_data"
|
|
|
|
) as loader_mock, patch(
|
|
|
|
"bookwyrm.connectors.inventaire.Connector.get_book_data"
|
|
|
|
) as getter_mock:
|
|
|
|
loader_mock.return_value = {"uris": ["hello"]}
|
|
|
|
self.connector.get_edition_from_work_data(data)
|
|
|
|
self.assertTrue(getter_mock.called)
|
|
|
|
|
|
|
|
with patch(
|
|
|
|
"bookwyrm.connectors.inventaire.Connector.load_edition_data"
|
|
|
|
) as loader_mock:
|
|
|
|
loader_mock.return_value = {"uris": []}
|
|
|
|
with self.assertRaises(ConnectorException):
|
|
|
|
self.connector.get_edition_from_work_data(data)
|
|
|
|
|
|
|
|
@responses.activate
|
|
|
|
def test_get_work_from_edition_data(self):
|
|
|
|
"""load work"""
|
|
|
|
responses.add(
|
|
|
|
responses.GET,
|
|
|
|
"https://inventaire.io/?action=by-uris&uris=hello",
|
|
|
|
)
|
2021-11-16 18:16:28 +00:00
|
|
|
data = {"wdt:P629": ["hello"]}
|
|
|
|
with patch("bookwyrm.connectors.inventaire.Connector.get_book_data") as mock:
|
|
|
|
self.connector.get_work_from_edition_data(data)
|
|
|
|
self.assertEqual(mock.call_count, 1)
|
|
|
|
args = mock.call_args[0]
|
|
|
|
self.assertEqual(args[0], "https://inventaire.io?action=by-uris&uris=hello")
|
2021-10-27 17:03:09 +00:00
|
|
|
|
2021-11-16 18:16:28 +00:00
|
|
|
data = {"wdt:P629": [None]}
|
2021-10-27 17:03:09 +00:00
|
|
|
with self.assertRaises(ConnectorException):
|
|
|
|
self.connector.get_work_from_edition_data(data)
|
|
|
|
|
2021-04-30 19:50:35 +00:00
|
|
|
def test_get_language_code(self):
|
2021-04-30 19:52:20 +00:00
|
|
|
"""get english or whatever is in reach"""
|
2021-04-30 19:50:35 +00:00
|
|
|
options = {
|
|
|
|
"de": "bip",
|
|
|
|
"en": "hi",
|
|
|
|
"fr": "there",
|
|
|
|
}
|
|
|
|
self.assertEqual(get_language_code(options), "hi")
|
|
|
|
|
|
|
|
options = {
|
|
|
|
"fr": "there",
|
|
|
|
}
|
|
|
|
self.assertEqual(get_language_code(options), "there")
|
|
|
|
self.assertIsNone(get_language_code({}))
|
2021-08-06 01:16:23 +00:00
|
|
|
|
|
|
|
@responses.activate
|
|
|
|
def test_get_description(self):
|
|
|
|
"""extract a wikipedia excerpt"""
|
|
|
|
responses.add(
|
|
|
|
responses.GET,
|
|
|
|
"https://inventaire.io/api/data?action=wp-extract&lang=en&title=test_path",
|
|
|
|
json={"extract": "hi hi"},
|
|
|
|
)
|
|
|
|
|
|
|
|
extract = self.connector.get_description({"enwiki": "test_path"})
|
|
|
|
self.assertEqual(extract, "hi hi")
|
2021-12-07 21:53:25 +00:00
|
|
|
|
|
|
|
def test_remote_id_from_model(self):
|
|
|
|
"""figure out a url from an id"""
|
|
|
|
obj = models.Author.objects.create(name="hello", inventaire_id="123")
|
|
|
|
self.assertEqual(
|
|
|
|
self.connector.get_remote_id_from_model(obj),
|
|
|
|
"https://inventaire.io?action=by-uris&uris=123",
|
|
|
|
)
|