forked from mirrors/bookwyrm
Use remote_id resolver to load books, user
This commit is contained in:
parent
81bdd2b3f1
commit
a93b5cf5bc
15 changed files with 115 additions and 93 deletions
|
@ -4,7 +4,7 @@ import sys
|
||||||
|
|
||||||
from .base_activity import ActivityEncoder, PublicKey, Signature
|
from .base_activity import ActivityEncoder, PublicKey, Signature
|
||||||
from .base_activity import Link, Mention
|
from .base_activity import Link, Mention
|
||||||
from .base_activity import ActivitySerializerError
|
from .base_activity import ActivitySerializerError, resolve_remote_id
|
||||||
from .image import Image
|
from .image import Image
|
||||||
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
|
||||||
|
@ -14,7 +14,7 @@ from .person import Person
|
||||||
from .book import Edition, Work, Author
|
from .book import Edition, Work, Author
|
||||||
from .verbs import Create, Delete, Undo, Update
|
from .verbs import Create, Delete, Undo, Update
|
||||||
from .verbs import Follow, Accept, Reject
|
from .verbs import Follow, Accept, Reject
|
||||||
from .verbs import Add, Remove
|
from .verbs import Add, AddBook, Remove
|
||||||
|
|
||||||
# this creates a list of all the Activity types that we can serialize,
|
# this creates a list of all the Activity types that we can serialize,
|
||||||
# so when an Activity comes in from outside, we can check if it's known
|
# so when an Activity comes in from outside, we can check if it's known
|
||||||
|
|
|
@ -3,15 +3,20 @@ from dataclasses import dataclass, fields, MISSING
|
||||||
from json import JSONEncoder
|
from json import JSONEncoder
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
|
import dateutil.parser
|
||||||
|
from dateutil.parser import ParserError
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models.fields.related_descriptors \
|
from django.db.models.fields.related_descriptors \
|
||||||
import ForwardManyToOneDescriptor, ManyToManyDescriptor, \
|
import ForwardManyToOneDescriptor, ManyToManyDescriptor, \
|
||||||
ReverseManyToOneDescriptor
|
ReverseManyToOneDescriptor
|
||||||
|
from django.db.models.fields import DateTimeField
|
||||||
from django.db.models.fields.files import ImageFileDescriptor
|
from django.db.models.fields.files import ImageFileDescriptor
|
||||||
|
from django.db.models.query_utils import DeferredAttribute
|
||||||
|
from django.utils import timezone
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from bookwyrm import books_manager, models
|
from bookwyrm import models
|
||||||
|
|
||||||
|
|
||||||
class ActivitySerializerError(ValueError):
|
class ActivitySerializerError(ValueError):
|
||||||
|
@ -106,11 +111,27 @@ class ActivityObject:
|
||||||
model_field = getattr(model, mapping.model_key)
|
model_field = getattr(model, mapping.model_key)
|
||||||
|
|
||||||
formatted_value = mapping.model_formatter(value)
|
formatted_value = mapping.model_formatter(value)
|
||||||
if isinstance(model_field, ForwardManyToOneDescriptor) and \
|
if isinstance(model_field, DeferredAttribute) and \
|
||||||
|
isinstance(model_field.field, DateTimeField):
|
||||||
|
print("DATE")
|
||||||
|
try:
|
||||||
|
formatted_value = timezone.make_aware(
|
||||||
|
dateutil.parser.parse(formatted_value)
|
||||||
|
)
|
||||||
|
except ParserError:
|
||||||
|
formatted_value = None
|
||||||
|
elif isinstance(model_field, ForwardManyToOneDescriptor) and \
|
||||||
formatted_value:
|
formatted_value:
|
||||||
# foreign key remote id reolver (work on Edition, for example)
|
# foreign key remote id reolver (work on Edition, for example)
|
||||||
fk_model = model_field.field.related_model
|
fk_model = model_field.field.related_model
|
||||||
reference = resolve_foreign_key(fk_model, formatted_value)
|
if isinstance(formatted_value, dict) and \
|
||||||
|
formatted_value.get('id'):
|
||||||
|
# if the AP field is a serialized object (as in Add)
|
||||||
|
remote_id = formatted_value['id']
|
||||||
|
else:
|
||||||
|
# if the AP field is just a remote_id (as in every other case)
|
||||||
|
remote_id = formatted_value
|
||||||
|
reference = resolve_remote_id(fk_model, remote_id)
|
||||||
mapped_fields[mapping.model_key] = reference
|
mapped_fields[mapping.model_key] = reference
|
||||||
elif isinstance(model_field, ManyToManyDescriptor):
|
elif isinstance(model_field, ManyToManyDescriptor):
|
||||||
# status mentions book/users
|
# status mentions book/users
|
||||||
|
@ -122,6 +143,8 @@ class ActivityObject:
|
||||||
# image fields need custom handling
|
# image fields need custom handling
|
||||||
image_fields[mapping.model_key] = formatted_value
|
image_fields[mapping.model_key] = formatted_value
|
||||||
else:
|
else:
|
||||||
|
if formatted_value == MISSING:
|
||||||
|
formatted_value = None
|
||||||
mapped_fields[mapping.model_key] = formatted_value
|
mapped_fields[mapping.model_key] = formatted_value
|
||||||
|
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
|
@ -153,12 +176,15 @@ class ActivityObject:
|
||||||
model = model_field.model
|
model = model_field.model
|
||||||
items = []
|
items = []
|
||||||
for link in values:
|
for link in values:
|
||||||
|
# check that the Type matches the model (because Status
|
||||||
|
# tags contain both user mentions and book tags)
|
||||||
|
if not model.activity_serializer.type == link.get('type'):
|
||||||
|
continue
|
||||||
items.append(
|
items.append(
|
||||||
resolve_foreign_key(model, link.get('href'))
|
resolve_remote_id(model, link.get('href'))
|
||||||
)
|
)
|
||||||
getattr(instance, model_key).set(items)
|
getattr(instance, model_key).set(items)
|
||||||
|
|
||||||
|
|
||||||
# add one to many fields
|
# add one to many fields
|
||||||
for (model_key, values) in one_to_many_fields.items():
|
for (model_key, values) in one_to_many_fields.items():
|
||||||
if values == MISSING:
|
if values == MISSING:
|
||||||
|
@ -183,11 +209,8 @@ class ActivityObject:
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def resolve_foreign_key(model, remote_id):
|
def resolve_remote_id(model, remote_id, refresh=False):
|
||||||
''' look up the remote_id on an activity json field '''
|
''' look up the remote_id in the database or load it remotely '''
|
||||||
if model in [models.Edition, models.Work, models.Book]:
|
|
||||||
return books_manager.get_or_create_book(remote_id)
|
|
||||||
|
|
||||||
result = model.objects
|
result = model.objects
|
||||||
if hasattr(model.objects, 'select_subclasses'):
|
if hasattr(model.objects, 'select_subclasses'):
|
||||||
result = result.select_subclasses()
|
result = result.select_subclasses()
|
||||||
|
@ -196,10 +219,10 @@ def resolve_foreign_key(model, remote_id):
|
||||||
result = result.filter(
|
result = result.filter(
|
||||||
remote_id=remote_id
|
remote_id=remote_id
|
||||||
).first()
|
).first()
|
||||||
if result:
|
if result and not refresh:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# failing that, load the data and create the object
|
# load the data and create the object
|
||||||
try:
|
try:
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
remote_id,
|
remote_id,
|
||||||
|
@ -215,7 +238,8 @@ def resolve_foreign_key(model, remote_id):
|
||||||
(model.__name__, remote_id))
|
(model.__name__, remote_id))
|
||||||
|
|
||||||
item = model.activity_serializer(**response.json())
|
item = model.activity_serializer(**response.json())
|
||||||
return item.to_model(model)
|
# if we're refreshing, "result" will be set and we'll update it
|
||||||
|
return item.to_model(model, instance=result)
|
||||||
|
|
||||||
|
|
||||||
def image_formatter(image_slug):
|
def image_formatter(image_slug):
|
||||||
|
|
|
@ -12,13 +12,13 @@ class Book(ActivityObject):
|
||||||
sortTitle: str = ''
|
sortTitle: str = ''
|
||||||
subtitle: str = ''
|
subtitle: str = ''
|
||||||
description: str = ''
|
description: str = ''
|
||||||
languages: List[str]
|
languages: List[str] = field(default_factory=lambda: [])
|
||||||
series: str = ''
|
series: str = ''
|
||||||
seriesNumber: str = ''
|
seriesNumber: str = ''
|
||||||
subjects: List[str]
|
subjects: List[str] = field(default_factory=lambda: [])
|
||||||
subjectPlaces: List[str]
|
subjectPlaces: List[str] = field(default_factory=lambda: [])
|
||||||
|
|
||||||
authors: List[str]
|
authors: List[str] = field(default_factory=lambda: [])
|
||||||
firstPublishedDate: str = ''
|
firstPublishedDate: str = ''
|
||||||
publishedDate: str = ''
|
publishedDate: str = ''
|
||||||
|
|
||||||
|
@ -33,22 +33,22 @@ class Book(ActivityObject):
|
||||||
@dataclass(init=False)
|
@dataclass(init=False)
|
||||||
class Edition(Book):
|
class Edition(Book):
|
||||||
''' Edition instance of a book object '''
|
''' Edition instance of a book object '''
|
||||||
isbn10: str
|
|
||||||
isbn13: str
|
|
||||||
oclcNumber: str
|
|
||||||
asin: str
|
|
||||||
pages: str
|
|
||||||
physicalFormat: str
|
|
||||||
publishers: List[str]
|
|
||||||
|
|
||||||
work: str
|
work: str
|
||||||
|
isbn10: str = ''
|
||||||
|
isbn13: str = ''
|
||||||
|
oclcNumber: str = ''
|
||||||
|
asin: str = ''
|
||||||
|
pages: str = ''
|
||||||
|
physicalFormat: str = ''
|
||||||
|
publishers: List[str] = field(default_factory=lambda: [])
|
||||||
|
|
||||||
type: str = 'Edition'
|
type: str = 'Edition'
|
||||||
|
|
||||||
|
|
||||||
@dataclass(init=False)
|
@dataclass(init=False)
|
||||||
class Work(Book):
|
class Work(Book):
|
||||||
''' work instance of a book object '''
|
''' work instance of a book object '''
|
||||||
lccn: str
|
lccn: str = ''
|
||||||
editions: List[str]
|
editions: List[str]
|
||||||
type: str = 'Work'
|
type: str = 'Work'
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ class OrderedCollection(ActivityObject):
|
||||||
first: str
|
first: str
|
||||||
last: str = ''
|
last: str = ''
|
||||||
name: str = ''
|
name: str = ''
|
||||||
|
owner: str = ''
|
||||||
type: str = 'OrderedCollection'
|
type: str = 'OrderedCollection'
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ from dataclasses import dataclass
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from .base_activity import ActivityObject, Signature
|
from .base_activity import ActivityObject, Signature
|
||||||
|
from .book import Book
|
||||||
|
|
||||||
@dataclass(init=False)
|
@dataclass(init=False)
|
||||||
class Verb(ActivityObject):
|
class Verb(ActivityObject):
|
||||||
|
@ -69,6 +70,13 @@ class Add(Verb):
|
||||||
type: str = 'Add'
|
type: str = 'Add'
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(init=False)
|
||||||
|
class AddBook(Verb):
|
||||||
|
'''Add activity that's aware of the book obj '''
|
||||||
|
target: Book
|
||||||
|
type: str = 'Add'
|
||||||
|
|
||||||
|
|
||||||
@dataclass(init=False)
|
@dataclass(init=False)
|
||||||
class Remove(Verb):
|
class Remove(Verb):
|
||||||
'''Remove activity '''
|
'''Remove activity '''
|
||||||
|
|
|
@ -16,23 +16,6 @@ def get_edition(book_id):
|
||||||
return book
|
return book
|
||||||
|
|
||||||
|
|
||||||
def get_or_create_book(remote_id):
|
|
||||||
''' pull up a book record by whatever means possible '''
|
|
||||||
book = models.Book.objects.select_subclasses().filter(
|
|
||||||
remote_id=remote_id
|
|
||||||
).first()
|
|
||||||
if book:
|
|
||||||
return book
|
|
||||||
|
|
||||||
connector = get_or_create_connector(remote_id)
|
|
||||||
|
|
||||||
# raises ConnectorException
|
|
||||||
book = connector.get_or_create_book(remote_id)
|
|
||||||
if book:
|
|
||||||
load_more_data.delay(book.id)
|
|
||||||
return book
|
|
||||||
|
|
||||||
|
|
||||||
def get_or_create_connector(remote_id):
|
def get_or_create_connector(remote_id):
|
||||||
''' get the connector related to the author's server '''
|
''' get the connector related to the author's server '''
|
||||||
url = urlparse(remote_id)
|
url = urlparse(remote_id)
|
||||||
|
|
|
@ -317,7 +317,7 @@ def handle_tag(activity):
|
||||||
user = get_or_create_remote_user(activity['actor'])
|
user = get_or_create_remote_user(activity['actor'])
|
||||||
if not user.local:
|
if not user.local:
|
||||||
# ordered collection weirndess so we can't just to_model
|
# ordered collection weirndess so we can't just to_model
|
||||||
book = books_manager.get_or_create_book(activity['object']['id'])
|
book = (activity['object']['id'])
|
||||||
name = activity['object']['target'].split('/')[-1]
|
name = activity['object']['target'].split('/')[-1]
|
||||||
name = unquote_plus(name)
|
name = unquote_plus(name)
|
||||||
models.Tag.objects.get_or_create(
|
models.Tag.objects.get_or_create(
|
||||||
|
@ -330,17 +330,7 @@ def handle_tag(activity):
|
||||||
@app.task
|
@app.task
|
||||||
def handle_shelve(activity):
|
def handle_shelve(activity):
|
||||||
''' putting a book on a shelf '''
|
''' putting a book on a shelf '''
|
||||||
user = get_or_create_remote_user(activity['actor'])
|
activitypub.AddBook(**activity).to_model(models.ShelfBook)
|
||||||
book = books_manager.get_or_create_book(activity['object'])
|
|
||||||
try:
|
|
||||||
shelf = models.Shelf.objects.get(remote_id=activity['target'])
|
|
||||||
except models.Shelf.DoesNotExist:
|
|
||||||
return
|
|
||||||
if shelf.user != user:
|
|
||||||
# this doesn't add up.
|
|
||||||
return
|
|
||||||
shelf.books.add(book)
|
|
||||||
shelf.save()
|
|
||||||
|
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
|
|
24
bookwyrm/migrations/0016_auto_20201128_1804.py
Normal file
24
bookwyrm/migrations/0016_auto_20201128_1804.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Generated by Django 3.0.7 on 2020-11-28 18:04
|
||||||
|
|
||||||
|
import bookwyrm.utils.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('bookwyrm', '0015_auto_20201128_0349'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='book',
|
||||||
|
name='subject_places',
|
||||||
|
field=bookwyrm.utils.fields.ArrayField(base_field=models.CharField(max_length=255), blank=True, default=list, null=True, size=None),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='book',
|
||||||
|
name='subjects',
|
||||||
|
field=bookwyrm.utils.fields.ArrayField(base_field=models.CharField(max_length=255), blank=True, default=list, null=True, size=None),
|
||||||
|
),
|
||||||
|
]
|
|
@ -227,12 +227,16 @@ class OrderedCollectionPageMixin(ActivitypubMixin):
|
||||||
name = ''
|
name = ''
|
||||||
if hasattr(self, 'name'):
|
if hasattr(self, 'name'):
|
||||||
name = self.name
|
name = self.name
|
||||||
|
owner = ''
|
||||||
|
if hasattr(self, 'user'):
|
||||||
|
owner = self.user.remote_id
|
||||||
|
|
||||||
size = queryset.count()
|
size = queryset.count()
|
||||||
return activitypub.OrderedCollection(
|
return activitypub.OrderedCollection(
|
||||||
id=remote_id,
|
id=remote_id,
|
||||||
totalItems=size,
|
totalItems=size,
|
||||||
name=name,
|
name=name,
|
||||||
|
owner=owner,
|
||||||
first='%s%s' % (remote_id, self.page()),
|
first='%s%s' % (remote_id, self.page()),
|
||||||
last='%s%s' % (remote_id, self.page(min_id=0))
|
last='%s%s' % (remote_id, self.page(min_id=0))
|
||||||
).serialize()
|
).serialize()
|
||||||
|
|
|
@ -41,10 +41,10 @@ class Book(ActivitypubMixin, BookWyrmModel):
|
||||||
series = models.CharField(max_length=255, blank=True, null=True)
|
series = models.CharField(max_length=255, blank=True, null=True)
|
||||||
series_number = models.CharField(max_length=255, blank=True, null=True)
|
series_number = models.CharField(max_length=255, blank=True, null=True)
|
||||||
subjects = ArrayField(
|
subjects = ArrayField(
|
||||||
models.CharField(max_length=255), blank=True, default=list
|
models.CharField(max_length=255), blank=True, null=True, default=list
|
||||||
)
|
)
|
||||||
subject_places = ArrayField(
|
subject_places = ArrayField(
|
||||||
models.CharField(max_length=255), blank=True, default=list
|
models.CharField(max_length=255), blank=True, null=True, default=list
|
||||||
)
|
)
|
||||||
# TODO: include an annotation about the type of authorship (ie, translator)
|
# TODO: include an annotation about the type of authorship (ie, translator)
|
||||||
authors = models.ManyToManyField('Author')
|
authors = models.ManyToManyField('Author')
|
||||||
|
@ -132,7 +132,8 @@ class Work(OrderedCollectionPageMixin, Book):
|
||||||
''' it'd be nice to serialize the edition instead but, recursion '''
|
''' it'd be nice to serialize the edition instead but, recursion '''
|
||||||
default = self.default_edition
|
default = self.default_edition
|
||||||
ed_list = [
|
ed_list = [
|
||||||
e.remote_id for e in self.edition_set.filter(~Q(id=default.id)).all()
|
e.remote_id for e in \
|
||||||
|
self.edition_set.filter(~Q(id=default.id)).all()
|
||||||
]
|
]
|
||||||
return [default.remote_id] + ed_list
|
return [default.remote_id] + ed_list
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@ import re
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from bookwyrm import activitypub
|
from bookwyrm import activitypub
|
||||||
from .base_model import BookWyrmModel, OrderedCollectionMixin, PrivacyLevels
|
from .base_model import ActivityMapping, BookWyrmModel
|
||||||
|
from .base_model import OrderedCollectionMixin, PrivacyLevels
|
||||||
|
|
||||||
|
|
||||||
class Shelf(OrderedCollectionMixin, BookWyrmModel):
|
class Shelf(OrderedCollectionMixin, BookWyrmModel):
|
||||||
|
@ -47,6 +48,12 @@ class Shelf(OrderedCollectionMixin, BookWyrmModel):
|
||||||
''' user/shelf unqiueness '''
|
''' user/shelf unqiueness '''
|
||||||
unique_together = ('user', 'identifier')
|
unique_together = ('user', 'identifier')
|
||||||
|
|
||||||
|
activity_mappings = [
|
||||||
|
ActivityMapping('id', 'remote_id'),
|
||||||
|
ActivityMapping('owner', 'user'),
|
||||||
|
ActivityMapping('name', 'name'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class ShelfBook(BookWyrmModel):
|
class ShelfBook(BookWyrmModel):
|
||||||
''' many to many join table for books and shelves '''
|
''' many to many join table for books and shelves '''
|
||||||
|
@ -59,6 +66,15 @@ class ShelfBook(BookWyrmModel):
|
||||||
on_delete=models.PROTECT
|
on_delete=models.PROTECT
|
||||||
)
|
)
|
||||||
|
|
||||||
|
activity_mappings = [
|
||||||
|
ActivityMapping('id', 'remote_id'),
|
||||||
|
ActivityMapping('actor', 'added_by'),
|
||||||
|
ActivityMapping('object', 'book'),
|
||||||
|
ActivityMapping('target', 'shelf')
|
||||||
|
]
|
||||||
|
|
||||||
|
activity_serializer = activitypub.AddBook
|
||||||
|
|
||||||
def to_add_activity(self, user):
|
def to_add_activity(self, user):
|
||||||
''' AP for shelving a book'''
|
''' AP for shelving a book'''
|
||||||
return activitypub.Add(
|
return activitypub.Add(
|
||||||
|
|
|
@ -80,12 +80,10 @@ 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'),
|
||||||
lambda x: [i for i in x if x.get('type') == 'Book']
|
|
||||||
),
|
),
|
||||||
ActivityMapping(
|
ActivityMapping(
|
||||||
'tag', 'mention_users',
|
'tag', 'mention_users',
|
||||||
lambda x: tag_formatter(x, 'username', 'Mention'),
|
lambda x: tag_formatter(x, 'username', 'Mention'),
|
||||||
lambda x: [i for i in x if x.get('type') == 'Mention']
|
|
||||||
),
|
),
|
||||||
ActivityMapping(
|
ActivityMapping(
|
||||||
'attachment', 'attachments',
|
'attachment', 'attachments',
|
||||||
|
|
|
@ -11,7 +11,6 @@ from bookwyrm.models.status import Status
|
||||||
from bookwyrm.settings import DOMAIN
|
from bookwyrm.settings import DOMAIN
|
||||||
from bookwyrm.signatures import create_key_pair
|
from bookwyrm.signatures import create_key_pair
|
||||||
from .base_model import ActivityMapping, OrderedCollectionPageMixin
|
from .base_model import ActivityMapping, OrderedCollectionPageMixin
|
||||||
from .base_model import image_formatter
|
|
||||||
|
|
||||||
|
|
||||||
class User(OrderedCollectionPageMixin, AbstractUser):
|
class User(OrderedCollectionPageMixin, AbstractUser):
|
||||||
|
|
|
@ -16,11 +16,9 @@ def get_or_create_remote_user(actor):
|
||||||
except models.User.DoesNotExist:
|
except models.User.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
data = fetch_user_data(actor)
|
|
||||||
|
|
||||||
actor_parts = urlparse(actor)
|
actor_parts = urlparse(actor)
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
user = activitypub.Person(**data).to_model(models.User)
|
user = activitypub.resolve_remote_id(models.User, actor)
|
||||||
user.federated_server = get_or_create_remote_server(actor_parts.netloc)
|
user.federated_server = get_or_create_remote_server(actor_parts.netloc)
|
||||||
user.save()
|
user.save()
|
||||||
if user.bookwyrm_user:
|
if user.bookwyrm_user:
|
||||||
|
@ -28,32 +26,9 @@ def get_or_create_remote_user(actor):
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
def fetch_user_data(actor):
|
|
||||||
''' load the user's info from the actor url '''
|
|
||||||
try:
|
|
||||||
response = requests.get(
|
|
||||||
actor,
|
|
||||||
headers={'Accept': 'application/activity+json'}
|
|
||||||
)
|
|
||||||
except ConnectionError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if not response.ok:
|
|
||||||
response.raise_for_status()
|
|
||||||
data = response.json()
|
|
||||||
|
|
||||||
# make sure our actor is who they say they are
|
|
||||||
if actor != data['id']:
|
|
||||||
raise ValueError("Remote actor id must match url.")
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def refresh_remote_user(user):
|
def refresh_remote_user(user):
|
||||||
''' get updated user data from its home instance '''
|
''' get updated user data from its home instance '''
|
||||||
data = fetch_user_data(user.remote_id)
|
activitypub.resolve_remote_id(user.remote_id, refresh=True)
|
||||||
|
|
||||||
activity = activitypub.Person(**data)
|
|
||||||
activity.to_model(models.User, instance=user)
|
|
||||||
|
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
|
|
|
@ -5,8 +5,7 @@ from django.contrib.auth.decorators import login_required, permission_required
|
||||||
from django.contrib.postgres.search import TrigramSimilarity
|
from django.contrib.postgres.search import TrigramSimilarity
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
from django.db.models import Avg, Q
|
from django.db.models import Avg, Q
|
||||||
from django.http import HttpResponseBadRequest, HttpResponseNotFound,\
|
from django.http import HttpResponseNotFound, JsonResponse
|
||||||
JsonResponse
|
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
|
|
Loading…
Reference in a new issue