Adds fav/boost class view

This commit is contained in:
Mouse Reeve 2021-01-13 08:10:50 -08:00
parent c8efa1ffc8
commit cf2b9937c6
8 changed files with 289 additions and 220 deletions

View file

@ -16,7 +16,6 @@ from bookwyrm.broadcast import broadcast
from bookwyrm.sanitize_html import InputHtmlParser from bookwyrm.sanitize_html import InputHtmlParser
from bookwyrm.status import create_notification from bookwyrm.status import create_notification
from bookwyrm.status import create_generated_note from bookwyrm.status import create_generated_note
from bookwyrm.status import delete_status
from bookwyrm.settings import DOMAIN from bookwyrm.settings import DOMAIN
from bookwyrm.utils import regex from bookwyrm.utils import regex
@ -211,99 +210,3 @@ def handle_imported_book(user, item, include_reviews, privacy):
# we don't need to send out pure activities because non-bookwyrm # we don't need to send out pure activities because non-bookwyrm
# instances don't need this data # instances don't need this data
broadcast(user, review.to_create_activity(user), privacy=privacy) broadcast(user, review.to_create_activity(user), privacy=privacy)
def handle_favorite(user, status):
''' a user likes a status '''
try:
favorite = models.Favorite.objects.create(
status=status,
user=user
)
except IntegrityError:
# you already fav'ed that
return
fav_activity = favorite.to_activity()
broadcast(
user, fav_activity, privacy='direct', direct_recipients=[status.user])
if status.user.local:
create_notification(
status.user,
'FAVORITE',
related_user=user,
related_status=status
)
def handle_unfavorite(user, status):
''' a user likes a status '''
try:
favorite = models.Favorite.objects.get(
status=status,
user=user
)
except models.Favorite.DoesNotExist:
# can't find that status, idk
return
fav_activity = favorite.to_undo_activity(user)
favorite.delete()
broadcast(user, fav_activity, direct_recipients=[status.user])
# check for notification
if status.user.local:
notification = models.Notification.objects.filter(
user=status.user, related_user=user,
related_status=status, notification_type='FAVORITE'
).first()
if notification:
notification.delete()
def handle_boost(user, status):
''' a user wishes to boost a status '''
# is it boostable?
if not status.boostable:
return
if models.Boost.objects.filter(
boosted_status=status, user=user).exists():
# you already boosted that.
return
boost = models.Boost.objects.create(
boosted_status=status,
privacy=status.privacy,
user=user,
)
boost_activity = boost.to_activity()
broadcast(user, boost_activity)
if status.user.local:
create_notification(
status.user,
'BOOST',
related_user=user,
related_status=status
)
def handle_unboost(user, status):
''' a user regrets boosting a status '''
boost = models.Boost.objects.filter(
boosted_status=status, user=user
).first()
activity = boost.to_undo_activity(user)
boost.delete()
broadcast(user, activity)
# delete related notification
if status.user.local:
notification = models.Notification.objects.filter(
user=status.user, related_user=user,
related_status=status, notification_type='BOOST'
).first()
if notification:
notification.delete()

View file

@ -430,112 +430,3 @@ class Outgoing(TestCase):
self.assertFalse(models.Review.objects.filter( self.assertFalse(models.Review.objects.filter(
book=self.book, user=self.local_user book=self.book, user=self.local_user
).exists()) ).exists())
def test_handle_delete_status(self):
''' marks a status as deleted '''
status = models.Status.objects.create(
user=self.local_user, content='hi')
self.assertFalse(status.deleted)
with patch('bookwyrm.broadcast.broadcast_task.delay'):
outgoing.handle_delete_status(self.local_user, status)
status.refresh_from_db()
self.assertTrue(status.deleted)
def test_handle_favorite(self):
''' create and broadcast faving a status '''
status = models.Status.objects.create(
user=self.local_user, content='hi')
with patch('bookwyrm.broadcast.broadcast_task.delay'):
outgoing.handle_favorite(self.remote_user, status)
fav = models.Favorite.objects.get()
self.assertEqual(fav.status, status)
self.assertEqual(fav.user, self.remote_user)
notification = models.Notification.objects.get()
self.assertEqual(notification.notification_type, 'FAVORITE')
self.assertEqual(notification.user, self.local_user)
self.assertEqual(notification.related_user, self.remote_user)
def test_handle_unfavorite(self):
''' unfav a status '''
status = models.Status.objects.create(
user=self.local_user, content='hi')
with patch('bookwyrm.broadcast.broadcast_task.delay'):
outgoing.handle_favorite(self.remote_user, status)
self.assertEqual(models.Favorite.objects.count(), 1)
self.assertEqual(models.Notification.objects.count(), 1)
with patch('bookwyrm.broadcast.broadcast_task.delay'):
outgoing.handle_unfavorite(self.remote_user, status)
self.assertEqual(models.Favorite.objects.count(), 0)
self.assertEqual(models.Notification.objects.count(), 0)
def test_handle_boost(self):
''' boost a status '''
status = models.Status.objects.create(
user=self.local_user, content='hi')
with patch('bookwyrm.broadcast.broadcast_task.delay'):
outgoing.handle_boost(self.remote_user, status)
boost = models.Boost.objects.get()
self.assertEqual(boost.boosted_status, status)
self.assertEqual(boost.user, self.remote_user)
self.assertEqual(boost.privacy, 'public')
notification = models.Notification.objects.get()
self.assertEqual(notification.notification_type, 'BOOST')
self.assertEqual(notification.user, self.local_user)
self.assertEqual(notification.related_user, self.remote_user)
self.assertEqual(notification.related_status, status)
def test_handle_boost_unlisted(self):
''' boost a status '''
status = models.Status.objects.create(
user=self.local_user, content='hi', privacy='unlisted')
with patch('bookwyrm.broadcast.broadcast_task.delay'):
outgoing.handle_boost(self.remote_user, status)
boost = models.Boost.objects.get()
self.assertEqual(boost.privacy, 'unlisted')
def test_handle_boost_private(self):
''' boost a status '''
status = models.Status.objects.create(
user=self.local_user, content='hi', privacy='followers')
with patch('bookwyrm.broadcast.broadcast_task.delay'):
outgoing.handle_boost(self.remote_user, status)
self.assertFalse(models.Boost.objects.exists())
def test_handle_boost_twice(self):
''' boost a status '''
status = models.Status.objects.create(
user=self.local_user, content='hi')
with patch('bookwyrm.broadcast.broadcast_task.delay'):
outgoing.handle_boost(self.remote_user, status)
outgoing.handle_boost(self.remote_user, status)
self.assertEqual(models.Boost.objects.count(), 1)
def test_handle_unboost(self):
''' undo a boost '''
status = models.Status.objects.create(
user=self.local_user, content='hi')
with patch('bookwyrm.broadcast.broadcast_task.delay'):
outgoing.handle_boost(self.remote_user, status)
self.assertEqual(models.Boost.objects.count(), 1)
self.assertEqual(models.Notification.objects.count(), 1)
with patch('bookwyrm.broadcast.broadcast_task.delay'):
outgoing.handle_unboost(self.remote_user, status)
self.assertEqual(models.Boost.objects.count(), 0)
self.assertEqual(models.Notification.objects.count(), 0)

View file

@ -0,0 +1,152 @@
''' test for app action functionality '''
from unittest.mock import patch
from django.test import TestCase
from django.test.client import RequestFactory
from bookwyrm import models, views
class InteractionViews(TestCase):
''' viewing and creating statuses '''
def setUp(self):
''' we need basic test data and mocks '''
self.factory = RequestFactory()
self.local_user = models.User.objects.create_user(
'mouse@local.com', 'mouse@mouse.com', 'mouseword',
local=True, localname='mouse',
remote_id='https://example.com/users/mouse',
)
with patch('bookwyrm.models.user.set_remote_server'):
self.remote_user = models.User.objects.create_user(
'rat', 'rat@email.com', 'ratword',
local=False,
remote_id='https://example.com/users/rat',
inbox='https://example.com/users/rat/inbox',
outbox='https://example.com/users/rat/outbox',
)
work = models.Work.objects.create(title='Test Work')
self.book = models.Edition.objects.create(
title='Example Edition',
remote_id='https://example.com/book/1',
parent_work=work
)
def test_handle_favorite(self):
''' create and broadcast faving a status '''
view = views.Favorite.as_view()
request = self.factory.post('')
request.user = self.local_user
status = models.Status.objects.create(
user=self.local_user, content='hi')
with patch('bookwyrm.broadcast.broadcast_task.delay'):
view(request, status)
fav = models.Favorite.objects.get()
self.assertEqual(fav.status, status)
self.assertEqual(fav.user, self.remote_user)
notification = models.Notification.objects.get()
self.assertEqual(notification.notification_type, 'FAVORITE')
self.assertEqual(notification.user, self.local_user)
self.assertEqual(notification.related_user, self.remote_user)
def test_handle_unfavorite(self):
''' unfav a status '''
view = views.Unfavorite.as_view()
request = self.factory.post('')
request.user = self.local_user
status = models.Status.objects.create(
user=self.local_user, content='hi')
with patch('bookwyrm.broadcast.broadcast_task.delay'):
view(request, status)
self.assertEqual(models.Favorite.objects.count(), 1)
self.assertEqual(models.Notification.objects.count(), 1)
with patch('bookwyrm.broadcast.broadcast_task.delay'):
view(request, status)
self.assertEqual(models.Favorite.objects.count(), 0)
self.assertEqual(models.Notification.objects.count(), 0)
def test_handle_boost(self):
''' boost a status '''
view = views.Boost.as_view()
request = self.factory.post('')
request.user = self.local_user
status = models.Status.objects.create(
user=self.local_user, content='hi')
with patch('bookwyrm.broadcast.broadcast_task.delay'):
view(request, status)
boost = models.Boost.objects.get()
self.assertEqual(boost.boosted_status, status)
self.assertEqual(boost.user, self.remote_user)
self.assertEqual(boost.privacy, 'public')
notification = models.Notification.objects.get()
self.assertEqual(notification.notification_type, 'BOOST')
self.assertEqual(notification.user, self.local_user)
self.assertEqual(notification.related_user, self.remote_user)
self.assertEqual(notification.related_status, status)
def test_handle_boost_unlisted(self):
''' boost a status '''
view = views.Boost.as_view()
request = self.factory.post('')
request.user = self.local_user
status = models.Status.objects.create(
user=self.local_user, content='hi', privacy='unlisted')
with patch('bookwyrm.broadcast.broadcast_task.delay'):
view(request, status)
boost = models.Boost.objects.get()
self.assertEqual(boost.privacy, 'unlisted')
def test_handle_boost_private(self):
''' boost a status '''
view = views.Boost.as_view()
request = self.factory.post('')
request.user = self.local_user
status = models.Status.objects.create(
user=self.local_user, content='hi', privacy='followers')
with patch('bookwyrm.broadcast.broadcast_task.delay'):
view(request, status)
self.assertFalse(models.Boost.objects.exists())
def test_handle_boost_twice(self):
''' boost a status '''
view = views.Boost.as_view()
request = self.factory.post('')
request.user = self.local_user
status = models.Status.objects.create(
user=self.local_user, content='hi')
with patch('bookwyrm.broadcast.broadcast_task.delay'):
view(request, status)
view(request, status)
self.assertEqual(models.Boost.objects.count(), 1)
def test_handle_unboost(self):
''' undo a boost '''
view = views.Unboost.as_view()
request = self.factory.post('')
request.user = self.local_user
status = models.Status.objects.create(
user=self.local_user, content='hi')
with patch('bookwyrm.broadcast.broadcast_task.delay'):
view(request, status)
self.assertEqual(models.Boost.objects.count(), 1)
self.assertEqual(models.Notification.objects.count(), 1)
with patch('bookwyrm.broadcast.broadcast_task.delay'):
view(request, status)
self.assertEqual(models.Boost.objects.count(), 0)
self.assertEqual(models.Notification.objects.count(), 0)

View file

@ -82,10 +82,10 @@ urlpatterns = [
views.DeleteStatus.as_view()), views.DeleteStatus.as_view()),
# interact # interact
re_path(r'^favorite/(?P<status_id>\d+)/?$', actions.favorite), re_path(r'^favorite/(?P<status_id>\d+)/?$', views.Favorite.as_view()),
re_path(r'^unfavorite/(?P<status_id>\d+)/?$', actions.unfavorite), re_path(r'^unfavorite/(?P<status_id>\d+)/?$', views.Unfavorite.as_view()),
re_path(r'^boost/(?P<status_id>\d+)/?$', actions.boost), re_path(r'^boost/(?P<status_id>\d+)/?$', views.Boost.as_view()),
re_path(r'^unboost/(?P<status_id>\d+)/?$', actions.unboost), re_path(r'^unboost/(?P<status_id>\d+)/?$', views.Unboost.as_view()),
# books # books
re_path(r'%s(.json)?/?$' % book_path, vviews.book_page), re_path(r'%s(.json)?/?$' % book_path, vviews.book_page),

View file

@ -378,15 +378,6 @@ def untag(request):
return redirect('/book/%s' % book_id) return redirect('/book/%s' % book_id)
@login_required
@require_POST
def favorite(request, status_id):
''' like a status '''
status = models.Status.objects.get(id=status_id)
outgoing.handle_favorite(request.user, status)
return redirect(request.headers.get('Referer', '/'))
@login_required @login_required
@require_POST @require_POST
def unfavorite(request, status_id): def unfavorite(request, status_id):

View file

@ -8,3 +8,4 @@ from .direct_message import DirectMessage
from .import_data import Import, ImportStatus from .import_data import Import, ImportStatus
from .user import User, EditUser, Followers, Following from .user import User, EditUser, Followers, Following
from .status import Status, Replies, CreateStatus, DeleteStatus from .status import Status, Replies, CreateStatus, DeleteStatus
from .interaction import Favorite, Unfavorite, Boost, Unboost

View file

@ -0,0 +1,131 @@
''' boosts and favs '''
from django.db import IntegrityError
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseBadRequest, HttpResponseNotFound
from django.shortcuts import redirect
from django.utils.decorators import method_decorator
from django.views import View
from bookwyrm import models
from bookwyrm.broadcast import broadcast
from bookwyrm.status import create_notification
# pylint: disable= no-self-use
@method_decorator(login_required, name='dispatch')
class Favorite(View):
''' like a status '''
def post(self, request, status_id):
''' create a like '''
status = models.Status.objects.get(id=status_id)
try:
favorite = models.Favorite.objects.create(
status=status,
user=request.user
)
except IntegrityError:
# you already fav'ed that
return HttpResponseBadRequest()
fav_activity = favorite.to_activity()
broadcast(
request.user, fav_activity, privacy='direct',
direct_recipients=[status.user])
if status.user.local:
create_notification(
status.user,
'FAVORITE',
related_user=request.user,
related_status=status
)
return redirect(request.headers.get('Referer', '/'))
@method_decorator(login_required, name='dispatch')
class Unfavorite(View):
''' take back a fav '''
def post(self, request, status_id):
''' unlike a status '''
status = models.Status.objects.get(id=status_id)
try:
favorite = models.Favorite.objects.get(
status=status,
user=request.user
)
except models.Favorite.DoesNotExist:
# can't find that status, idk
return HttpResponseNotFound()
fav_activity = favorite.to_undo_activity(request.user)
favorite.delete()
broadcast(request.user, fav_activity, direct_recipients=[status.user])
# check for notification
if status.user.local:
notification = models.Notification.objects.filter(
user=status.user, related_user=request.user,
related_status=status, notification_type='FAVORITE'
).first()
if notification:
notification.delete()
return redirect(request.headers.get('Referer', '/'))
@method_decorator(login_required, name='dispatch')
class Boost(View):
''' boost a status '''
def post(self, request, status_id):
''' boost a status '''
status = models.Status.objects.get(id=status_id)
# is it boostable?
if not status.boostable:
return HttpResponseBadRequest()
if models.Boost.objects.filter(
boosted_status=status, user=request.user).exists():
# you already boosted that.
return redirect(request.headers.get('Referer', '/'))
boost = models.Boost.objects.create(
boosted_status=status,
privacy=status.privacy,
user=request.user,
)
boost_activity = boost.to_activity()
broadcast(request.user, boost_activity)
if status.user.local:
create_notification(
status.user,
'BOOST',
related_user=request.user,
related_status=status
)
return redirect(request.headers.get('Referer', '/'))
@method_decorator(login_required, name='dispatch')
class Unboost(View):
''' boost a status '''
def post(self, request, status_id):
''' boost a status '''
status = models.Status.objects.get(id=status_id)
boost = models.Boost.objects.filter(
boosted_status=status, user=request.user
).first()
activity = boost.to_undo_activity(request.user)
boost.delete()
broadcast(request.user, activity)
# delete related notification
if status.user.local:
notification = models.Notification.objects.filter(
user=status.user, related_user=request.user,
related_status=status, notification_type='BOOST'
).first()
if notification:
notification.delete()
return redirect(request.headers.get('Referer', '/'))

View file

@ -1,4 +1,4 @@
''' non-interactive pages ''' ''' what are we here for if not for posting '''
import re import re
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.http import HttpResponseBadRequest, HttpResponseNotFound from django.http import HttpResponseBadRequest, HttpResponseNotFound