Merge branch 'main' into refactor-readthroughs

This commit is contained in:
Mouse Reeve
2021-09-22 15:08:58 -07:00
20 changed files with 173 additions and 164 deletions

View File

@ -14,7 +14,6 @@ from .status import Review, ReviewRating
from .status import Boost
from .attachment import Image
from .favorite import Favorite
from .notification import Notification
from .readthrough import ReadThrough, ProgressUpdate, ProgressMode
from .user import User, KeyPair, AnnualGoal
@ -29,6 +28,8 @@ from .site import PasswordReset, InviteRequest
from .announcement import Announcement
from .antispam import EmailBlocklist, IPBlocklist
from .notification import Notification
cls_members = inspect.getmembers(sys.modules[__name__], inspect.isclass)
activity_models = {
c[1].activity_serializer.__name__: c[1]

View File

@ -1,7 +1,5 @@
""" like/fav/star a status """
from django.apps import apps
from django.db import models
from django.utils import timezone
from bookwyrm import activitypub
from .activitypub_mixin import ActivityMixin
@ -29,38 +27,9 @@ class Favorite(ActivityMixin, BookWyrmModel):
def save(self, *args, **kwargs):
"""update user active time"""
self.user.last_active_date = timezone.now()
self.user.save(broadcast=False, update_fields=["last_active_date"])
self.user.update_active_date()
super().save(*args, **kwargs)
if self.status.user.local and self.status.user != self.user:
notification_model = apps.get_model(
"bookwyrm.Notification", require_ready=True
)
notification_model.objects.create(
user=self.status.user,
notification_type="FAVORITE",
related_user=self.user,
related_status=self.status,
)
def delete(self, *args, **kwargs):
"""delete and delete notifications"""
# check for notification
if self.status.user.local:
notification_model = apps.get_model(
"bookwyrm.Notification", require_ready=True
)
notification = notification_model.objects.filter(
user=self.status.user,
related_user=self.user,
related_status=self.status,
notification_type="FAVORITE",
).first()
if notification:
notification.delete()
super().delete(*args, **kwargs)
class Meta:
"""can't fav things twice"""

View File

@ -2,7 +2,6 @@
import re
import dateutil.parser
from django.apps import apps
from django.db import models
from django.utils import timezone
@ -50,19 +49,6 @@ class ImportJob(models.Model):
)
retry = models.BooleanField(default=False)
def save(self, *args, **kwargs):
"""save and notify"""
super().save(*args, **kwargs)
if self.complete:
notification_model = apps.get_model(
"bookwyrm.Notification", require_ready=True
)
notification_model.objects.create(
user=self.user,
notification_type="IMPORT",
related_import=self,
)
class ImportItem(models.Model):
"""a single line of a csv being imported"""

View File

@ -1,6 +1,8 @@
""" alert a user to activity """
from django.db import models
from django.dispatch import receiver
from .base_model import BookWyrmModel
from . import Boost, Favorite, ImportJob, Report, Status, User
NotificationType = models.TextChoices(
@ -53,3 +55,127 @@ class Notification(BookWyrmModel):
name="notification_type_valid",
)
]
@receiver(models.signals.post_save, sender=Favorite)
# pylint: disable=unused-argument
def notify_on_fav(sender, instance, *args, **kwargs):
"""someone liked your content, you ARE loved"""
if not instance.status.user.local or instance.status.user == instance.user:
return
Notification.objects.create(
user=instance.status.user,
notification_type="FAVORITE",
related_user=instance.user,
related_status=instance.status,
)
@receiver(models.signals.post_delete, sender=Favorite)
# pylint: disable=unused-argument
def notify_on_unfav(sender, instance, *args, **kwargs):
"""oops, didn't like that after all"""
if not instance.status.user.local:
return
Notification.objects.filter(
user=instance.status.user,
related_user=instance.user,
related_status=instance.status,
notification_type="FAVORITE",
).delete()
@receiver(models.signals.post_save)
# pylint: disable=unused-argument
def notify_user_on_mention(sender, instance, *args, **kwargs):
"""creating and deleting statuses with @ mentions and replies"""
if not issubclass(sender, Status):
return
if instance.deleted:
Notification.objects.filter(related_status=instance).delete()
return
if (
instance.reply_parent
and instance.reply_parent.user != instance.user
and instance.reply_parent.user.local
):
Notification.objects.create(
user=instance.reply_parent.user,
notification_type="REPLY",
related_user=instance.user,
related_status=instance,
)
for mention_user in instance.mention_users.all():
# avoid double-notifying about this status
if not mention_user.local or (
instance.reply_parent and mention_user == instance.reply_parent.user
):
continue
Notification.objects.create(
user=mention_user,
notification_type="MENTION",
related_user=instance.user,
related_status=instance,
)
@receiver(models.signals.post_save, sender=Boost)
# pylint: disable=unused-argument
def notify_user_on_boost(sender, instance, *args, **kwargs):
"""boosting a status"""
if (
not instance.boosted_status.user.local
or instance.boosted_status.user == instance.user
):
return
Notification.objects.create(
user=instance.boosted_status.user,
related_status=instance.boosted_status,
related_user=instance.user,
notification_type="BOOST",
)
@receiver(models.signals.post_delete, sender=Boost)
# pylint: disable=unused-argument
def notify_user_on_unboost(sender, instance, *args, **kwargs):
"""unboosting a status"""
Notification.objects.filter(
user=instance.boosted_status.user,
related_status=instance.boosted_status,
related_user=instance.user,
notification_type="BOOST",
).delete()
@receiver(models.signals.post_save, sender=ImportJob)
# pylint: disable=unused-argument
def notify_user_on_import_complete(sender, instance, *args, **kwargs):
"""we imported your books! aren't you proud of us"""
if not instance.complete:
return
Notification.objects.create(
user=instance.user,
notification_type="IMPORT",
related_import=instance,
)
@receiver(models.signals.post_save, sender=Report)
# pylint: disable=unused-argument
def notify_admins_on_report(sender, instance, *args, **kwargs):
"""something is up, make sure the admins know"""
# moderators and superusers should be notified
admins = User.objects.filter(
models.Q(user_permissions__name__in=["moderate_user", "moderate_post"])
| models.Q(is_superuser=True)
).all()
for admin in admins:
Notification.objects.create(
user=admin,
related_report=instance,
notification_type="REPORT",
)

View File

@ -2,7 +2,6 @@
from django.core import validators
from django.db import models
from django.db.models import F, Q
from django.utils import timezone
from .base_model import BookWyrmModel
@ -31,8 +30,7 @@ class ReadThrough(BookWyrmModel):
def save(self, *args, **kwargs):
"""update user active time"""
self.user.last_active_date = timezone.now()
self.user.save(broadcast=False, update_fields=["last_active_date"])
self.user.update_active_date()
super().save(*args, **kwargs)
def create_update(self):
@ -66,6 +64,5 @@ class ProgressUpdate(BookWyrmModel):
def save(self, *args, **kwargs):
"""update user active time"""
self.user.last_active_date = timezone.now()
self.user.save(broadcast=False, update_fields=["last_active_date"])
self.user.update_active_date()
super().save(*args, **kwargs)

View File

@ -1,5 +1,4 @@
""" flagged for moderation """
from django.apps import apps
from django.db import models
from django.db.models import F, Q
from .base_model import BookWyrmModel
@ -16,23 +15,6 @@ class Report(BookWyrmModel):
statuses = models.ManyToManyField("Status", blank=True)
resolved = models.BooleanField(default=False)
def save(self, *args, **kwargs):
"""notify admins when a report is created"""
super().save(*args, **kwargs)
user_model = apps.get_model("bookwyrm.User", require_ready=True)
# moderators and superusers should be notified
admins = user_model.objects.filter(
Q(user_permissions__name__in=["moderate_user", "moderate_post"])
| Q(is_superuser=True)
).all()
notification_model = apps.get_model("bookwyrm.Notification", require_ready=True)
for admin in admins:
notification_model.objects.create(
user=admin,
related_report=self,
notification_type="REPORT",
)
class Meta:
"""don't let users report themselves"""

View File

@ -67,40 +67,6 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
ordering = ("-published_date",)
def save(self, *args, **kwargs):
"""save and notify"""
super().save(*args, **kwargs)
notification_model = apps.get_model("bookwyrm.Notification", require_ready=True)
if self.deleted:
notification_model.objects.filter(related_status=self).delete()
return
if (
self.reply_parent
and self.reply_parent.user != self.user
and self.reply_parent.user.local
):
notification_model.objects.create(
user=self.reply_parent.user,
notification_type="REPLY",
related_user=self.user,
related_status=self,
)
for mention_user in self.mention_users.all():
# avoid double-notifying about this status
if not mention_user.local or (
self.reply_parent and mention_user == self.reply_parent.user
):
continue
notification_model.objects.create(
user=mention_user,
notification_type="MENTION",
related_user=self.user,
related_status=self,
)
def delete(self, *args, **kwargs): # pylint: disable=unused-argument
""" "delete" a status"""
if hasattr(self, "boosted_status"):
@ -108,6 +74,10 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
super().delete(*args, **kwargs)
return
self.deleted = True
# clear user content
self.content = None
if hasattr(self, "quotation"):
self.quotation = None # pylint: disable=attribute-defined-outside-init
self.deleted_date = timezone.now()
self.save()
@ -386,27 +356,6 @@ class Boost(ActivityMixin, Status):
return
super().save(*args, **kwargs)
if not self.boosted_status.user.local or self.boosted_status.user == self.user:
return
notification_model = apps.get_model("bookwyrm.Notification", require_ready=True)
notification_model.objects.create(
user=self.boosted_status.user,
related_status=self.boosted_status,
related_user=self.user,
notification_type="BOOST",
)
def delete(self, *args, **kwargs):
"""delete and un-notify"""
notification_model = apps.get_model("bookwyrm.Notification", require_ready=True)
notification_model.objects.filter(
user=self.boosted_status.user,
related_status=self.boosted_status,
related_user=self.user,
notification_type="BOOST",
).delete()
super().delete(*args, **kwargs)
def __init__(self, *args, **kwargs):
"""the user field is "actor" here instead of "attributedTo" """
@ -419,10 +368,6 @@ class Boost(ActivityMixin, Status):
self.image_fields = []
self.deserialize_reverse_fields = []
# This constraint can't work as it would cross tables.
# class Meta:
# unique_together = ('user', 'boosted_status')
# pylint: disable=unused-argument
@receiver(models.signals.post_save)

View File

@ -195,6 +195,11 @@ class User(OrderedCollectionPageMixin, AbstractUser):
queryset = queryset.exclude(blocks=viewer)
return queryset
def update_active_date(self):
"""this user is here! they are doing things!"""
self.last_active_date = timezone.now()
self.save(broadcast=False, update_fields=["last_active_date"])
def to_outbox(self, filter_type=None, **kwargs):
"""an ordered collection of statuses"""
if filter_type: