Merge branch 'main' into production

This commit is contained in:
Mouse Reeve 2020-10-31 11:05:53 -07:00
commit 8d21cfa707
16 changed files with 95 additions and 17 deletions

View file

@ -4,7 +4,7 @@ import sys
from .base_activity import ActivityEncoder, Image, PublicKey, Signature from .base_activity import ActivityEncoder, Image, PublicKey, Signature
from .note import Note, GeneratedNote, Article, Comment, Review, Quotation from .note import Note, GeneratedNote, Article, Comment, Review, Quotation
from .note import Tombstone from .note import Tombstone, Link
from .interaction import Boost, Like from .interaction import Boost, Like
from .ordered_collection import OrderedCollection, OrderedCollectionPage from .ordered_collection import OrderedCollection, OrderedCollectionPage
from .person import Person from .person import Person

View file

@ -15,7 +15,6 @@ class ActivityEncoder(JSONEncoder):
@dataclass @dataclass
class Image: class Image:
''' image block ''' ''' image block '''
mediaType: str
url: str url: str
type: str = 'Image' type: str = 'Image'

View file

@ -36,9 +36,17 @@ class Article(Note):
type: str = 'Article' type: str = 'Article'
@dataclass
class Link():
''' for tagging a book in a status '''
href: str
name: str
type: str = 'Link'
@dataclass(init=False) @dataclass(init=False)
class GeneratedNote(Note): class GeneratedNote(Note):
''' just a re-typed note ''' ''' just a re-typed note '''
tag: List[Link]
type: str = 'GeneratedNote' type: str = 'GeneratedNote'

View file

@ -26,7 +26,6 @@ class Delete(Verb):
''' Create activity ''' ''' Create activity '''
to: List to: List
cc: List cc: List
signature: Signature
type: str = 'Delete' type: str = 'Delete'

View file

@ -122,11 +122,11 @@ class AbstractConnector(ABC):
# atomic so that we don't save a work with no edition for vice versa # atomic so that we don't save a work with no edition for vice versa
with transaction.atomic(): with transaction.atomic():
if not work: 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) work = self.create_book(work_key, work_data, models.Work)
if not edition: 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 = self.create_book(ed_key, edition_data, models.Edition)
edition.default = True edition.default = True
edition.parent_work = work edition.parent_work = work
@ -161,7 +161,8 @@ class AbstractConnector(ABC):
author_text = [] author_text = []
for author in self.get_authors_from_data(data): for author in self.get_authors_from_data(data):
book.authors.add(author) 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.author_text = ', '.join(author_text)
book.save() book.save()
@ -215,6 +216,11 @@ class AbstractConnector(ABC):
return None return None
@abstractmethod
def get_remote_id_from_data(self, data):
''' otherwise we won't properly set the remote_id in the db '''
@abstractmethod @abstractmethod
def is_work_data(self, data): def is_work_data(self, data):
''' differentiate works and editions ''' ''' differentiate works and editions '''

View file

@ -41,14 +41,22 @@ class Connector(AbstractConnector):
] ]
self.author_mappings = [ self.author_mappings = [
Mapping('born', remote_field='birth_date', formatter=get_date), Mapping('name'),
Mapping('died', remote_field='death_date', formatter=get_date),
Mapping('bio'), 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): def is_work_data(self, data):
return data['book_type'] == 'Work' return data['type'] == 'Work'
def get_edition_from_work_data(self, data): def get_edition_from_work_data(self, data):

View file

@ -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): def is_work_data(self, data):
return bool(re.match(r'^[\/\w]+OL\d+W$', data['key'])) return bool(re.match(r'^[\/\w]+OL\d+W$', data['key']))

View file

@ -51,6 +51,9 @@ class Connector(AbstractConnector):
) )
def get_remote_id_from_data(self, data):
pass
def is_work_data(self, data): def is_work_data(self, data):
pass pass

View file

@ -225,6 +225,15 @@ def handle_create(activity):
if not reply: if not reply:
return 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] model = models.activity_models[activity.type]
status = activity.to_model(model) status = activity.to_model(model)

View file

