Adds create_book functionality for fedireads conn

This commit is contained in:
Mouse Reeve 2020-05-08 16:56:49 -07:00
parent 1b91fb375f
commit 0edb9688cb
5 changed files with 95 additions and 29 deletions

View file

@ -5,6 +5,7 @@ def get_book(book):
''' activitypub serialize a book ''' ''' activitypub serialize a book '''
fields = [ fields = [
'title',
'sort_title', 'sort_title',
'subtitle', 'subtitle',
'isbn_13', 'isbn_13',
@ -27,10 +28,11 @@ def get_book(book):
'physical_format', 'physical_format',
] ]
book_type = type(book).__name__
activity = { activity = {
'@context': 'https://www.w3.org/ns/activitystreams', '@context': 'https://www.w3.org/ns/activitystreams',
'type': 'Document', 'type': 'Document',
'book_type': type(book).__name__, 'book_type': book_type,
'name': book.title, 'name': book.title,
'url': book.absolute_id, 'url': book.absolute_id,
@ -39,9 +41,13 @@ def get_book(book):
book.first_published_date else None, book.first_published_date else None,
'published_date': book.published_date.isoformat() if \ 'published_date': book.published_date.isoformat() if \
book.published_date else None, 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: for field in fields:
if hasattr(book, field): if hasattr(book, field):
activity[field] = book.__getattribute__(field) activity[field] = book.__getattribute__(field)

View file

@ -18,11 +18,13 @@ def get_or_create_book(value, key='id', connector_id=None):
book = get_by_absolute_id(value, models.Book) book = get_by_absolute_id(value, models.Book)
if book: if book:
return 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) if connector_id:
connector = load_connector(connector_info) 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) book = connector.get_or_create_book(value)
load_more_data.delay(book.id) load_more_data.delay(book.id)
return book return book
@ -33,7 +35,7 @@ def get_or_create_connector(remote_id):
url = urlparse(remote_id) url = urlparse(remote_id)
identifier = url.netloc identifier = url.netloc
if not identifier: if not identifier:
raise(ValueError) raise ValueError('Invalid remote id')
try: try:
connector_info = models.Connector.objects.get(identifier=identifier) connector_info = models.Connector.objects.get(identifier=identifier)

View file

@ -52,6 +52,33 @@ class AbstractConnector(ABC):
return results 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 @abstractmethod
def format_search_result(self, search_result): def format_search_result(self, search_result):
''' create a SearchResult obj from json ''' ''' create a SearchResult obj from json '''

View file

@ -1,7 +1,9 @@
''' using another fedireads instance as a source of book data ''' ''' using another fedireads instance as a source of book data '''
import requests
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
import requests from django.db import transaction
from fedireads import models from fedireads import models
from .abstract_connector import AbstractConnector, SearchResult, get_date 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): class Connector(AbstractConnector):
''' interact with other instances ''' ''' 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): def format_search_result(self, search_result):
return SearchResult(**search_result) return SearchResult(**search_result)
@ -26,8 +37,44 @@ class Connector(AbstractConnector):
return book return book
# no book was found, so we start creating a new one # no book was found, so we start creating a new one
book = models.Book(remote_id=remote_id) response = requests.get(
self.update_book(book) 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): def update_book(self, book, data=None):

View file

@ -7,7 +7,7 @@ from django.db import transaction
from fedireads import models from fedireads import models
from .abstract_connector import AbstractConnector, SearchResult 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 .abstract_connector import get_date
from .openlibrary_languages import languages from .openlibrary_languages import languages
@ -104,26 +104,10 @@ class Connector(AbstractConnector):
return edition 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): def update_book_from_data(self, book, data):
''' updaet a book model instance from ol data ''' ''' updaet a book model instance from ol data '''
# populate the simple data fields # populate the simple data fields
update_from_mappings(book, data, self.book_mappings) super().update_book_from_data(book, data)
book.save()
authors = self.get_authors_from_data(data) authors = self.get_authors_from_data(data)
for author in authors: for author in authors: