From a47f573b2be5f60ee1b7b10331148e2224a4cd6a Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Tue, 10 Mar 2020 12:04:38 -0700 Subject: [PATCH] Fixes storage of remote statuses --- fedireads/incoming.py | 14 ++--- fedireads/migrations/0014_status_remote_id.py | 18 ++++++ fedireads/models/status.py | 11 ++++ fedireads/outgoing.py | 2 +- fedireads/status.py | 63 +++++++++++++++++-- fedireads/urls.py | 2 +- 6 files changed, 92 insertions(+), 18 deletions(-) create mode 100644 fedireads/migrations/0014_status_remote_id.py diff --git a/fedireads/incoming.py b/fedireads/incoming.py index baaedd056..2daa8fe8a 100644 --- a/fedireads/incoming.py +++ b/fedireads/incoming.py @@ -13,8 +13,8 @@ import requests from fedireads import activitypub from fedireads import models from fedireads import outgoing -from fedireads.status import create_review, create_status, create_tag, \ - create_notification +from fedireads.status import create_review_from_activity, \ + create_status_from_activity, create_tag, create_notification from fedireads.remote_user import get_or_create_remote_user @@ -256,25 +256,19 @@ def handle_incoming_create(activity): # TODO: should only create notes if they are relevent to a book, # so, not every single thing someone posts on mastodon response = HttpResponse() - content = activity['object'].get('content') if activity['object'].get('fedireadsType') == 'Review' and \ 'inReplyToBook' in activity['object']: - book = activity['object']['inReplyToBook'] - book = book.split('/')[-1] - name = activity['object'].get('name') - rating = activity['object'].get('rating') - published = activity['object'].get('published') if user.local: review_id = activity['object']['id'].split('/')[-1] models.Review.objects.get(id=review_id) else: try: - create_review(user, book, name, content, rating, published) + create_review_from_activity(user, activity['object']) except ValueError: return HttpResponseBadRequest() elif not user.local: try: - status = create_status(user, content) + status = create_status_from_activity(user, activity['object']) if status.reply_parent: create_notification( status.reply_parent.user, diff --git a/fedireads/migrations/0014_status_remote_id.py b/fedireads/migrations/0014_status_remote_id.py new file mode 100644 index 000000000..3a9c4ed73 --- /dev/null +++ b/fedireads/migrations/0014_status_remote_id.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.3 on 2020-03-10 19:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fedireads', '0013_user_manually_approves_followers'), + ] + + operations = [ + migrations.AddField( + model_name='status', + name='remote_id', + field=models.CharField(max_length=255, null=True, unique=True), + ), + ] diff --git a/fedireads/models/status.py b/fedireads/models/status.py index 4077cef7a..34715fb9d 100644 --- a/fedireads/models/status.py +++ b/fedireads/models/status.py @@ -10,6 +10,7 @@ from fedireads.utils.models import FedireadsModel class Status(FedireadsModel): ''' any post, like a reply to a review, etc ''' + remote_id = models.CharField(max_length=255, unique=True, null=True) user = models.ForeignKey('User', on_delete=models.PROTECT) status_type = models.CharField(max_length=255, default='Note') content = models.TextField(blank=True, null=True) @@ -35,6 +36,16 @@ class Status(FedireadsModel): ) objects = InheritanceManager() + @property + def absolute_id(self): + ''' constructs the absolute reference to any db object ''' + if self.remote_id: + return self.remote_id + base_path = self.user.absolute_id + model_name = type(self).__name__.lower() + return '%s/%s/%d' % (base_path, model_name, self.id) + + class Review(Status): ''' a book review ''' diff --git a/fedireads/outgoing.py b/fedireads/outgoing.py index ac45f405e..7c31a219a 100644 --- a/fedireads/outgoing.py +++ b/fedireads/outgoing.py @@ -151,7 +151,7 @@ def handle_unshelve(user, book, shelf): def handle_review(user, book, name, content, rating): ''' post a review ''' # validated and saves the review in the database so it has an id - review = create_review(user, book, name, content, rating, None) + review = create_review(user, book, name, content, rating) review_activity = activitypub.get_review(review) review_create_activity = activitypub.get_create(user, review_activity) diff --git a/fedireads/status.py b/fedireads/status.py index 599eefa9a..8e2615783 100644 --- a/fedireads/status.py +++ b/fedireads/status.py @@ -5,7 +5,24 @@ from fedireads.sanitize_html import InputHtmlParser from django.db import IntegrityError -def create_review(user, possible_book, name, content, rating, published): +def create_review_from_activity(author, activity): + ''' parse an activity json blob into a status ''' + book = activity['inReplyToBook'] + book = book.split('/')[-1] + name = activity.get('name') + rating = activity.get('rating') + content = activity.get('content') + published = activity.get('published') + remote_id = activity['id'] + + review = create_review(author, book, name, content, rating) + review.published_date = published + review.remote_id = remote_id + review.save() + return review + + +def create_review(user, possible_book, name, content, rating): ''' a book review has been added ''' # throws a value error if the book is not found book = get_or_create_book(possible_book) @@ -15,17 +32,51 @@ def create_review(user, possible_book, name, content, rating, published): # no ratings outside of 0-5 rating = rating if 0 <= rating <= 5 else 0 - review = models.Review( + return models.Review.objects.create( user=user, book=book, name=name, rating=rating, content=content, ) - if published: - review.published_date = published - review.save() - return review + + +def create_status_from_activity(author, activity): + ''' parse a status object out of an activity json blob ''' + content = activity.get('content') + reply_parent_id = activity.get('inReplyTo') + reply_parent = get_status(reply_parent_id) + + remote_id = activity['id'] + + status = create_status(author, content, reply_parent=reply_parent) + status.remote_id = remote_id + status.published_date = activity.get('published') + status.save() + return status + + +def get_status(absolute_id): + ''' find a status in the database ''' + # check if it's a remote status + try: + return models.Status.objects.get(remote_id=absolute_id) + except models.Status.DoesNotExist: + pass + + # try finding a local status with that id + local_id = absolute_id.split('/')[-1] + try: + possible_match = models.Status.objects.get(id=local_id) + except models.Status.DoesNotExist: + return None + + # make sure it's not actually a remote status with an id that + # clashes with a local id + if possible_match.absolute_id == absolute_id: + return possible_match + return None + def create_status(user, content, reply_parent=None, mention_books=None): diff --git a/fedireads/urls.py b/fedireads/urls.py index 1b1ae1dba..d962671b4 100644 --- a/fedireads/urls.py +++ b/fedireads/urls.py @@ -10,7 +10,7 @@ username_regex = r'(?P[\w@\.-]+)' localname_regex = r'(?P[\w\.-]+)' user_path = r'^user/%s' % username_regex local_user_path = r'^user/%s' % localname_regex -status_path = r'%s/(status|review)/(?P\d+)' % user_path +status_path = r'%s/(status|review)/(?P\d+)' % local_user_path urlpatterns = [ path('admin/', admin.site.urls),