Serialize lists as custom activitypub object

This commit is contained in:
Mouse Reeve 2021-02-02 11:05:47 -08:00
parent d37f8f68d8
commit e61d8b7638
7 changed files with 53 additions and 27 deletions

View file

@ -10,6 +10,7 @@ from .note import Note, GeneratedNote, Article, Comment, Review, Quotation
from .note import Tombstone from .note import Tombstone
from .interaction import Boost, Like from .interaction import Boost, Like
from .ordered_collection import OrderedCollection, OrderedCollectionPage from .ordered_collection import OrderedCollection, OrderedCollectionPage
from .ordered_collection import BookList
from .person import Person, PublicKey from .person import Person, PublicKey
from .response import ActivitypubResponse from .response import ActivitypubResponse
from .book import Edition, Work, Author from .book import Edition, Work, Author

View file

@ -130,6 +130,7 @@ class ActivityObject:
def serialize(self): def serialize(self):
''' convert to dictionary with context attr ''' ''' convert to dictionary with context attr '''
data = self.__dict__ data = self.__dict__
data = {k:v for (k, v) in data.items() if v is not None}
data['@context'] = 'https://www.w3.org/ns/activitystreams' data['@context'] = 'https://www.w3.org/ns/activitystreams'
return data return data

View file

@ -12,13 +12,20 @@ class OrderedCollection(ActivityObject):
first: str first: str
last: str = None last: str = None
name: str = None name: str = None
summary: str = None
owner: str = None owner: str = None
to: List[str] = field(default_factory=lambda: []) to: List[str] = field(default_factory=lambda: [])
cc: List[str] = field(default_factory=lambda: []) cc: List[str] = field(default_factory=lambda: [])
type: str = 'OrderedCollection' type: str = 'OrderedCollection'
@dataclass(init=False)
class BookList(OrderedCollection):
''' structure of an ordered collection activity '''
summary: str = None
curation: str = 'closed'
type: str = 'List'
@dataclass(init=False) @dataclass(init=False)
class OrderedCollectionPage(ActivityObject): class OrderedCollectionPage(ActivityObject):
''' structure of an ordered collection activity ''' ''' structure of an ordered collection activity '''

View file

@ -140,20 +140,7 @@ class ActivitypubMixin:
def to_activity(self): def to_activity(self):
''' convert from a model to an activity ''' ''' convert from a model to an activity '''
activity = {} activity = generate_activity(self)
for field in self.activity_fields:
field.set_activity_from_field(activity, self)
if hasattr(self, 'serialize_reverse_fields'):
# for example, editions of a work
for model_field_name, activity_field_name, sort_field in \
self.serialize_reverse_fields:
related_field = getattr(self, model_field_name)
activity[activity_field_name] = \
unfurl_related_field(related_field, sort_field)
if not activity.get('id'):
activity['id'] = self.get_remote_id()
return self.activity_serializer(**activity).serialize() return self.activity_serializer(**activity).serialize()
@ -225,7 +212,7 @@ class OrderedCollectionPageMixin(ActivitypubMixin):
def to_ordered_collection(self, queryset, \ def to_ordered_collection(self, queryset, \
remote_id=None, page=False, **kwargs): remote_id=None, page=False, collection_only=False, **kwargs):
''' an ordered collection of whatevers ''' ''' an ordered collection of whatevers '''
if not queryset.ordered: if not queryset.ordered:
raise RuntimeError('queryset must be ordered') raise RuntimeError('queryset must be ordered')
@ -234,18 +221,24 @@ class OrderedCollectionPageMixin(ActivitypubMixin):
if page: if page:
return to_ordered_collection_page( return to_ordered_collection_page(
queryset, remote_id, **kwargs) queryset, remote_id, **kwargs)
name = self.name if hasattr(self, 'name') else None
owner = self.user.remote_id if hasattr(self, 'user') else '' if collection_only or not hasattr(self, 'activity_serializer'):
serializer = activitypub.OrderedCollection
activity = {}
else:
serializer = self.activity_serializer
# a dict from the model fields
activity = generate_activity(self)
if remote_id:
activity['id'] = remote_id
paginated = Paginator(queryset, PAGE_LENGTH) paginated = Paginator(queryset, PAGE_LENGTH)
return activitypub.OrderedCollection( # add computed fields specific to orderd collections
id=remote_id, activity['totalItems'] = paginated.count
totalItems=paginated.count, activity['first'] = '%s?page=1' % remote_id
name=name, activity['last'] = '%s?page=%d' % (remote_id, paginated.num_pages)
owner=owner,
first='%s?page=1' % remote_id, return serializer(**activity).serialize()
last='%s?page=%d' % (remote_id, paginated.num_pages)
).serialize()
# pylint: disable=unused-argument # pylint: disable=unused-argument
@ -287,3 +280,22 @@ class OrderedCollectionMixin(OrderedCollectionPageMixin):
def to_activity(self, **kwargs): def to_activity(self, **kwargs):
''' an ordered collection of the specified model queryset ''' ''' an ordered collection of the specified model queryset '''
return self.to_ordered_collection(self.collection_queryset, **kwargs) return self.to_ordered_collection(self.collection_queryset, **kwargs)
def generate_activity(obj):
''' go through the fields on an object '''
activity = {}
for field in obj.activity_fields:
field.set_activity_from_field(activity, obj)
if hasattr(obj, 'serialize_reverse_fields'):
# for example, editions of a work
for model_field_name, activity_field_name, sort_field in \
obj.serialize_reverse_fields:
related_field = getattr(obj, model_field_name)
activity[activity_field_name] = \
unfurl_related_field(related_field, sort_field)
if not activity.get('id'):
activity['id'] = obj.get_remote_id()
return activity

View file

@ -213,7 +213,10 @@ class PrivacyField(ActivitypubFieldMixin, models.CharField):
setattr(instance, self.name, 'followers') setattr(instance, self.name, 'followers')
def set_activity_from_field(self, activity, instance): def set_activity_from_field(self, activity, instance):
mentions = [u.remote_id for u in instance.mention_users.all()] # explicitly to anyone mentioned (statuses only)
mentions = []
if hasattr(instance, 'mention_users'):
mentions = [u.remote_id for u in instance.mention_users.all()]
# this is a link to the followers list # this is a link to the followers list
followers = instance.user.__class__._meta.get_field('followers')\ followers = instance.user.__class__._meta.get_field('followers')\
.field_to_activity(instance.user.followers) .field_to_activity(instance.user.followers)

View file

@ -33,6 +33,7 @@ class List(OrderedCollectionMixin, BookWyrmModel):
through='ListItem', through='ListItem',
through_fields=('book_list', 'book'), through_fields=('book_list', 'book'),
) )
activity_serializer = activitypub.BookList
def get_remote_id(self): def get_remote_id(self):
''' don't want the user to be in there in this case ''' ''' don't want the user to be in there in this case '''

View file

@ -94,6 +94,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
return self.to_ordered_collection( return self.to_ordered_collection(
self.replies(self), self.replies(self),
remote_id='%s/replies' % self.remote_id, remote_id='%s/replies' % self.remote_id,
collection_only=True,
**kwargs **kwargs
) )