@ -108,6 +108,20 @@ class ActivitypubMixin:
).serialize() ).serialize()
def to_delete_activity(self, user):
''' notice of deletion '''
# this should be a tombstone
activity_object = self.to_activity()
return activitypub.Delete(
id=self.remote_id + '/activity',
actor=user.remote_id,
to=['%s/followers' % user.remote_id],
cc=['https://www.w3.org/ns/activitystreams#Public'],
object=activity_object,
).serialize()
def to_update_activity(self, user): def to_update_activity(self, user):
''' wrapper for Updates to an activity ''' ''' wrapper for Updates to an activity '''
activity_id = '%s#update/%s' % (user.remote_id, uuid4()) activity_id = '%s#update/%s' % (user.remote_id, uuid4())

View file

@ -58,6 +58,16 @@ class Book(ActivitypubMixin, BookWyrmModel):
''' the activitypub serialization should be a list of author ids ''' ''' the activitypub serialization should be a list of author ids '''
return [a.remote_id for a in self.authors.all()] 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 = [ activity_mappings = [
ActivityMapping('id', 'remote_id'), ActivityMapping('id', 'remote_id'),
@ -90,6 +100,7 @@ class Book(ActivitypubMixin, BookWyrmModel):
ActivityMapping('lccn', 'lccn'), ActivityMapping('lccn', 'lccn'),
ActivityMapping('editions', 'editions_path'), ActivityMapping('editions', 'editions_path'),
ActivityMapping('attachment', 'ap_cover'),
] ]
def save(self, *args, **kwargs): def save(self, *args, **kwargs):

View file

@ -72,7 +72,7 @@ class ImportItem(models.Model):
def get_book_from_isbn(self): def get_book_from_isbn(self):
''' search by isbn ''' ''' search by isbn '''
search_result = books_manager.first_search_result( search_result = books_manager.first_search_result(
self.isbn, min_confidence=0.992 self.isbn, min_confidence=0.995
) )
if search_result: if search_result:
try: try:
@ -90,7 +90,7 @@ class ImportItem(models.Model):
self.data['Author'] self.data['Author']
) )
search_result = books_manager.first_search_result( search_result = books_manager.first_search_result(
search_term, min_confidence=0.992 search_term, min_confidence=0.995
) )
if search_result: if search_result:
try: try:

View file

@ -57,6 +57,17 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
''' structured replies block ''' ''' structured replies block '''
return self.to_replies() 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 = [ shared_mappings = [
ActivityMapping('id', 'remote_id'), ActivityMapping('id', 'remote_id'),
ActivityMapping('url', 'remote_id'), ActivityMapping('url', 'remote_id'),
@ -66,6 +77,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
ActivityMapping('to', 'ap_to'), ActivityMapping('to', 'ap_to'),
ActivityMapping('cc', 'ap_cc'), ActivityMapping('cc', 'ap_cc'),
ActivityMapping('replies', 'ap_replies'), ActivityMapping('replies', 'ap_replies'),
ActivityMapping('tag', 'ap_tag'),
] ]
# serializing to bookwyrm expanded activitypub # serializing to bookwyrm expanded activitypub

View file

@ -204,7 +204,7 @@ def handle_imported_book(user, item, include_reviews, privacy):
def handle_delete_status(user, status): def handle_delete_status(user, status):
''' delete a status and broadcast deletion to other servers ''' ''' delete a status and broadcast deletion to other servers '''
delete_status(status) delete_status(status)
broadcast(user, status.to_activity()) broadcast(user, status.to_delete_activity(user))
def handle_status(user, form): def handle_status(user, form):

View file

@ -116,3 +116,8 @@ input.toggle-control:checked ~ .toggle-content {
content: "\e903"; content: "\e903";
right: 0; right: 0;
} }
/* --- BLOCKQUOTE --- */
blockquote {
white-space: pre-line;
}

View file

@ -1,6 +1,2 @@
{% if book.description %} <blockquote class="content">{% if book.description %}{{ book.description }}{% elif book.parent_work.description %}{{ book.parent_work.description }}{% endif %}</blockquote>
<blockquote class="content">{{ book.description }}</blockquote>
{% elif book.parent_work.description %}
<blockquote>{{ book.parent_work.description }}</blockquote>
{% endif %}