Receive and save incoming images

This commit is contained in:
Mouse Reeve 2020-11-24 11:25:07 -08:00
parent 88e4705717
commit ad7ce6595b
7 changed files with 62 additions and 29 deletions

View file

@ -5,7 +5,8 @@ import sys
from .base_activity import ActivityEncoder, Image, PublicKey, Signature
from .base_activity import Link, Mention
from .base_activity import ActivitySerializerError
from .base_activity import tag_formatter, image_formatter
from .base_activity import tag_formatter
from .base_activity import image_formatter, image_attachments_formatter
from .note import Note, GeneratedNote, Article, Comment, Review, Quotation
from .note import Tombstone
from .interaction import Boost, Like

View file

@ -5,7 +5,8 @@ from uuid import uuid4
from django.core.files.base import ContentFile
from django.db.models.fields.related_descriptors \
import ForwardManyToOneDescriptor, ManyToManyDescriptor
import ForwardManyToOneDescriptor, ManyToManyDescriptor, \
ReverseManyToOneDescriptor
from django.db.models.fields.files import ImageFileDescriptor
import requests
@ -95,6 +96,7 @@ class ActivityObject:
model_fields = [m.name for m in model._meta.get_fields()]
mapped_fields = {}
many_to_many_fields = {}
one_to_many_fields = {}
image_fields = {}
for mapping in model.activity_mappings:
@ -107,15 +109,20 @@ class ActivityObject:
value = getattr(self, mapping.activity_key)
model_field = getattr(model, mapping.model_key)
# remote_id -> foreign key resolver
if isinstance(model_field, ForwardManyToOneDescriptor) and value:
fk_model = model_field.field.related_model
value = resolve_foreign_key(fk_model, value)
formatted_value = mapping.model_formatter(value)
if isinstance(model_field, ManyToManyDescriptor):
if isinstance(model_field, ForwardManyToOneDescriptor) and \
formatted_value:
# foreign key remote id reolver
fk_model = model_field.field.related_model
reference = resolve_foreign_key(fk_model, formatted_value)
mapped_fields[mapping.model_key] = reference
elif isinstance(model_field, ManyToManyDescriptor):
many_to_many_fields[mapping.model_key] = formatted_value
elif isinstance(model_field, ReverseManyToOneDescriptor):
# attachments on statuses, for example
one_to_many_fields[mapping.model_key] = formatted_value
elif isinstance(model_field, ImageFileDescriptor):
# image fields need custom handling
image_fields[mapping.model_key] = formatted_value
else:
mapped_fields[mapping.model_key] = formatted_value
@ -137,6 +144,18 @@ class ActivityObject:
# add images
for (model_key, value) in image_fields.items():
getattr(instance, model_key).save(*value, save=True)
# add one to many fields
for (model_key, values) in one_to_many_fields.items():
items = []
for item in values:
# the reference id wasn't available at creation time
setattr(item, instance.__class__.__name__.lower(), instance)
item.save()
items.append(item)
if items:
getattr(instance, model_key).set(items)
instance.save()
return instance
@ -167,15 +186,14 @@ def resolve_foreign_key(model, remote_id):
return result
def tag_formatter(tags):
def tag_formatter(tags, tag_type):
''' 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')
for tag in [t for t in tags if t.get('type') == tag_type]:
if not tag_type in types:
continue
remote_id = tag.get('href')
@ -203,3 +221,14 @@ def image_formatter(image_json):
image_name = str(uuid4()) + '.' + url.split('.')[-1]
image_content = ContentFile(response.content)
return [image_name, image_content]
def image_attachments_formatter(images_json):
''' deserialize a list of images '''
attachments = []
for image in images_json:
attachment = models.Attachment()
image_field = image_formatter(image)
attachment.image.save(*image_field, save=False)
attachments.append(attachment)
return attachments

View file

@ -1,4 +1,4 @@
# Generated by Django 3.0.7 on 2020-11-24 00:41
# Generated by Django 3.0.7 on 2020-11-24 17:46
from django.db import migrations, models
import django.db.models.deletion
@ -19,7 +19,7 @@ class Migration(migrations.Migration):
('updated_date', models.DateTimeField(auto_now=True)),
('remote_id', models.CharField(max_length=255, null=True)),
('image', models.ImageField(blank=True, null=True, upload_to='status/')),
('status', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='bookwyrm.Status')),
('status', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='bookwyrm.Status')),
],
options={
'abstract': False,

View file

@ -7,7 +7,7 @@ from .connector import Connector
from .relationship import UserFollows, UserFollowRequest, UserBlocks
from .shelf import Shelf, ShelfBook
from .status import Status, GeneratedNote, Review, Comment, Quotation
from .status import Favorite, Boost, Notification, ReadThrough
from .status import Attachment, Favorite, Boost, Notification, ReadThrough
from .tag import Tag
from .user import User
from .federated_server import FederatedServer

View file

@ -276,10 +276,5 @@ def image_formatter(image, default_path=None):
def image_attachments_formatter(images):
''' create a list of image attachemnts '''
if not isinstance(images, list):
images = [images]
attachments = []
for image in images:
attachments.append(image_formatter(image))
return attachments
''' create a list of image attachments '''
return [image_formatter(i) for i in images]

View file

@ -100,7 +100,9 @@ class Book(ActivitypubMixin, BookWyrmModel):
ActivityMapping('editions', 'editions_path'),
ActivityMapping(
'attachment', 'cover',
image_attachments_formatter
# this expects an iterable and the field is just an image
lambda x: image_attachments_formatter([x]),
lambda x: activitypub.image_attachments_formatter(x)[0]
),
]

View file

@ -7,7 +7,7 @@ from model_utils.managers import InheritanceManager
from bookwyrm import activitypub
from .base_model import ActivitypubMixin, OrderedCollectionPageMixin
from .base_model import ActivityMapping, BookWyrmModel, PrivacyLevels
from .base_model import tag_formatter
from .base_model import tag_formatter, image_attachments_formatter
class Status(OrderedCollectionPageMixin, BookWyrmModel):
@ -80,13 +80,18 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
ActivityMapping(
'tag', 'mention_books',
lambda x: tag_formatter(x, 'title', 'Book'),
activitypub.tag_formatter
lambda x: activitypub.tag_formatter(x, 'Book')
),
ActivityMapping(
'tag', 'mention_users',
lambda x: tag_formatter(x, 'username', 'Mention'),
activitypub.tag_formatter
lambda x: activitypub.tag_formatter(x, 'Mention')
),
ActivityMapping(
'attachment', 'attachments',
lambda x: image_attachments_formatter(x.all()),
activitypub.image_attachments_formatter
)
]
# serializing to bookwyrm expanded activitypub
@ -140,9 +145,10 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
def save(self, *args, **kwargs):
''' update user active time '''
self.user.last_active_date = timezone.now()
self.user.save()
super().save(*args, **kwargs)
if self.user.local:
self.user.last_active_date = timezone.now()
self.user.save()
return super().save(*args, **kwargs)
class Attachment(BookWyrmModel):
@ -150,7 +156,7 @@ class Attachment(BookWyrmModel):
status = models.ForeignKey(
'Status',
on_delete=models.CASCADE,
related_name='items'
related_name='attachments'
)
image = models.ImageField(upload_to='status/', null=True, blank=True)