20181024224956_migrate_account_conversations.rb 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. # frozen_string_literal: true
  2. require_relative '../../lib/mastodon/migration_warning'
  3. class MigrateAccountConversations < ActiveRecord::Migration[5.2]
  4. include Mastodon::MigrationWarning
  5. disable_ddl_transaction!
  6. class MigrationAccount < ApplicationRecord
  7. self.table_name = :accounts
  8. has_many :mentions, inverse_of: :account, dependent: :destroy, class_name: 'MigrationMention', foreign_key: :account_id
  9. end
  10. class MigrationConversation < ApplicationRecord
  11. self.table_name = :conversations
  12. end
  13. class MigrationStatus < ApplicationRecord
  14. self.table_name = :statuses
  15. belongs_to :account, class_name: 'MigrationAccount'
  16. has_many :mentions, dependent: :destroy, inverse_of: :status, class_name: 'MigrationMention', foreign_key: :status_id
  17. scope :local, -> { where(local: true).or(where(uri: nil)) }
  18. enum visibility: { public: 0, unlisted: 1, private: 2, direct: 3, limited: 4 }, _suffix: :visibility
  19. has_many :active_mentions, -> { active }, class_name: 'MigrationMention', inverse_of: :status, foreign_key: :status_id
  20. end
  21. class MigrationMention < ApplicationRecord
  22. self.table_name = :mentions
  23. belongs_to :account, inverse_of: :mentions, class_name: 'MigrationAccount'
  24. belongs_to :status, -> { unscope(where: :deleted_at) }, class_name: 'MigrationStatus'
  25. scope :active, -> { where(silent: false) }
  26. delegate(
  27. :username,
  28. :acct,
  29. to: :account,
  30. prefix: true
  31. )
  32. end
  33. class MigrationNotification < ApplicationRecord
  34. self.table_name = :notifications
  35. belongs_to :account, optional: true, class_name: 'MigrationAccount'
  36. belongs_to :activity, polymorphic: true, optional: true
  37. belongs_to :status, foreign_key: 'activity_id', optional: true, class_name: 'MigrationStatus'
  38. belongs_to :mention, foreign_key: 'activity_id', optional: true, class_name: 'MigrationMention'
  39. def target_status
  40. mention&.status
  41. end
  42. end
  43. class MigrationAccountConversation < ApplicationRecord
  44. self.table_name = :account_conversations
  45. belongs_to :account, class_name: 'MigrationAccount'
  46. belongs_to :conversation, class_name: 'MigrationConversation'
  47. belongs_to :last_status, -> { unscope(where: :deleted_at) }, class_name: 'MigrationStatus'
  48. before_validation :set_last_status
  49. class << self
  50. def add_status(recipient, status)
  51. conversation = find_or_initialize_by(account: recipient, conversation_id: status.conversation_id, participant_account_ids: participants_from_status(recipient, status))
  52. return conversation if conversation.status_ids.include?(status.id)
  53. conversation.status_ids << status.id
  54. conversation.unread = status.account_id != recipient.id
  55. conversation.save
  56. conversation
  57. rescue ActiveRecord::StaleObjectError
  58. retry
  59. end
  60. private
  61. def participants_from_status(recipient, status)
  62. ((status.active_mentions.pluck(:account_id) + [status.account_id]).uniq - [recipient.id]).sort
  63. end
  64. end
  65. private
  66. def set_last_status
  67. self.status_ids = status_ids.sort
  68. self.last_status_id = status_ids.last
  69. end
  70. end
  71. def up
  72. migration_duration_warning
  73. migrated = 0
  74. last_time = Time.zone.now
  75. local_direct_statuses.includes(:account, mentions: :account).find_each do |status|
  76. MigrationAccountConversation.add_status(status.account, status)
  77. migrated += 1
  78. if Time.zone.now - last_time > 1
  79. say_progress(migrated)
  80. last_time = Time.zone.now
  81. end
  82. end
  83. notifications_about_direct_statuses.includes(:account, mention: { status: [:account, { mentions: :account }] }).find_each do |notification|
  84. MigrationAccountConversation.add_status(notification.account, notification.target_status)
  85. migrated += 1
  86. if Time.zone.now - last_time > 1
  87. say_progress(migrated)
  88. last_time = Time.zone.now
  89. end
  90. end
  91. end
  92. def down; end
  93. private
  94. def say_progress(migrated)
  95. say "Migrated #{migrated} rows", true
  96. end
  97. def local_direct_statuses
  98. MigrationStatus.unscoped.local.where(visibility: :direct)
  99. end
  100. def notifications_about_direct_statuses
  101. MigrationNotification.joins('INNER JOIN mentions ON mentions.id = notifications.activity_id INNER JOIN statuses ON statuses.id = mentions.status_id').where(activity_type: 'Mention', statuses: { visibility: :direct })
  102. end
  103. end