mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-11-26 11:31:08 +00:00
Merge pull request #261 from mouse-reeve/federated-book-lookup
Federated book lookup
This commit is contained in:
commit
0b2889d747
11 changed files with 74 additions and 10 deletions
|
@ -4,7 +4,7 @@ import sys
|
|||
|
||||
from .base_activity import ActivityEncoder, Image, PublicKey, Signature
|
||||
from .note import Note, GeneratedNote, Article, Comment, Review, Quotation
|
||||
from .note import Tombstone
|
||||
from .note import Tombstone, Link
|
||||
from .interaction import Boost, Like
|
||||
from .ordered_collection import OrderedCollection, OrderedCollectionPage
|
||||
from .person import Person
|
||||
|
|
|
@ -15,7 +15,6 @@ class ActivityEncoder(JSONEncoder):
|
|||
@dataclass
|
||||
class Image:
|
||||
''' image block '''
|
||||
mediaType: str
|
||||
url: str
|
||||
type: str = 'Image'
|
||||
|
||||
|
|
|
@ -36,9 +36,17 @@ class Article(Note):
|
|||
type: str = 'Article'
|
||||
|
||||
|
||||
@dataclass
|
||||
class Link():
|
||||
''' for tagging a book in a status '''
|
||||
href: str
|
||||
name: str
|
||||
type: str = 'Link'
|
||||
|
||||
@dataclass(init=False)
|
||||
class GeneratedNote(Note):
|
||||
''' just a re-typed note '''
|
||||
tag: List[Link]
|
||||
type: str = 'GeneratedNote'
|
||||
|
||||
|
||||
|
|
|
@ -122,11 +122,11 @@ class AbstractConnector(ABC):
|
|||
# atomic so that we don't save a work with no edition for vice versa
|
||||
with transaction.atomic():
|
||||
if not work:
|
||||
work_key = work_data.get('url')
|
||||
work_key = self.get_remote_id_from_data(work_data)
|
||||
work = self.create_book(work_key, work_data, models.Work)
|
||||
|
||||
if not edition:
|
||||
ed_key = edition_data.get('url')
|
||||
ed_key = self.get_remote_id_from_data(edition_data)
|
||||
edition = self.create_book(ed_key, edition_data, models.Edition)
|
||||
edition.default = True
|
||||
edition.parent_work = work
|
||||
|
@ -161,7 +161,8 @@ class AbstractConnector(ABC):
|
|||
author_text = []
|
||||
for author in self.get_authors_from_data(data):
|
||||
book.authors.add(author)
|
||||
author_text.append(author.display_name)
|
||||
if author.display_name:
|
||||
author_text.append(author.display_name)
|
||||
book.author_text = ', '.join(author_text)
|
||||
book.save()
|
||||
|
||||
|
@ -215,6 +216,11 @@ class AbstractConnector(ABC):
|
|||
return None
|
||||
|
||||
|
||||
@abstractmethod
|
||||
def get_remote_id_from_data(self, data):
|
||||
''' otherwise we won't properly set the remote_id in the db '''
|
||||
|
||||
|
||||
@abstractmethod
|
||||
def is_work_data(self, data):
|
||||
''' differentiate works and editions '''
|
||||
|
|
|
@ -41,14 +41,22 @@ class Connector(AbstractConnector):
|
|||
]
|
||||
|
||||
self.author_mappings = [
|
||||
Mapping('born', remote_field='birth_date', formatter=get_date),
|
||||
Mapping('died', remote_field='death_date', formatter=get_date),
|
||||
Mapping('name'),
|
||||
Mapping('bio'),
|
||||
Mapping('openlibrary_key'),
|
||||
Mapping('wikipedia_link'),
|
||||
Mapping('aliases'),
|
||||
Mapping('born', formatter=get_date),
|
||||
Mapping('died', formatter=get_date),
|
||||
]
|
||||
|
||||
|
||||
def get_remote_id_from_data(self, data):
|
||||
return data.get('id')
|
||||
|
||||
|
||||
def is_work_data(self, data):
|
||||
return data['book_type'] == 'Work'
|
||||
return data['type'] == 'Work'
|
||||
|
||||
|
||||
def get_edition_from_work_data(self, data):
|
||||
|
|
|
@ -73,6 +73,14 @@ class Connector(AbstractConnector):
|
|||
|
||||
|
||||
|
||||
def get_remote_id_from_data(self, data):
|
||||
try:
|
||||
key = data['key']
|
||||
except KeyError:
|
||||
raise ConnectorException('Invalid book data')
|
||||
return '%s/%s' % (self.books_url, key)
|
||||
|
||||
|
||||
def is_work_data(self, data):
|
||||
return bool(re.match(r'^[\/\w]+OL\d+W$', data['key']))
|
||||
|
||||
|
|
|
@ -51,6 +51,9 @@ class Connector(AbstractConnector):
|
|||
)
|
||||
|
||||
|
||||
def get_remote_id_from_data(self, data):
|
||||
pass
|
||||
|
||||
def is_work_data(self, data):
|
||||
pass
|
||||
|
||||
|
|
|
@ -225,6 +225,15 @@ def handle_create(activity):
|
|||
if not reply:
|
||||
return
|
||||
|
||||
# look up books
|
||||
book_urls = []
|
||||
if hasattr(activity, 'inReplyToBook'):
|
||||
book_urls.append(activity.inReplyToBook)
|
||||
if hasattr(activity, 'tag'):
|
||||
book_urls += [t.href for t in activity.tag if t.type == 'Book']
|
||||
for remote_id in book_urls:
|
||||
books_manager.get_or_create_book(remote_id)
|
||||
|
||||
model = models.activity_models[activity.type]
|
||||
status = activity.to_model(model)
|
||||
|
||||
|
|
|
@ -58,6 +58,16 @@ class Book(ActivitypubMixin, BookWyrmModel):
|
|||
''' the activitypub serialization should be a list of author ids '''
|
||||
return [a.remote_id for a in self.authors.all()]
|
||||
|
||||
@property
|
||||
def ap_cover(self):
|
||||
''' an image attachment '''
|
||||
if not self.cover or not hasattr(self.cover, 'url'):
|
||||
return []
|
||||
return [activitypub.Image(
|
||||
url='https://%s%s' % (DOMAIN, self.cover.url),
|
||||
)]
|
||||
|
||||
|
||||
activity_mappings = [
|
||||
ActivityMapping('id', 'remote_id'),
|
||||
|
||||
|
@ -90,6 +100,7 @@ class Book(ActivitypubMixin, BookWyrmModel):
|
|||
|
||||
ActivityMapping('lccn', 'lccn'),
|
||||
ActivityMapping('editions', 'editions_path'),
|
||||
ActivityMapping('attachment', 'ap_cover'),
|
||||
]
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
|
|
@ -72,7 +72,7 @@ class ImportItem(models.Model):
|
|||
def get_book_from_isbn(self):
|
||||
''' search by isbn '''
|
||||
search_result = books_manager.first_search_result(
|
||||
self.isbn, min_confidence=0.992
|
||||
self.isbn, min_confidence=0.995
|
||||
)
|
||||
if search_result:
|
||||
try:
|
||||
|
@ -90,7 +90,7 @@ class ImportItem(models.Model):
|
|||
self.data['Author']
|
||||
)
|
||||
search_result = books_manager.first_search_result(
|
||||
search_term, min_confidence=0.992
|
||||
search_term, min_confidence=0.995
|
||||
)
|
||||
if search_result:
|
||||
try:
|
||||
|
|
|
@ -57,6 +57,17 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
|||
''' structured replies block '''
|
||||
return self.to_replies()
|
||||
|
||||
@property
|
||||
def ap_tag(self):
|
||||
tags = []
|
||||
for book in self.mention_books.all():
|
||||
tags.append(activitypub.Link(
|
||||
href=book.local_id,
|
||||
name=book.title,
|
||||
type='Book'
|
||||
))
|
||||
return tags
|
||||
|
||||
shared_mappings = [
|
||||
ActivityMapping('id', 'remote_id'),
|
||||
ActivityMapping('url', 'remote_id'),
|
||||
|
@ -66,6 +77,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
|||
ActivityMapping('to', 'ap_to'),
|
||||
ActivityMapping('cc', 'ap_cc'),
|
||||
ActivityMapping('replies', 'ap_replies'),
|
||||
ActivityMapping('tag', 'ap_tag'),
|
||||
]
|
||||
|
||||
# serializing to bookwyrm expanded activitypub
|
||||
|
|
Loading…
Reference in a new issue