Files
guilan-ace-backend/apps/gamification/signals.py

129 lines
4.5 KiB
Python

from django.db.models.signals import m2m_changed, post_delete, post_save, pre_save
from django.dispatch import receiver
from apps.blog.models import Comment, Like, Post, SavedPost
from apps.events.models import Registration
from apps.gamification.services import (
desired_effect,
has_source_history,
invalidate_post_engagement,
sync_comment,
sync_effect,
sync_like,
sync_post_engagement_for_credited_authors,
sync_post_publication,
sync_registration_attendance,
sync_save,
sync_source_effects,
)
from apps.users.models import User
def _post_is_public(post):
return Post.StatusChoices.PUBLISHED == post.status and not post.is_deleted
def _credited_author_ids(post):
return {post.author_id, *post.writers.values_list("id", flat=True)}
@receiver(pre_save, sender=Post)
def remember_post_visibility(sender, instance, **kwargs):
if not instance.pk:
instance._gamification_was_public = False
return
old = Post.all_objects.filter(pk=instance.pk).values("status", "is_deleted").first()
instance._gamification_was_public = bool(
old
and old["status"] == Post.StatusChoices.PUBLISHED
and not old["is_deleted"]
)
@receiver(post_save, sender=Post)
def score_post_visibility(sender, instance, created, **kwargs):
was_public = getattr(instance, "_gamification_was_public", False)
is_public = _post_is_public(instance)
if is_public and not was_public:
sync_post_publication(instance, valid=True)
sync_post_engagement_for_credited_authors(instance)
elif was_public and not is_public:
sync_post_publication(instance, valid=False)
invalidate_post_engagement(instance)
@receiver(post_delete, sender=Post)
def reverse_deleted_post(sender, instance, **kwargs):
sync_source_effects("blog_post", instance.pk, [], reason="post was deleted")
@receiver(m2m_changed, sender=Post.writers.through)
def score_credited_writer_changes(sender, instance, action, reverse, pk_set, **kwargs):
if reverse or not _post_is_public(instance):
return
writer_ids = set(pk_set or getattr(instance, "_gamification_writer_ids_before_clear", set()))
if action in {"pre_clear"}:
instance._gamification_writer_ids_before_clear = set(instance.writers.values_list("id", flat=True))
return
if action not in {"post_add", "post_remove", "post_clear"}:
return
for user in User.objects.filter(pk__in=writer_ids):
if action != "post_add" and user.pk == instance.author_id:
continue
effect = {
**desired_effect(user, "post", "author"),
"source_type": "blog_post",
"source_id": str(instance.pk),
}
sync_effect(effect, valid=action == "post_add", reason="post coauthor credit was removed")
sync_post_engagement_for_credited_authors(instance)
instance._gamification_writer_ids_before_clear = set()
@receiver(pre_save, sender=Comment)
def remember_comment_approval(sender, instance, **kwargs):
if not instance.pk:
instance._gamification_was_approved = False
return
old = Comment.all_objects.filter(pk=instance.pk).values("is_approved", "is_hidden", "is_deleted").first()
instance._gamification_was_approved = bool(
old and old["is_approved"] and not old["is_hidden"] and not old["is_deleted"]
)
@receiver(post_save, sender=Comment)
def score_approved_comment(sender, instance, created, **kwargs):
became_approved = not getattr(instance, "_gamification_was_approved", False) and instance.is_approved
sync_comment(instance, allow_new=created or became_approved)
@receiver(post_delete, sender=Comment)
def reverse_deleted_comment(sender, instance, **kwargs):
sync_source_effects("blog_comment", instance.pk, [], reason="comment was deleted")
@receiver(post_save, sender=Like)
def score_like(sender, instance, created, **kwargs):
sync_like(instance, allow_new=created)
@receiver(post_delete, sender=Like)
def reverse_like(sender, instance, **kwargs):
sync_source_effects("blog_like", instance.pk, [], reason="like was removed")
@receiver(post_save, sender=SavedPost)
def score_saved_post(sender, instance, created, **kwargs):
sync_save(instance, allow_new=created)
@receiver(post_delete, sender=SavedPost)
def reverse_saved_post(sender, instance, **kwargs):
sync_source_effects("blog_save", instance.pk, [], reason="saved post was removed")
@receiver(post_save, sender=Registration)
def score_registration_attendance(sender, instance, **kwargs):
sync_registration_attendance(instance)