mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-12-01 22:11:16 +00:00
Merge pull request #359 from mouse-reeve/activity-tags
Serialize and deserialize activitypub "Tags" on statuses
This commit is contained in:
commit
56638f79cf
5 changed files with 67 additions and 28 deletions
|
@ -4,7 +4,7 @@ import sys
|
||||||
|
|
||||||
from .base_activity import ActivityEncoder, Image, PublicKey, Signature
|
from .base_activity import ActivityEncoder, Image, PublicKey, Signature
|
||||||
from .base_activity import Link, Mention
|
from .base_activity import Link, Mention
|
||||||
from .base_activity import ActivitySerializerError
|
from .base_activity import ActivitySerializerError, tag_formatter
|
||||||
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
|
||||||
from .interaction import Boost, Like
|
from .interaction import Boost, Like
|
||||||
|
|
|
@ -5,7 +5,7 @@ from json import JSONEncoder
|
||||||
from bookwyrm import books_manager, models
|
from bookwyrm import books_manager, models
|
||||||
|
|
||||||
from django.db.models.fields.related_descriptors \
|
from django.db.models.fields.related_descriptors \
|
||||||
import ForwardManyToOneDescriptor
|
import ForwardManyToOneDescriptor, ManyToManyDescriptor
|
||||||
|
|
||||||
|
|
||||||
class ActivitySerializerError(ValueError):
|
class ActivitySerializerError(ValueError):
|
||||||
|
@ -90,6 +90,7 @@ class ActivityObject:
|
||||||
|
|
||||||
model_fields = [m.name for m in model._meta.get_fields()]
|
model_fields = [m.name for m in model._meta.get_fields()]
|
||||||
mapped_fields = {}
|
mapped_fields = {}
|
||||||
|
many_to_many_fields = {}
|
||||||
|
|
||||||
for mapping in model.activity_mappings:
|
for mapping in model.activity_mappings:
|
||||||
if mapping.model_key not in model_fields:
|
if mapping.model_key not in model_fields:
|
||||||
|
@ -106,7 +107,11 @@ class ActivityObject:
|
||||||
fk_model = model_field.field.related_model
|
fk_model = model_field.field.related_model
|
||||||
value = resolve_foreign_key(fk_model, value)
|
value = resolve_foreign_key(fk_model, value)
|
||||||
|
|
||||||
mapped_fields[mapping.model_key] = mapping.model_formatter(value)
|
formatted_value = mapping.model_formatter(value)
|
||||||
|
if isinstance(model_field, ManyToManyDescriptor):
|
||||||
|
many_to_many_fields[mapping.model_key] = formatted_value
|
||||||
|
else:
|
||||||
|
mapped_fields[mapping.model_key] = formatted_value
|
||||||
|
|
||||||
|
|
||||||
# updating an existing model isntance
|
# updating an existing model isntance
|
||||||
|
@ -114,10 +119,14 @@ class ActivityObject:
|
||||||
for k, v in mapped_fields.items():
|
for k, v in mapped_fields.items():
|
||||||
setattr(instance, k, v)
|
setattr(instance, k, v)
|
||||||
instance.save()
|
instance.save()
|
||||||
return instance
|
else:
|
||||||
|
# creating a new model instance
|
||||||
|
instance = model.objects.create(**mapped_fields)
|
||||||
|
|
||||||
# creating a new model instance
|
for (model_key, values) in many_to_many_fields.items():
|
||||||
return model.objects.create(**mapped_fields)
|
getattr(instance, model_key).set(values)
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
|
@ -129,7 +138,7 @@ class ActivityObject:
|
||||||
|
|
||||||
def resolve_foreign_key(model, remote_id):
|
def resolve_foreign_key(model, remote_id):
|
||||||
''' look up the remote_id on an activity json field '''
|
''' look up the remote_id on an activity json field '''
|
||||||
if model in [models.Edition, models.Work]:
|
if model in [models.Edition, models.Work, models.Book]:
|
||||||
return books_manager.get_or_create_book(remote_id)
|
return books_manager.get_or_create_book(remote_id)
|
||||||
|
|
||||||
result = model.objects
|
result = model.objects
|
||||||
|
@ -145,3 +154,23 @@ def resolve_foreign_key(model, remote_id):
|
||||||
'Could not resolve remote_id in %s model: %s' % \
|
'Could not resolve remote_id in %s model: %s' % \
|
||||||
(model.__name__, remote_id))
|
(model.__name__, remote_id))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def tag_formatter(tags):
|
||||||
|
''' helper function to extract foreign keys from tag activity json '''
|
||||||
|
items = []
|
||||||
|
types = {
|
||||||
|
'Book': models.Book,
|
||||||
|
'Mention': models.User,
|
||||||
|
}
|
||||||
|
for tag in tags:
|
||||||
|
tag_type = tag.get('type')
|
||||||
|
if not tag_type in types:
|
||||||
|
continue
|
||||||
|
remote_id = tag.get('href')
|
||||||
|
try:
|
||||||
|
item = resolve_foreign_key(types[tag_type], remote_id)
|
||||||
|
except ActivitySerializerError:
|
||||||
|
continue
|
||||||
|
items.append(item)
|
||||||
|
return items
|
||||||
|
|
|
@ -253,7 +253,6 @@ def handle_delete_status(activity):
|
||||||
status_builder.delete_status(status)
|
status_builder.delete_status(status)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def handle_favorite(activity):
|
def handle_favorite(activity):
|
||||||
''' approval of your good good post '''
|
''' approval of your good good post '''
|
||||||
|
|
|
@ -72,7 +72,14 @@ class ActivitypubMixin:
|
||||||
value = value.remote_id
|
value = value.remote_id
|
||||||
if isinstance(value, datetime):
|
if isinstance(value, datetime):
|
||||||
value = value.isoformat()
|
value = value.isoformat()
|
||||||
fields[mapping.activity_key] = mapping.activity_formatter(value)
|
result = mapping.activity_formatter(value)
|
||||||
|
if mapping.activity_key in fields and \
|
||||||
|
isinstance(fields[mapping.activity_key], list):
|
||||||
|
# there are two database fields that map to the same AP list
|
||||||
|
# this happens in status, which combines user and book tags
|
||||||
|
fields[mapping.activity_key] += result
|
||||||
|
else:
|
||||||
|
fields[mapping.activity_key] = result
|
||||||
|
|
||||||
if pure:
|
if pure:
|
||||||
return self.pure_activity_serializer(
|
return self.pure_activity_serializer(
|
||||||
|
@ -242,3 +249,15 @@ class ActivityMapping:
|
||||||
model_key: str
|
model_key: str
|
||||||
activity_formatter: Callable = lambda x: x
|
activity_formatter: Callable = lambda x: x
|
||||||
model_formatter: Callable = lambda x: x
|
model_formatter: Callable = lambda x: x
|
||||||
|
|
||||||
|
|
||||||
|
def tag_formatter(items, name_field, activity_type):
|
||||||
|
''' helper function to format lists of foreign keys into Tags '''
|
||||||
|
tags = []
|
||||||
|
for item in items.all():
|
||||||
|
tags.append(activitypub.Link(
|
||||||
|
href=item.remote_id,
|
||||||
|
name=getattr(item, name_field),
|
||||||
|
type=activity_type
|
||||||
|
))
|
||||||
|
return tags
|
||||||
|
|
|
@ -7,6 +7,7 @@ from model_utils.managers import InheritanceManager
|
||||||
from bookwyrm import activitypub
|
from bookwyrm import activitypub
|
||||||
from .base_model import ActivitypubMixin, OrderedCollectionPageMixin
|
from .base_model import ActivitypubMixin, OrderedCollectionPageMixin
|
||||||
from .base_model import ActivityMapping, BookWyrmModel, PrivacyLevels
|
from .base_model import ActivityMapping, BookWyrmModel, PrivacyLevels
|
||||||
|
from .base_model import tag_formatter
|
||||||
|
|
||||||
|
|
||||||
class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
||||||
|
@ -57,24 +58,6 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
||||||
''' structured replies block '''
|
''' structured replies block '''
|
||||||
return self.to_replies()
|
return self.to_replies()
|
||||||
|
|
||||||
@property
|
|
||||||
def ap_tag(self):
|
|
||||||
''' references to books and/or users '''
|
|
||||||
|
|
||||||
tags = []
|
|
||||||
for book in self.mention_books.all():
|
|
||||||
tags.append(activitypub.Link(
|
|
||||||
href=book.remote_id,
|
|
||||||
name=book.title,
|
|
||||||
type='Book'
|
|
||||||
))
|
|
||||||
for user in self.mention_users.all():
|
|
||||||
tags.append(activitypub.Mention(
|
|
||||||
href=user.remote_id,
|
|
||||||
name=user.username,
|
|
||||||
))
|
|
||||||
return tags
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ap_status_image(self):
|
def ap_status_image(self):
|
||||||
''' attach a book cover, if relevent '''
|
''' attach a book cover, if relevent '''
|
||||||
|
@ -94,7 +77,16 @@ 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'),
|
ActivityMapping(
|
||||||
|
'tag', 'mention_books',
|
||||||
|
lambda x: tag_formatter(x, 'title', 'Book'),
|
||||||
|
activitypub.tag_formatter
|
||||||
|
),
|
||||||
|
ActivityMapping(
|
||||||
|
'tag', 'mention_users',
|
||||||
|
lambda x: tag_formatter(x, 'username', 'Mention'),
|
||||||
|
activitypub.tag_formatter
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
# serializing to bookwyrm expanded activitypub
|
# serializing to bookwyrm expanded activitypub
|
||||||
|
|
Loading…
Reference in a new issue