From 0edb9688cb0b2dee04bf94c080f3f0a2c007a37c Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Fri, 8 May 2020 16:56:49 -0700 Subject: [PATCH] Adds create_book functionality for fedireads conn --- fedireads/activitypub/book.py | 12 +++-- fedireads/books_manager.py | 12 +++-- fedireads/connectors/abstract_connector.py | 27 +++++++++++ fedireads/connectors/fedireads_connector.py | 53 +++++++++++++++++++-- fedireads/connectors/openlibrary.py | 20 +------- 5 files changed, 95 insertions(+), 29 deletions(-) diff --git a/fedireads/activitypub/book.py b/fedireads/activitypub/book.py index 98a71f962..d3b7ca8d8 100644 --- a/fedireads/activitypub/book.py +++ b/fedireads/activitypub/book.py @@ -5,6 +5,7 @@ def get_book(book): ''' activitypub serialize a book ''' fields = [ + 'title', 'sort_title', 'subtitle', 'isbn_13', @@ -27,10 +28,11 @@ def get_book(book): 'physical_format', ] + book_type = type(book).__name__ activity = { '@context': 'https://www.w3.org/ns/activitystreams', 'type': 'Document', - 'book_type': type(book).__name__, + 'book_type': book_type, 'name': book.title, 'url': book.absolute_id, @@ -39,9 +41,13 @@ def get_book(book): book.first_published_date else None, 'published_date': book.published_date.isoformat() if \ book.published_date else None, - 'parent_work': book.parent_work.absolute_id if \ - hasattr(book, 'parent_work') else None, } + if book_type == 'Edition': + activity['work'] = book.parent_work.absolute_id + else: + editions = book.edition_set.order_by('default') + activity['editions'] = [get_book(b) for b in editions] + for field in fields: if hasattr(book, field): activity[field] = book.__getattribute__(field) diff --git a/fedireads/books_manager.py b/fedireads/books_manager.py index 209a9d500..e82aaeb61 100644 --- a/fedireads/books_manager.py +++ b/fedireads/books_manager.py @@ -18,11 +18,13 @@ def get_or_create_book(value, key='id', connector_id=None): book = get_by_absolute_id(value, models.Book) if book: return book - connector = get_or_create_connector(value) - return connector.get_or_create_book(value) - connector_info = models.Connector.objects.get(id=connector_id) - connector = load_connector(connector_info) + if connector_id: + connector_info = models.Connector.objects.get(id=connector_id) + connector = load_connector(connector_info) + else: + connector = get_or_create_connector(value) + book = connector.get_or_create_book(value) load_more_data.delay(book.id) return book @@ -33,7 +35,7 @@ def get_or_create_connector(remote_id): url = urlparse(remote_id) identifier = url.netloc if not identifier: - raise(ValueError) + raise ValueError('Invalid remote id') try: connector_info = models.Connector.objects.get(identifier=identifier) diff --git a/fedireads/connectors/abstract_connector.py b/fedireads/connectors/abstract_connector.py index 0eb18c2d9..8828f0eaf 100644 --- a/fedireads/connectors/abstract_connector.py +++ b/fedireads/connectors/abstract_connector.py @@ -52,6 +52,33 @@ class AbstractConnector(ABC): return results + def create_book(self, key, data, model): + ''' create a work or edition from data ''' + # we really would rather use an existing book than make a new one + match = match_from_mappings(data, self.key_mappings) + if match: + if not isinstance(match, model): + if type(match).__name__ == 'Edition': + return match.parent_work + else: + return match.default_edition + return match + + kwargs = { + self.key_name: key, + 'title': data['title'], + 'connector': self.connector + } + book = model.objects.create(**kwargs) + return self.update_book_from_data(book, data) + + + def update_book_from_data(self, book, data): + ''' simple function to save data to a book ''' + update_from_mappings(book, data, self.book_mappings) + book.save() + + @abstractmethod def format_search_result(self, search_result): ''' create a SearchResult obj from json ''' diff --git a/fedireads/connectors/fedireads_connector.py b/fedireads/connectors/fedireads_connector.py index 84d804f69..c6abe8f29 100644 --- a/fedireads/connectors/fedireads_connector.py +++ b/fedireads/connectors/fedireads_connector.py @@ -1,7 +1,9 @@ ''' using another fedireads instance as a source of book data ''' +import requests + from django.core.exceptions import ObjectDoesNotExist from django.core.files.base import ContentFile -import requests +from django.db import transaction from fedireads import models from .abstract_connector import AbstractConnector, SearchResult, get_date @@ -10,6 +12,15 @@ from .abstract_connector import match_from_mappings, update_from_mappings class Connector(AbstractConnector): ''' interact with other instances ''' + def __init__(self, identifier): + self.key_mappings = { + 'isbn_13': ('isbn_13', None), + 'isbn_10': ('isbn_10', None), + 'oclc_numbers': ('oclc_number', None), + 'lccn': ('lccn', None), + } + super().__init__(identifier) + def format_search_result(self, search_result): return SearchResult(**search_result) @@ -26,8 +37,44 @@ class Connector(AbstractConnector): return book # no book was found, so we start creating a new one - book = models.Book(remote_id=remote_id) - self.update_book(book) + response = requests.get( + remote_id, + headers={ + 'Accept': 'application/activity+json; charset=utf-8', + }, + ) + if not response.ok: + response.raise_for_status() + data = response.json() + + if data['book_type'] == 'work': + work_data = data + try: + edition_data = data['editions'][0] + except KeyError: + # hack: re-use the work data as the edition data + edition_data = data + else: + edition_data = data + try: + work_data = data['work'] + except KeyError: + # hack: re-use the work data as the edition data + work_data = data + + with transaction.atomic(): + # create both work and a default edition + work_key = edition_data.get('url') + work = self.create_book(work_key, work_data, models.Work) + + ed_key = edition_data.get('url') + edition = self.create_book(ed_key, edition_data, models.Edition) + edition.default = True + edition.parent_work = work + edition.save() + + print(work, edition) + return edition def update_book(self, book, data=None): diff --git a/fedireads/connectors/openlibrary.py b/fedireads/connectors/openlibrary.py index 5d383ab8f..5d4b272fa 100644 --- a/fedireads/connectors/openlibrary.py +++ b/fedireads/connectors/openlibrary.py @@ -7,7 +7,7 @@ from django.db import transaction from fedireads import models from .abstract_connector import AbstractConnector, SearchResult -from .abstract_connector import match_from_mappings, update_from_mappings +from .abstract_connector import update_from_mappings from .abstract_connector import get_date from .openlibrary_languages import languages @@ -104,26 +104,10 @@ class Connector(AbstractConnector): return edition - def create_book(self, key, data, model): - ''' create a work or edition from data ''' - # we really would rather use an existing book than make a new one - match = match_from_mappings(data, self.key_mappings) - if match: - return match - - book = model.objects.create( - openlibrary_key=key, - title=data['title'], - connector=self.connector, - ) - return self.update_book_from_data(book, data) - - def update_book_from_data(self, book, data): ''' updaet a book model instance from ol data ''' # populate the simple data fields - update_from_mappings(book, data, self.book_mappings) - book.save() + super().update_book_from_data(book, data) authors = self.get_authors_from_data(data) for author in authors: