forked from mirrors/bookwyrm
Remove books manager at long last
This commit is contained in:
parent
e169565e00
commit
d828b0ead9
11 changed files with 93 additions and 89 deletions
|
@ -1,4 +1,6 @@
|
|||
''' bring connectors into the namespace '''
|
||||
from .settings import CONNECTORS
|
||||
from .abstract_connector import ConnectorException, load_connector
|
||||
from .abstract_connector import ConnectorException
|
||||
from .abstract_connector import get_data, get_image
|
||||
|
||||
from .connector_manager import search, local_search, first_search_result
|
||||
|
|
|
@ -1,24 +1,18 @@
|
|||
''' functionality outline for a book data connector '''
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import asdict, dataclass
|
||||
import importlib
|
||||
import logging
|
||||
from urllib3.exceptions import RequestError
|
||||
|
||||
from django.db import transaction
|
||||
import requests
|
||||
from requests import HTTPError
|
||||
from requests.exceptions import SSLError
|
||||
|
||||
from bookwyrm import activitypub, models, settings
|
||||
from bookwyrm.tasks import app
|
||||
from .connector_manager import load_more_data, ConnectorException
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
class ConnectorException(HTTPError):
|
||||
''' when the connector can't do what was asked '''
|
||||
|
||||
|
||||
class AbstractMinimalConnector(ABC):
|
||||
''' just the bare bones, for other bookwyrm instances '''
|
||||
def __init__(self, identifier):
|
||||
|
@ -192,23 +186,6 @@ class AbstractConnector(AbstractMinimalConnector):
|
|||
''' get more info on a book '''
|
||||
|
||||
|
||||
@app.task
|
||||
def load_more_data(connector_id, book_id):
|
||||
''' background the work of getting all 10,000 editions of LoTR '''
|
||||
connector_info = models.Connector.objects.get(id=connector_id)
|
||||
connector = load_connector(connector_info)
|
||||
book = models.Book.objects.select_subclasses().get(id=book_id)
|
||||
connector.expand_book_data(book)
|
||||
|
||||
|
||||
def load_connector(connector_info):
|
||||
''' instantiate the connector class '''
|
||||
connector = importlib.import_module(
|
||||
'bookwyrm.connectors.%s' % connector_info.connector_file
|
||||
)
|
||||
return connector.Connector(connector_info.identifier)
|
||||
|
||||
|
||||
def dict_from_mappings(data, mappings):
|
||||
''' create a dict in Activitypub format, using mappings supplies by
|
||||
the subclass '''
|
||||
|
|
|
@ -1,41 +1,15 @@
|
|||
''' select and call a connector for whatever book task needs doing '''
|
||||
''' interface with whatever connectors the app has '''
|
||||
import importlib
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from requests import HTTPError
|
||||
|
||||
from bookwyrm import models
|
||||
from bookwyrm.connectors import ConnectorException, load_connector
|
||||
from bookwyrm.tasks import app
|
||||
|
||||
|
||||
def get_edition(book_id):
|
||||
''' look up a book in the db and return an edition '''
|
||||
book = models.Book.objects.select_subclasses().get(id=book_id)
|
||||
if isinstance(book, models.Work):
|
||||
book = book.default_edition
|
||||
return book
|
||||
|
||||
|
||||
def get_or_create_connector(remote_id):
|
||||
''' get the connector related to the author's server '''
|
||||
url = urlparse(remote_id)
|
||||
identifier = url.netloc
|
||||
if not identifier:
|
||||
raise ValueError('Invalid remote id')
|
||||
|
||||
try:
|
||||
connector_info = models.Connector.objects.get(identifier=identifier)
|
||||
except models.Connector.DoesNotExist:
|
||||
connector_info = models.Connector.objects.create(
|
||||
identifier=identifier,
|
||||
connector_file='bookwyrm_connector',
|
||||
base_url='https://%s' % identifier,
|
||||
books_url='https://%s/book' % identifier,
|
||||
covers_url='https://%s/images/covers' % identifier,
|
||||
search_url='https://%s/search?q=' % identifier,
|
||||
priority=2
|
||||
)
|
||||
|
||||
return load_connector(connector_info)
|
||||
class ConnectorException(HTTPError):
|
||||
''' when the connector can't do what was asked '''
|
||||
|
||||
|
||||
def search(query, min_confidence=0.1):
|
||||
|
@ -80,3 +54,43 @@ def get_connectors():
|
|||
''' load all connectors '''
|
||||
for info in models.Connector.objects.order_by('priority').all():
|
||||
yield load_connector(info)
|
||||
|
||||
|
||||
def get_or_create_connector(remote_id):
|
||||
''' get the connector related to the author's server '''
|
||||
url = urlparse(remote_id)
|
||||
identifier = url.netloc
|
||||
if not identifier:
|
||||
raise ValueError('Invalid remote id')
|
||||
|
||||
try:
|
||||
connector_info = models.Connector.objects.get(identifier=identifier)
|
||||
except models.Connector.DoesNotExist:
|
||||
connector_info = models.Connector.objects.create(
|
||||
identifier=identifier,
|
||||
connector_file='bookwyrm_connector',
|
||||
base_url='https://%s' % identifier,
|
||||
books_url='https://%s/book' % identifier,
|
||||
covers_url='https://%s/images/covers' % identifier,
|
||||
search_url='https://%s/search?q=' % identifier,
|
||||
priority=2
|
||||
)
|
||||
|
||||
return load_connector(connector_info)
|
||||
|
||||
|
||||
@app.task
|
||||
def load_more_data(connector_id, book_id):
|
||||
''' background the work of getting all 10,000 editions of LoTR '''
|
||||
connector_info = models.Connector.objects.get(id=connector_id)
|
||||
connector = load_connector(connector_info)
|
||||
book = models.Book.objects.select_subclasses().get(id=book_id)
|
||||
connector.expand_book_data(book)
|
||||
|
||||
|
||||
def load_connector(connector_info):
|
||||
''' instantiate the connector class '''
|
||||
connector = importlib.import_module(
|
||||
'bookwyrm.connectors.%s' % connector_info.connector_file
|
||||
)
|
||||
return connector.Connector(connector_info.identifier)
|
|
@ -3,7 +3,7 @@ import re
|
|||
|
||||
from bookwyrm import models
|
||||
from .abstract_connector import AbstractConnector, SearchResult, Mapping
|
||||
from .abstract_connector import ConnectorException, get_data
|
||||
from .connector_manager import ConnectorException, get_data
|
||||
from .openlibrary_languages import languages
|
||||
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.contrib.postgres.fields import JSONField
|
|||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
from bookwyrm import books_manager
|
||||
from bookwyrm.connectors import connector_manager
|
||||
from bookwyrm.models import ReadThrough, User, Book
|
||||
from .fields import PrivacyLevels
|
||||
|
||||
|
@ -71,7 +71,7 @@ class ImportItem(models.Model):
|
|||
|
||||
def get_book_from_isbn(self):
|
||||
''' search by isbn '''
|
||||
search_result = books_manager.first_search_result(
|
||||
search_result = connector_manager.first_search_result(
|
||||
self.isbn, min_confidence=0.999
|
||||
)
|
||||
if search_result:
|
||||
|
@ -86,7 +86,7 @@ class ImportItem(models.Model):
|
|||
self.data['Title'],
|
||||
self.data['Author']
|
||||
)
|
||||
search_result = books_manager.first_search_result(
|
||||
search_result = connector_manager.first_search_result(
|
||||
search_term, min_confidence=0.999
|
||||
)
|
||||
if search_result:
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
from django.test import TestCase
|
||||
|
||||
from bookwyrm import books_manager, models
|
||||
from bookwyrm import models
|
||||
from bookwyrm.connectors import connector_manager
|
||||
from bookwyrm.connectors.bookwyrm_connector import Connector as BookWyrmConnector
|
||||
from bookwyrm.connectors.self_connector import Connector as SelfConnector
|
||||
|
||||
|
||||
class Book(TestCase):
|
||||
class ConnectorManager(TestCase):
|
||||
def setUp(self):
|
||||
self.work = models.Work.objects.create(
|
||||
title='Example Work'
|
||||
|
@ -29,52 +30,52 @@ class Book(TestCase):
|
|||
)
|
||||
|
||||
def test_get_edition(self):
|
||||
edition = books_manager.get_edition(self.edition.id)
|
||||
edition = connector_manager.get_edition(self.edition.id)
|
||||
self.assertEqual(edition, self.edition)
|
||||
|
||||
|
||||
def test_get_edition_work(self):
|
||||
edition = books_manager.get_edition(self.work.id)
|
||||
edition = connector_manager.get_edition(self.work.id)
|
||||
self.assertEqual(edition, self.edition)
|
||||
|
||||
|
||||
def test_get_or_create_connector(self):
|
||||
remote_id = 'https://example.com/object/1'
|
||||
connector = books_manager.get_or_create_connector(remote_id)
|
||||
connector = connector_manager.get_or_create_connector(remote_id)
|
||||
self.assertIsInstance(connector, BookWyrmConnector)
|
||||
self.assertEqual(connector.identifier, 'example.com')
|
||||
self.assertEqual(connector.base_url, 'https://example.com')
|
||||
|
||||
same_connector = books_manager.get_or_create_connector(remote_id)
|
||||
same_connector = connector_manager.get_or_create_connector(remote_id)
|
||||
self.assertEqual(connector.identifier, same_connector.identifier)
|
||||
|
||||
def test_get_connectors(self):
|
||||
remote_id = 'https://example.com/object/1'
|
||||
books_manager.get_or_create_connector(remote_id)
|
||||
connectors = list(books_manager.get_connectors())
|
||||
connector_manager.get_or_create_connector(remote_id)
|
||||
connectors = list(connector_manager.get_connectors())
|
||||
self.assertEqual(len(connectors), 2)
|
||||
self.assertIsInstance(connectors[0], SelfConnector)
|
||||
self.assertIsInstance(connectors[1], BookWyrmConnector)
|
||||
|
||||
def test_search(self):
|
||||
results = books_manager.search('Example')
|
||||
results = connector_manager.search('Example')
|
||||
self.assertEqual(len(results), 1)
|
||||
self.assertIsInstance(results[0]['connector'], SelfConnector)
|
||||
self.assertEqual(len(results[0]['results']), 1)
|
||||
self.assertEqual(results[0]['results'][0].title, 'Example Edition')
|
||||
|
||||
def test_local_search(self):
|
||||
results = books_manager.local_search('Example')
|
||||
results = connector_manager.local_search('Example')
|
||||
self.assertEqual(len(results), 1)
|
||||
self.assertEqual(results[0].title, 'Example Edition')
|
||||
|
||||
def test_first_search_result(self):
|
||||
result = books_manager.first_search_result('Example')
|
||||
result = connector_manager.first_search_result('Example')
|
||||
self.assertEqual(result.title, 'Example Edition')
|
||||
no_result = books_manager.first_search_result('dkjfhg')
|
||||
no_result = connector_manager.first_search_result('dkjfhg')
|
||||
self.assertIsNone(no_result)
|
||||
|
||||
def test_load_connector(self):
|
||||
connector = books_manager.load_connector(self.connector)
|
||||
connector = connector_manager.load_connector(self.connector)
|
||||
self.assertIsInstance(connector, SelfConnector)
|
||||
self.assertEqual(connector.identifier, 'test_connector')
|
|
@ -12,7 +12,7 @@ from bookwyrm.connectors.openlibrary import get_languages, get_description
|
|||
from bookwyrm.connectors.openlibrary import pick_default_edition, \
|
||||
get_openlibrary_key
|
||||
from bookwyrm.connectors.abstract_connector import SearchResult
|
||||
from bookwyrm.connectors.abstract_connector import ConnectorException
|
||||
from bookwyrm.connectors.connector_manager import ConnectorException
|
||||
|
||||
|
||||
class Openlibrary(TestCase):
|
||||
|
|
|
@ -8,7 +8,8 @@ from django.utils import timezone
|
|||
from django.test import TestCase
|
||||
import responses
|
||||
|
||||
from bookwyrm import books_manager, models
|
||||
from bookwyrm import models
|
||||
from bookwyrm.connectors import connector_manager
|
||||
from bookwyrm.connectors.abstract_connector import SearchResult
|
||||
|
||||
|
||||
|
@ -134,7 +135,7 @@ class ImportJob(TestCase):
|
|||
search_url='https://openlibrary.org/search?q=',
|
||||
priority=3,
|
||||
)
|
||||
connector = books_manager.load_connector(connector_info)
|
||||
connector = connector_manager.load_connector(connector_info)
|
||||
result = SearchResult(
|
||||
title='Test Result',
|
||||
key='https://openlibrary.org/works/OL1234W',
|
||||
|
@ -163,7 +164,7 @@ class ImportJob(TestCase):
|
|||
json={'name': 'test author'},
|
||||
status=200)
|
||||
|
||||
with patch('bookwyrm.books_manager.first_search_result') as search:
|
||||
with patch('bookwyrm.connector_manager.first_search_result') as search:
|
||||
search.return_value = result
|
||||
book = self.item_1.get_book_from_isbn()
|
||||
|
||||
|
|
|
@ -193,7 +193,7 @@ class Views(TestCase):
|
|||
request = self.factory.get('', {'q': 'Test Book'})
|
||||
with patch('bookwyrm.views.is_api_request') as is_api:
|
||||
is_api.return_value = False
|
||||
with patch('bookwyrm.books_manager.search') as manager:
|
||||
with patch('bookwyrm.connectors.connector_manager.search') as manager:
|
||||
manager.return_value = [search_result]
|
||||
response = views.search(request)
|
||||
self.assertIsInstance(response, TemplateResponse)
|
||||
|
|
|
@ -17,11 +17,12 @@ from django.template.response import TemplateResponse
|
|||
from django.utils import timezone
|
||||
from django.views.decorators.http import require_GET, require_POST
|
||||
|
||||
from bookwyrm import books_manager, forms, models, outgoing, goodreads_import
|
||||
from bookwyrm import forms, models, outgoing, goodreads_import
|
||||
from bookwyrm.connectors import connector_manager
|
||||
from bookwyrm.broadcast import broadcast
|
||||
from bookwyrm.emailing import password_reset_email
|
||||
from bookwyrm.settings import DOMAIN
|
||||
from bookwyrm.views import get_user_from_username
|
||||
from bookwyrm.views import get_user_from_username, get_edition
|
||||
|
||||
|
||||
@require_POST
|
||||
|
@ -210,7 +211,7 @@ def edit_profile(request):
|
|||
def resolve_book(request):
|
||||
''' figure out the local path to a book from a remote_id '''
|
||||
remote_id = request.POST.get('remote_id')
|
||||
connector = books_manager.get_or_create_connector(remote_id)
|
||||
connector = connector_manager.get_or_create_connector(remote_id)
|
||||
book = connector.get_or_create_book(remote_id)
|
||||
|
||||
return redirect('/book/%d' % book.id)
|
||||
|
@ -369,7 +370,7 @@ def delete_shelf(request, shelf_id):
|
|||
@require_POST
|
||||
def shelve(request):
|
||||
''' put a on a user's shelf '''
|
||||
book = books_manager.get_edition(request.POST['book'])
|
||||
book = get_edition(request.POST['book'])
|
||||
|
||||
desired_shelf = models.Shelf.objects.filter(
|
||||
identifier=request.POST['shelf'],
|
||||
|
@ -415,7 +416,7 @@ def unshelve(request):
|
|||
@require_POST
|
||||
def start_reading(request, book_id):
|
||||
''' begin reading a book '''
|
||||
book = books_manager.get_edition(book_id)
|
||||
book = get_edition(book_id)
|
||||
shelf = models.Shelf.objects.filter(
|
||||
identifier='reading',
|
||||
user=request.user
|
||||
|
@ -451,7 +452,7 @@ def start_reading(request, book_id):
|
|||
@require_POST
|
||||
def finish_reading(request, book_id):
|
||||
''' a user completed a book, yay '''
|
||||
book = books_manager.get_edition(book_id)
|
||||
book = get_edition(book_id)
|
||||
shelf = models.Shelf.objects.filter(
|
||||
identifier='read',
|
||||
user=request.user
|
||||
|
|
|
@ -13,14 +13,22 @@ from django.views.decorators.csrf import csrf_exempt
|
|||
from django.views.decorators.http import require_GET
|
||||
|
||||
from bookwyrm import outgoing
|
||||
from bookwyrm.activitypub import ActivitypubResponse
|
||||
from bookwyrm import forms, models, books_manager
|
||||
from bookwyrm import forms, models
|
||||
from bookwyrm import goodreads_import
|
||||
from bookwyrm.activitypub import ActivitypubResponse
|
||||
from bookwyrm.connectors import connector_manager
|
||||
from bookwyrm.settings import PAGE_LENGTH
|
||||
from bookwyrm.tasks import app
|
||||
from bookwyrm.utils import regex
|
||||
|
||||
|
||||
def get_edition(book_id):
|
||||
''' look up a book in the db and return an edition '''
|
||||
book = models.Book.objects.select_subclasses().get(id=book_id)
|
||||
if isinstance(book, models.Work):
|
||||
book = book.default_edition
|
||||
return book
|
||||
|
||||
def get_user_from_username(username):
|
||||
''' helper function to resolve a localname or a username to a user '''
|
||||
# raises DoesNotExist if user is now found
|
||||
|
@ -211,7 +219,7 @@ def search(request):
|
|||
|
||||
if is_api_request(request):
|
||||
# only return local book results via json so we don't cause a cascade
|
||||
book_results = books_manager.local_search(query)
|
||||
book_results = connector_manager.local_search(query)
|
||||
return JsonResponse([r.json() for r in book_results], safe=False)
|
||||
|
||||
# use webfinger for mastodon style account@domain.com username
|
||||
|
@ -225,7 +233,7 @@ def search(request):
|
|||
similarity__gt=0.5,
|
||||
).order_by('-similarity')[:10]
|
||||
|
||||
book_results = books_manager.search(query)
|
||||
book_results = connector_manager.search(query)
|
||||
data = {
|
||||
'title': 'Search Results',
|
||||
'book_results': book_results,
|
||||
|
@ -645,7 +653,7 @@ def book_page(request, book_id):
|
|||
@require_GET
|
||||
def edit_book_page(request, book_id):
|
||||
''' info about a book '''
|
||||
book = books_manager.get_edition(book_id)
|
||||
book = get_edition(book_id)
|
||||
if not book.description:
|
||||
book.description = book.parent_work.description
|
||||
data = {
|
||||
|
|
Loading…
Reference in a new issue