From 20a16ea231f8be82ee3fc0d0c0cc81e94301c554 Mon Sep 17 00:00:00 2001 From: Ruben Oussoren Date: Tue, 7 Jan 2025 08:58:26 -0500 Subject: [PATCH] Adding Import Support for Discourse Reactions Plugin (#30361) * Adding Import Support for Discourse Reactions Plugin --------- Co-authored-by: Gerhard Schlager --- script/bulk_import/base.rb | 52 +++++++++++ script/bulk_import/generic_bulk.rb | 138 ++++++++++++++++++++++++++++- 2 files changed, 186 insertions(+), 4 deletions(-) diff --git a/script/bulk_import/base.rb b/script/bulk_import/base.rb index 53d9bea05ba..32faf7c8f59 100644 --- a/script/bulk_import/base.rb +++ b/script/bulk_import/base.rb @@ -120,6 +120,7 @@ class BulkImport::Base chat_channel: 6, chat_thread: 7, chat_message: 8, + discourse_reactions_reaction: 9, ) def create_migration_mappings_table @@ -309,6 +310,10 @@ class BulkImport::Base @chat_message_mapping = load_index(MAPPING_TYPES[:chat_message]) @last_chat_message_id = last_id(Chat::Message) + + puts "Loading reaction indexes..." + @discourse_reaction_mapping = load_index(MAPPING_TYPES[:discourse_reactions_reaction]) + @last_discourse_reaction_id = last_id(DiscourseReactions::Reaction) end def use_bbcode_to_md? @@ -390,6 +395,11 @@ class BulkImport::Base "SELECT setval('#{Chat::Message.sequence_name}', #{@last_chat_message_id})", ) end + if @last_discourse_reaction_id > 0 + @raw_connection.exec( + "SELECT setval('#{DiscourseReactions::Reaction.sequence_name}', #{@last_discourse_reaction_id})", + ) + end end def group_id_from_imported_id(id) @@ -475,6 +485,10 @@ class BulkImport::Base @chat_message_mapping[id.to_s]&.to_i end + def discourse_reaction_id_from_original_id(id) + @discourse_reaction_mapping[id.to_s]&.to_i + end + GROUP_COLUMNS = %i[ id name @@ -925,6 +939,18 @@ class BulkImport::Base CHAT_MENTION_COLUMNS = %i[chat_message_id target_id type created_at updated_at] + REACTION_USER_COLUMNS = %i[reaction_id user_id created_at updated_at post_id] + + REACTION_COLUMNS = %i[ + id + post_id + reaction_type + reaction_value + reaction_users_count + created_at + updated_at + ] + def create_groups(rows, &block) create_records(rows, "group", GROUP_COLUMNS, &block) end @@ -1157,6 +1183,14 @@ class BulkImport::Base create_records(rows, "chat_mention", CHAT_MENTION_COLUMNS, &block) end + def create_reaction_users(rows, &block) + create_records(rows, "discourse_reactions_reaction_user", REACTION_USER_COLUMNS, &block) + end + + def create_reactions(rows, &block) + create_records_with_mapping(rows, "discourse_reactions_reaction", REACTION_COLUMNS, &block) + end + def process_group(group) @groups[group[:imported_id].to_i] = group[:id] = @last_group_id += 1 @@ -1962,6 +1996,24 @@ class BulkImport::Base mention end + def process_discourse_reactions_reaction_user(reaction_user) + reaction_user[:created_at] ||= NOW + reaction_user[:updated_at] ||= NOW + reaction_user + end + + def process_discourse_reactions_reaction(reaction) + reaction[:id] = @last_discourse_reaction_id += 1 + reaction[:created_at] ||= NOW + reaction[:updated_at] ||= NOW + reaction[:reaction_users_count] ||= 0 + + @imported_records[reaction[:original_id].to_s] = reaction[:id] + @discourse_reaction_mapping[reaction[:original_id].to_s] = reaction[:id] + + reaction + end + def create_records(all_rows, name, columns, &block) start = Time.now imported_ids = [] diff --git a/script/bulk_import/generic_bulk.rb b/script/bulk_import/generic_bulk.rb index 2ede9b1fdba..20270f7f71a 100644 --- a/script/bulk_import/generic_bulk.rb +++ b/script/bulk_import/generic_bulk.rb @@ -126,6 +126,10 @@ class BulkImport::Generic < BulkImport::Base update_chat_threads update_chat_membership_metadata + import_reactions + import_reaction_users + import_reaction_shadow_likes + import_upload_references end @@ -320,16 +324,19 @@ class BulkImport::Generic < BulkImport::Base puts "", "Importing category permissions..." permissions = query(<<~SQL) - SELECT c.id AS category_id, p.value -> 'group_id' AS group_id, p.value -> 'permission_type' AS permission_type - FROM categories c, - JSON_EACH(c.permissions) p + SELECT c.id AS category_id, + p.value -> 'group_id' AS group_id, + p.value -> 'existing_group_id' AS existing_group_id, + p.value -> 'permission_type' AS permission_type + FROM categories c, + JSON_EACH(c.permissions) p SQL existing_category_group_ids = CategoryGroup.pluck(:category_id, :group_id).to_set create_category_groups(permissions) do |row| category_id = category_id_from_imported_id(row["category_id"]) - group_id = group_id_from_imported_id(row["group_id"]) + group_id = row["existing_group_id"] || group_id_from_imported_id(row["group_id"]) next if existing_category_group_ids.include?([category_id, group_id]) { category_id: category_id, group_id: group_id, permission_type: row["permission_type"] } @@ -2943,6 +2950,129 @@ class BulkImport::Generic < BulkImport::Base puts " Update took #{(Time.now - start_time).to_i} seconds." end + def import_reaction_users + unless defined?(::DiscourseReactions) + puts "", + "Skipping reaction users import, because the Discourse Reactions plugin is not installed." + return + end + + puts "", "Importing reaction users..." + + reaction_users = query(<<~SQL) + SELECT * + FROM discourse_reactions_reaction_users + ORDER BY post_id, user_id + SQL + + existing_reaction_users = + DiscourseReactions::ReactionUser.pluck(:reaction_id, :user_id, :post_id).to_set + + create_reaction_users(reaction_users) do |row| + next if row["reaction_id"].blank? + + user_id = user_id_from_imported_id(row["user_id"]) + post_id = post_id_from_imported_id(row["post_id"]) + reaction_id = discourse_reaction_id_from_original_id(row["reaction_id"]) + + next if post_id.blank? || user_id.blank? || reaction_id.blank? + next unless existing_reaction_users.add?([reaction_id, user_id, post_id]) + + { + reaction_id:, + user_id:, + created_at: to_datetime(row["created_at"]), + updated_at: to_datetime(row["updated_at"]), + post_id:, + } + end + + reaction_users.close + end + + def import_reactions + unless defined?(::DiscourseReactions) + puts "", "Skipping reactions import, because the Discourse Reactions plugin is not installed." + return + end + + puts "", "Importing reactions..." + + reactions = query(<<~SQL) + SELECT * + FROM discourse_reactions_reactions + ORDER BY post_id, reaction_value + SQL + + reaction_type_id = DiscourseReactions::Reaction.reaction_types["emoji"] + existing_reactions = DiscourseReactions::Reaction.pluck(:post_id, :reaction_value).to_set + + create_reactions(reactions) do |row| + next if row["id"].blank? + + post_id = post_id_from_imported_id(row["post_id"]) + + next if post_id.blank? || row["reaction_value"].blank? + next unless existing_reactions.add?([post_id, row["reaction_value"]]) + + { + original_id: row["id"], + post_id: post_id, + reaction_type: reaction_type_id, + reaction_value: row["reaction_value"], + reaction_users_count: row["reaction_users_count"], + created_at: to_datetime(row["created_at"]), + updated_at: to_datetime(row["updated_at"]), + } + end + + reactions.close + end + + def import_reaction_shadow_likes + unless defined?(::DiscourseReactions) + puts "", + "Skipping reaction shadow likes import, because the Discourse Reactions plugin is not installed." + return + end + + puts "", "Importing reaction shadow likes..." + + reactions = query(<<~SQL) + SELECT u.post_id, u.user_id, r.reaction_value, u.created_at + FROM discourse_reactions_reactions r + JOIN discourse_reactions_reaction_users u ON r.id = u.reaction_id + ORDER BY u.post_id, u.user_id + SQL + + post_action_type_id = PostActionType.types[:like] + existing_likes = + PostAction.where(post_action_type_id: post_action_type_id).pluck(:post_id, :user_id).to_set + + create_post_actions(reactions) do |row| + next if reaction_excluded_from_like?(row["reaction_value"]) + + post_id = post_id_from_imported_id(row["post_id"]) + user_id = user_id_from_imported_id(row["user_id"]) + + next unless post_id && user_id + next unless existing_likes.add?([post_id, user_id]) + + { + post_id: post_id, + user_id: user_id, + post_action_type_id: post_action_type_id, + created_at: to_datetime(row["created_at"]), + } + end + + reactions.close + end + + def reaction_excluded_from_like?(reaction_value) + DiscourseReactions::Reaction.reactions_excluded_from_like.include?(reaction_value) + end + def calculate_external_url(row) external_url = row["external_url"].dup placeholders = row["external_url_placeholders"]&.then { |json| JSON.parse(json) }