signals.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. from collections import OrderedDict
  2. from django.contrib.auth import get_user_model
  3. from django.db import transaction
  4. from django.db.models.signals import pre_delete
  5. from django.dispatch import Signal, receiver
  6. from django.utils.translation import gettext as _
  7. from ..categories.models import Category
  8. from ..categories.signals import delete_category_content, move_category_content
  9. from ..core.pgutils import chunk_queryset
  10. from ..users.signals import (
  11. anonymize_user_data,
  12. archive_user_data,
  13. delete_user_content,
  14. username_changed,
  15. )
  16. from .anonymize import ANONYMIZABLE_EVENTS, anonymize_event, anonymize_post_last_likes
  17. from .models import Attachment, Poll, PollVote, Post, PostEdit, PostLike, Thread
  18. delete_post = Signal()
  19. delete_thread = Signal()
  20. merge_post = Signal()
  21. merge_thread = Signal()
  22. move_post = Signal()
  23. move_thread = Signal()
  24. @receiver(merge_thread)
  25. def merge_threads(sender, **kwargs):
  26. other_thread = kwargs["other_thread"]
  27. other_thread.post_set.update(category=sender.category, thread=sender)
  28. other_thread.postedit_set.update(category=sender.category, thread=sender)
  29. other_thread.postlike_set.update(category=sender.category, thread=sender)
  30. other_thread.subscription_set.exclude(
  31. user__in=sender.subscription_set.values("user")
  32. ).update(category=sender.category, thread=sender)
  33. @receiver(merge_post)
  34. def merge_posts(sender, **kwargs):
  35. other_post = kwargs["other_post"]
  36. for user in sender.mentions.iterator():
  37. other_post.mentions.add(user)
  38. @receiver(move_thread)
  39. def move_thread_content(sender, **kwargs):
  40. sender.post_set.update(category=sender.category)
  41. sender.postedit_set.update(category=sender.category)
  42. sender.postlike_set.update(category=sender.category)
  43. sender.pollvote_set.update(category=sender.category)
  44. sender.subscription_set.update(category=sender.category)
  45. Poll.objects.filter(thread=sender).update(category=sender.category)
  46. @receiver(delete_category_content)
  47. def delete_category_threads(sender, **kwargs):
  48. sender.subscription_set.all().delete()
  49. sender.pollvote_set.all().delete()
  50. sender.poll_set.all().delete()
  51. sender.postlike_set.all().delete()
  52. sender.thread_set.all().delete()
  53. sender.postedit_set.all().delete()
  54. sender.post_set.all().delete()
  55. @receiver(move_category_content)
  56. def move_category_threads(sender, **kwargs):
  57. new_category = kwargs["new_category"]
  58. sender.thread_set.update(category=new_category)
  59. sender.post_set.filter(category=sender).update(category=new_category)
  60. sender.postedit_set.filter(category=sender).update(category=new_category)
  61. sender.postlike_set.filter(category=sender).update(category=new_category)
  62. sender.poll_set.filter(category=sender).update(category=new_category)
  63. sender.pollvote_set.update(category=new_category)
  64. sender.subscription_set.update(category=new_category)
  65. @receiver(delete_user_content)
  66. def delete_user_threads(sender, **kwargs):
  67. recount_categories = set()
  68. recount_threads = set()
  69. for post in chunk_queryset(sender.liked_post_set):
  70. cleaned_likes = list(filter(lambda i: i["id"] != sender.id, post.last_likes))
  71. if cleaned_likes != post.last_likes:
  72. post.last_likes = cleaned_likes
  73. post.save(update_fields=["last_likes"])
  74. for thread in chunk_queryset(sender.thread_set):
  75. recount_categories.add(thread.category_id)
  76. with transaction.atomic():
  77. thread.delete()
  78. for post in chunk_queryset(sender.post_set):
  79. recount_categories.add(post.category_id)
  80. recount_threads.add(post.thread_id)
  81. with transaction.atomic():
  82. post.delete()
  83. if recount_threads:
  84. changed_threads_qs = Thread.objects.filter(id__in=recount_threads)
  85. for thread in chunk_queryset(changed_threads_qs):
  86. thread.synchronize()
  87. thread.save()
  88. if recount_categories:
  89. for category in Category.objects.filter(id__in=recount_categories):
  90. category.synchronize()
  91. category.save()
  92. @receiver(archive_user_data)
  93. def archive_user_attachments(sender, archive=None, **kwargs):
  94. queryset = sender.attachment_set.order_by("id")
  95. for attachment in chunk_queryset(queryset):
  96. archive.add_model_file(
  97. attachment.file,
  98. prefix=attachment.uploaded_on.strftime("%H%M%S-file"),
  99. date=attachment.uploaded_on,
  100. )
  101. archive.add_model_file(
  102. attachment.image,
  103. prefix=attachment.uploaded_on.strftime("%H%M%S-image"),
  104. date=attachment.uploaded_on,
  105. )
  106. archive.add_model_file(
  107. attachment.thumbnail,
  108. prefix=attachment.uploaded_on.strftime("%H%M%S-thumbnail"),
  109. date=attachment.uploaded_on,
  110. )
  111. @receiver(archive_user_data)
  112. def archive_user_posts(sender, archive=None, **kwargs):
  113. queryset = sender.post_set.order_by("id")
  114. for post in chunk_queryset(queryset):
  115. item_name = post.posted_on.strftime("%H%M%S-post")
  116. archive.add_text(item_name, post.parsed, date=post.posted_on)
  117. @receiver(archive_user_data)
  118. def archive_user_posts_edits(sender, archive=None, **kwargs):
  119. queryset = PostEdit.objects.filter(post__poster=sender).order_by("id")
  120. for post_edit in chunk_queryset(queryset):
  121. item_name = post_edit.edited_on.strftime("%H%M%S-post-edit")
  122. archive.add_text(item_name, post_edit.edited_from, date=post_edit.edited_on)
  123. queryset = sender.postedit_set.exclude(id__in=queryset.values("id")).order_by("id")
  124. for post_edit in chunk_queryset(queryset):
  125. item_name = post_edit.edited_on.strftime("%H%M%S-post-edit")
  126. archive.add_text(item_name, post_edit.edited_from, date=post_edit.edited_on)
  127. @receiver(archive_user_data)
  128. def archive_user_polls(sender, archive=None, **kwargs):
  129. queryset = sender.poll_set.order_by("id")
  130. for poll in chunk_queryset(queryset):
  131. item_name = poll.posted_on.strftime("%H%M%S-poll")
  132. archive.add_dict(
  133. item_name,
  134. OrderedDict(
  135. [
  136. (_("Question"), poll.question),
  137. (_("Choices"), ", ".join([c["label"] for c in poll.choices])),
  138. ]
  139. ),
  140. date=poll.posted_on,
  141. )
  142. @receiver(anonymize_user_data)
  143. def anonymize_user_in_events(sender, **kwargs):
  144. queryset = Post.objects.filter(
  145. is_event=True,
  146. event_type__in=ANONYMIZABLE_EVENTS,
  147. event_context__user__id=sender.id,
  148. )
  149. for event in chunk_queryset(queryset):
  150. anonymize_event(sender, event)
  151. @receiver([anonymize_user_data])
  152. def anonymize_user_in_likes(sender, **kwargs):
  153. for post in chunk_queryset(sender.liked_post_set):
  154. anonymize_post_last_likes(sender, post)
  155. @receiver([anonymize_user_data, username_changed])
  156. def update_usernames(sender, **kwargs):
  157. Thread.objects.filter(starter=sender).update(
  158. starter_name=sender.username, starter_slug=sender.slug
  159. )
  160. Thread.objects.filter(last_poster=sender).update(
  161. last_poster_name=sender.username, last_poster_slug=sender.slug
  162. )
  163. Thread.objects.filter(best_answer_marked_by=sender).update(
  164. best_answer_marked_by_name=sender.username,
  165. best_answer_marked_by_slug=sender.slug,
  166. )
  167. Post.objects.filter(poster=sender).update(poster_name=sender.username)
  168. Post.objects.filter(last_editor=sender).update(
  169. last_editor_name=sender.username, last_editor_slug=sender.slug
  170. )
  171. PostEdit.objects.filter(editor=sender).update(
  172. editor_name=sender.username, editor_slug=sender.slug
  173. )
  174. PostLike.objects.filter(liker=sender).update(
  175. liker_name=sender.username, liker_slug=sender.slug
  176. )
  177. Attachment.objects.filter(uploader=sender).update(
  178. uploader_name=sender.username, uploader_slug=sender.slug
  179. )
  180. Poll.objects.filter(poster=sender).update(
  181. poster_name=sender.username, poster_slug=sender.slug
  182. )
  183. PollVote.objects.filter(voter=sender).update(
  184. voter_name=sender.username, voter_slug=sender.slug
  185. )
  186. @receiver(pre_delete, sender=get_user_model())
  187. def remove_unparticipated_private_threads(sender, **kwargs):
  188. threads_qs = kwargs["instance"].privatethread_set.all()
  189. for thread in chunk_queryset(threads_qs):
  190. if thread.participants.count() == 1:
  191. with transaction.atomic():
  192. thread.delete()