forked from mirrors/bookwyrm
Receive and save incoming images
This commit is contained in:
parent
88e4705717
commit
ad7ce6595b
7 changed files with 62 additions and 29 deletions
|
@ -5,7 +5,8 @@ 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
|
||||||
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 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,8 @@ from uuid import uuid4
|
||||||
|
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
from django.db.models.fields.related_descriptors \
|
from django.db.models.fields.related_descriptors \
|
||||||
import ForwardManyToOneDescriptor, ManyToManyDescriptor
|
import ForwardManyToOneDescriptor, ManyToManyDescriptor, \
|
||||||
|
ReverseManyToOneDescriptor
|
||||||
from django.db.models.fields.files import ImageFileDescriptor
|
from django.db.models.fields.files import ImageFileDescriptor
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
@ -95,6 +96,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 = {}
|
many_to_many_fields = {}
|
||||||
|
one_to_many_fields = {}
|
||||||
image_fields = {}
|
image_fields = {}
|
||||||
|
|
||||||
for mapping in model.activity_mappings:
|
for mapping in model.activity_mappings:
|
||||||
|
@ -107,15 +109,20 @@ class ActivityObject:
|
||||||
value = getattr(self, mapping.activity_key)
|
value = getattr(self, mapping.activity_key)
|
||||||
model_field = getattr(model, mapping.model_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)
|
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
|
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):
|
elif isinstance(model_field, ImageFileDescriptor):
|
||||||
|
# image fields need custom handling
|
||||||
image_fields[mapping.model_key] = formatted_value
|
image_fields[mapping.model_key] = formatted_value
|
||||||
else:
|
else:
|
||||||
mapped_fields[mapping.model_key] = formatted_value
|
mapped_fields[mapping.model_key] = formatted_value
|
||||||
|
@ -137,6 +144,18 @@ class ActivityObject:
|
||||||
# add images
|
# add images
|
||||||
for (model_key, value) in image_fields.items():
|
for (model_key, value) in image_fields.items():
|
||||||
getattr(instance, model_key).save(*value, save=True)
|
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
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
@ -167,15 +186,14 @@ def resolve_foreign_key(model, remote_id):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def tag_formatter(tags):
|
def tag_formatter(tags, tag_type):
|
||||||
''' helper function to extract foreign keys from tag activity json '''
|
''' helper function to extract foreign keys from tag activity json '''
|
||||||
items = []
|
items = []
|
||||||
types = {
|
types = {
|
||||||
'Book': models.Book,
|
'Book': models.Book,
|
||||||
'Mention': models.User,
|
'Mention': models.User,
|
||||||
}
|
}
|
||||||
for tag in tags:
|
for tag in [t for t in tags if t.get('type') == tag_type]:
|
||||||
tag_type = tag.get('type')
|
|
||||||
if not tag_type in types:
|
if not tag_type in types:
|
||||||
continue
|
continue
|
||||||
remote_id = tag.get('href')
|
remote_id = tag.get('href')
|
||||||
|
@ -203,3 +221,14 @@ def image_formatter(image_json):
|
||||||
image_name = str(uuid4()) + '.' + url.split('.')[-1]
|
image_name = str(uuid4()) + '.' + url.split('.')[-1]
|
||||||
image_content = ContentFile(response.content)
|
image_content = ContentFile(response.content)
|
||||||
return [image_name, image_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
|
||||||
|
|
|
@ -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
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
@ -19,7 +19,7 @@ class Migration(migrations.Migration):
|
||||||
('updated_date', models.DateTimeField(auto_now=True)),
|
('updated_date', models.DateTimeField(auto_now=True)),
|
||||||
('remote_id', models.CharField(max_length=255, null=True)),
|
('remote_id', models.CharField(max_length=255, null=True)),
|
||||||
('image', models.ImageField(blank=True, null=True, upload_to='status/')),
|
('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={
|
options={
|
||||||
'abstract': False,
|
'abstract': False,
|
||||||
|
|
|
@ -7,7 +7,7 @@ from .connector import Connector
|
||||||
from .relationship import UserFollows, UserFollowRequest, UserBlocks
|
from .relationship import UserFollows, UserFollowRequest, UserBlocks
|
||||||
from .shelf import Shelf, ShelfBook
|
from .shelf import Shelf, ShelfBook
|
||||||
from .status import Status, GeneratedNote, Review, Comment, Quotation
|
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 .tag import Tag
|
||||||
from .user import User
|
from .user import User
|
||||||
from .federated_server import FederatedServer
|
from .federated_server import FederatedServer
|
||||||
|
|
|
@ -276,10 +276,5 @@ def image_formatter(image, default_path=None):
|
||||||
|
|
||||||
|
|
||||||
def image_attachments_formatter(images):
|
def image_attachments_formatter(images):
|
||||||
''' create a list of image attachemnts '''
|
''' create a list of image attachments '''
|
||||||
if not isinstance(images, list):
|
return [image_formatter(i) for i in images]
|
||||||
images = [images]
|
|
||||||
attachments = []
|
|
||||||
for image in images:
|
|
||||||
attachments.append(image_formatter(image))
|
|
||||||
return attachments
|
|
||||||
|
|
|
@ -100,7 +100,9 @@ class Book(ActivitypubMixin, BookWyrmModel):
|
||||||
ActivityMapping('editions', 'editions_path'),
|
ActivityMapping('editions', 'editions_path'),
|
||||||
ActivityMapping(
|
ActivityMapping(
|
||||||
'attachment', 'cover',
|
'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]
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -7,7 +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
|
from .base_model import tag_formatter, image_attachments_formatter
|
||||||
|
|
||||||
|
|
||||||
class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
||||||
|
@ -80,13 +80,18 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
||||||
ActivityMapping(
|
ActivityMapping(
|
||||||
'tag', 'mention_books',
|
'tag', 'mention_books',
|
||||||
lambda x: tag_formatter(x, 'title', 'Book'),
|
lambda x: tag_formatter(x, 'title', 'Book'),
|
||||||
activitypub.tag_formatter
|
lambda x: activitypub.tag_formatter(x, 'Book')
|
||||||
),
|
),
|
||||||
ActivityMapping(
|
ActivityMapping(
|
||||||
'tag', 'mention_users',
|
'tag', 'mention_users',
|
||||||
lambda x: tag_formatter(x, 'username', 'Mention'),
|
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
|
# serializing to bookwyrm expanded activitypub
|
||||||
|
@ -140,9 +145,10 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
''' update user active time '''
|
''' update user active time '''
|
||||||
|
if self.user.local:
|
||||||
self.user.last_active_date = timezone.now()
|
self.user.last_active_date = timezone.now()
|
||||||
self.user.save()
|
self.user.save()
|
||||||
super().save(*args, **kwargs)
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Attachment(BookWyrmModel):
|
class Attachment(BookWyrmModel):
|
||||||
|
@ -150,7 +156,7 @@ class Attachment(BookWyrmModel):
|
||||||
status = models.ForeignKey(
|
status = models.ForeignKey(
|
||||||
'Status',
|
'Status',
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='items'
|
related_name='attachments'
|
||||||
)
|
)
|
||||||
image = models.ImageField(upload_to='status/', null=True, blank=True)
|
image = models.ImageField(upload_to='status/', null=True, blank=True)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue