threadstracker.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. from django.utils import timezone
  2. from misago.readtracker import forumstracker, signals
  3. from misago.readtracker.dates import is_date_tracked
  4. from misago.readtracker.models import ForumRead, ThreadRead
  5. __all__ = ['make_read_aware', 'read_thread']
  6. def make_read_aware(user, target):
  7. if hasattr(target, '__iter__'):
  8. make_threads_read_aware(user, target)
  9. else:
  10. make_thread_read_aware(user, target)
  11. def make_threads_read_aware(user, threads, forum=None):
  12. if not threads:
  13. return None
  14. if user.is_anonymous():
  15. make_read(threads)
  16. return None
  17. if forum:
  18. make_forum_threads_read_aware(user, forum, threads)
  19. else:
  20. make_forums_threads_read_aware(user, threads)
  21. def make_read(threads):
  22. for thread in threads:
  23. thread.unread_replies = 0
  24. thread.is_read = True
  25. thread.is_new = False
  26. def make_forum_threads_read_aware(user, forum, threads):
  27. if forum.is_read:
  28. make_read(threads)
  29. else:
  30. threads_dict = {}
  31. for thread in threads:
  32. thread.is_read = not is_date_tracked(
  33. thread.last_post_on, user, forum.last_read_on)
  34. thread.is_new = True
  35. if thread.is_read:
  36. thread.unread_replies = 0
  37. else:
  38. thread.unread_replies = thread.replies
  39. threads_dict[thread.pk] = thread
  40. if threads_dict:
  41. make_threads_dict_read_aware(user, threads_dict)
  42. def make_forums_threads_read_aware(user, threads):
  43. forums_cutoffs = fetch_forums_cutoffs_for_threads(user, threads)
  44. threads_dict = {}
  45. for thread in threads:
  46. thread.is_read = not is_date_tracked(
  47. thread.last_post_on, user, forums_cutoffs.get(thread.forum_id))
  48. thread.is_new = True
  49. if thread.is_read:
  50. thread.unread_replies = 0
  51. else:
  52. thread.unread_replies = thread.replies
  53. threads_dict[thread.pk] = thread
  54. if threads_dict:
  55. make_threads_dict_read_aware(user, threads_dict)
  56. def fetch_forums_cutoffs_for_threads(user, threads):
  57. forums = []
  58. for thread in threads:
  59. if thread.forum_id not in forums:
  60. forums.append(thread.forum_id)
  61. forums_dict = {}
  62. for record in user.forumread_set.filter(forum__in=forums):
  63. forums_dict[record.forum_id] = record.last_read_on
  64. return forums_dict
  65. def make_threads_dict_read_aware(user, threads_dict):
  66. for record in user.threadread_set.filter(thread__in=threads_dict.keys()):
  67. if record.thread_id in threads_dict:
  68. thread = threads_dict[record.thread_id]
  69. thread.is_new = False
  70. thread.is_read = record.last_read_on >= thread.last_post_on
  71. if thread.is_read:
  72. thread.unread_replies = 0
  73. else:
  74. thread.unread_replies = thread.replies - record.read_replies
  75. if thread.unread_replies < 1:
  76. thread.unread_replies = 1
  77. def make_thread_read_aware(user, thread):
  78. thread.is_read = True
  79. thread.is_new = False
  80. thread.read_record = None
  81. if user.is_anonymous():
  82. thread.last_read_on = timezone.now()
  83. else:
  84. thread.last_read_on = user.reads_cutoff
  85. if user.is_authenticated() and is_date_tracked(thread.last_post_on, user):
  86. thread.is_read = False
  87. thread.is_new = True
  88. try:
  89. forum_record = user.forumread_set.get(forum_id=thread.forum_id)
  90. if thread.last_post_on > forum_record.last_read_on:
  91. try:
  92. thread_record = user.threadread_set.get(thread=thread)
  93. thread.last_read_on = record.last_read_on
  94. thread.is_new = False
  95. if thread.last_post_on <= thread_record.last_read_on:
  96. thread.is_read = True
  97. thread.read_record = thread_record
  98. except ThreadRead.DoesNotExist:
  99. pass
  100. except ForumRead.DoesNotExist:
  101. pass
  102. def make_posts_read_aware(user, thread, posts):
  103. try:
  104. is_thread_read = thread.is_read
  105. except AttributeError:
  106. raise ValueError("thread passed make_posts_read_aware should be "
  107. "read aware too via make_thread_read_aware")
  108. if is_thread_read:
  109. for post in posts:
  110. post.is_read = True
  111. else:
  112. for post in posts:
  113. if is_date_tracked(post.updated_on, user):
  114. post.is_read = post.updated_on <= thread.last_read_on
  115. else:
  116. post.is_read = True
  117. def read_thread(user, thread, last_read_reply):
  118. if not thread.is_read:
  119. if thread.last_read_on < last_read_reply.updated_on:
  120. sync_record(user, thread, last_read_reply)
  121. def sync_record(user, thread, last_read_reply):
  122. read_replies = count_read_replies(user, thread, last_read_reply)
  123. if thread.read_record:
  124. thread.read_record.read_replies = read_replies
  125. thread.read_record.last_read_on = last_read_reply.updated_on
  126. thread.read_record.save(update_fields=['read_replies', 'last_read_on'])
  127. else:
  128. user.threadread_set.create(
  129. forum=thread.forum,
  130. thread=thread,
  131. read_replies=read_replies,
  132. last_read_on=last_read_reply.updated_on)
  133. signals.thread_tracked.send(sender=user, thread=thread)
  134. if last_read_reply.updated_on == thread.last_post_on:
  135. signals.thread_read.send(sender=user, thread=thread)
  136. forumstracker.sync_record(user, thread.forum)
  137. def count_read_replies(user, thread, last_read_reply):
  138. if last_read_reply.updated_on >= thread.last_read_on:
  139. return 0
  140. else:
  141. last_reply_date = last_read_reply.last_read_on
  142. queryset = thread.post_set.filter(last_read_on__lte=last_reply_date)
  143. queryset = queryset.filter(is_moderated=False)
  144. return queryset.count() - 1 # - starters post