threads.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. from __future__ import unicode_literals
  2. from django.contrib.auth import get_user_model
  3. from django.utils import timezone
  4. from misago.categories.models import Category
  5. from misago.threads.checksums import update_post_checksum
  6. from misago.threads.models import (
  7. Thread, ThreadParticipant, Post, PostEdit, PostLike)
  8. from . import fetch_assoc, markup, movedids, localise_datetime
  9. UserModel = get_user_model()
  10. def move_threads(stdout, style):
  11. special_categories = get_special_categories_dict()
  12. for thread in fetch_assoc('SELECT * FROM misago_thread ORDER BY id'):
  13. if special_categories.get(thread['forum_id']) == 'reports':
  14. stdout.write(style.WARNING(
  15. "Skipping report: %s" % thread['name']))
  16. continue
  17. if not thread['start_post_id']:
  18. stdout.write(style.ERROR(
  19. "Corrupted thread: %s" % thread['name']))
  20. continue
  21. if special_categories.get(thread['forum_id']) == 'private_threads':
  22. category = Category.objects.private_threads()
  23. else:
  24. if thread['prefix_id']:
  25. label_id = '%s-%s' % (thread['prefix_id'], thread['forum_id'])
  26. category_pk = movedids.get('label', label_id)
  27. else:
  28. category_pk = movedids.get('category', thread['forum_id'])
  29. category = Category.objects.get(pk=category_pk)
  30. new_thread = Thread.objects.create(
  31. category=category,
  32. title=thread['name'],
  33. slug=thread['slug'],
  34. started_on=timezone.now(),
  35. last_post_on=timezone.now(),
  36. starter_name='none',
  37. starter_slug='none',
  38. weight=thread['weight'],
  39. is_hidden=thread['deleted'],
  40. is_closed=thread['closed'],
  41. )
  42. movedids.set('thread', thread['id'], new_thread.pk)
  43. def move_posts():
  44. special_categories = get_special_categories_dict()
  45. for post in fetch_assoc('SELECT * FROM misago_post ORDER BY id'):
  46. if special_categories.get(post['forum_id']) == 'reports':
  47. continue
  48. thread_pk = movedids.get('thread', post['thread_id'])
  49. thread = Thread.objects.select_related('category').get(pk=thread_pk)
  50. poster = None
  51. last_editor = None
  52. if post['user_id']:
  53. poster_id = movedids.get('user', post['user_id'])
  54. poster = UserModel.objects.get(pk=poster_id)
  55. if post['edit_user_id']:
  56. last_editor_id = movedids.get('user', post['edit_user_id'])
  57. last_editor = UserModel.objects.get(pk=last_editor_id)
  58. deleter = None
  59. deleter_name = None
  60. deleter_slug = None
  61. if post['deleted']:
  62. deleter = UserModel.objects.filter(
  63. is_staff=True
  64. ).order_by('id').last()
  65. if deleter:
  66. deleter_name = deleter.username
  67. deleter_slug = deleter.slug
  68. else:
  69. deleter = poster
  70. deleter_name = poster.username
  71. deleter_slug = poster.slug
  72. new_post = Post.objects.create(
  73. category=thread.category,
  74. thread=thread,
  75. poster=poster,
  76. poster_name=post['user_name'],
  77. poster_ip=post['ip'],
  78. original=markup.convert_original(post['post']),
  79. parsed=markup.convert_parsed(post['post_preparsed']),
  80. posted_on=localise_datetime(post['date']),
  81. updated_on=localise_datetime(post['current_date'] or post['date']),
  82. hidden_on=localise_datetime(post['delete_date'] or post['date']),
  83. edits=post['edits'],
  84. last_editor=last_editor,
  85. last_editor_name=post['edit_user_name'],
  86. last_editor_slug=post['edit_user_slug'],
  87. hidden_by=deleter,
  88. hidden_by_name=deleter_name,
  89. hidden_by_slug=deleter_slug,
  90. is_hidden=post['deleted'],
  91. is_protected=post['protected'],
  92. likes=post['upvotes']
  93. )
  94. update_post_checksum(new_post)
  95. new_post.save()
  96. movedids.set('post', post['id'], new_post.pk)
  97. def move_mentions():
  98. for metion in fetch_assoc('SELECT * FROM misago_post_mentions'):
  99. try:
  100. post_pk = movedids.get('post', metion['post_id'])
  101. user_pk = movedids.get('user', metion['user_id'])
  102. post = Post.objects.get(pk=post_pk)
  103. user = UserModel.objects.get(pk=user_pk)
  104. post.mentions.add(user)
  105. except:
  106. continue
  107. def move_edits():
  108. query = 'SELECT DISTINCT post_id FROM misago_change ORDER BY post_id'
  109. for edit in fetch_assoc(query):
  110. post_pk = movedids.get('post', edit['post_id'])
  111. post = Post.objects.select_related().get(pk=post_pk)
  112. move_post_edits(post, edit['post_id'])
  113. def move_post_edits(post, old_id):
  114. query = '''
  115. SELECT *
  116. FROM
  117. misago_change
  118. WHERE
  119. post_id = %s AND `change` <> 0
  120. ORDER BY
  121. id
  122. '''
  123. changelog = []
  124. for edit in fetch_assoc(query, [old_id]):
  125. if edit['user_id']:
  126. editor_pk = movedids.get('user', edit['user_id'])
  127. editor = UserModel.objects.get(pk=editor_pk)
  128. else:
  129. editor = None
  130. if changelog:
  131. changelog[-1].edited_to = edit['post_content']
  132. changelog.append(PostEdit(
  133. category=post.category,
  134. thread=post.thread,
  135. post=post,
  136. edited_on=localise_datetime(edit['date']),
  137. editor=editor,
  138. editor_name=edit['user_name'],
  139. editor_slug=edit['user_slug'],
  140. editor_ip=edit['ip'],
  141. edited_from=edit['post_content'],
  142. edited_to=post.original,
  143. ))
  144. if changelog:
  145. PostEdit.objects.bulk_create(changelog)
  146. def move_likes():
  147. query = '''
  148. SELECT *
  149. FROM
  150. misago_karma
  151. WHERE
  152. score > 0
  153. ORDER BY
  154. id
  155. '''
  156. posts = []
  157. for karma in fetch_assoc(query):
  158. post_pk = movedids.get('post', karma['post_id'])
  159. post = Post.objects.select_related().get(pk=post_pk)
  160. if karma['user_id']:
  161. liker_pk = movedids.get('user', karma['user_id'])
  162. liker = UserModel.objects.get(pk=liker_pk)
  163. else:
  164. liker = None
  165. PostLike.objects.create(
  166. category=post.category,
  167. thread=post.thread,
  168. post=post,
  169. liker=liker,
  170. liker_name=karma['user_name'],
  171. liker_slug=karma['user_slug'],
  172. liker_ip=karma['ip'],
  173. liked_on=localise_datetime(karma['date']),
  174. )
  175. posts.append(post_pk)
  176. for post in Post.objects.filter(id__in=posts).iterator():
  177. post.last_likes = []
  178. for like in post.postlike_set.all()[:4]:
  179. post.last_likes.append({
  180. 'id': like.liker_id,
  181. 'username': like.liker_name
  182. })
  183. post.save(update_fields=['last_likes'])
  184. def move_participants():
  185. for participant in fetch_assoc('SELECT * FROM misago_thread_participants'):
  186. thread_pk = movedids.get('thread', participant['thread_id'])
  187. thread = Thread.objects.get(pk=thread_pk)
  188. user_pk = movedids.get('user', participant['user_id'])
  189. user = UserModel.objects.get(pk=user_pk)
  190. starter = thread.post_set.order_by('id').first().poster
  191. ThreadParticipant.objects.create(
  192. thread=thread,
  193. user=user,
  194. is_owner=(user == starter)
  195. )
  196. def clean_private_threads(stdout, style):
  197. category = Category.objects.private_threads()
  198. # prune threads without participants
  199. participated_threads = ThreadParticipant.objects.values_list(
  200. 'thread_id', flat=True).distinct()
  201. for thread in category.thread_set.exclude(pk__in=participated_threads):
  202. thread.delete()
  203. # close threads with single participant, delete empty ones
  204. for thread in category.thread_set.iterator():
  205. participants_count = thread.participants.count()
  206. if participants_count == 1:
  207. thread.is_closed = True
  208. thread.save()
  209. elif participants_count == 0:
  210. thread.delete()
  211. stdout.write(style.ERROR(
  212. "Delete empty private thread: %s" % thread.title))
  213. def get_special_categories_dict():
  214. special_categories = {}
  215. query = '''
  216. SELECT
  217. id, special
  218. FROM
  219. misago_forum
  220. WHERE
  221. special IS NOT NULL
  222. '''
  223. for special in fetch_assoc(query):
  224. special_categories[special['id']] = special['special']
  225. return special_categories