Bulk convert topics from a Category to Group PMs

I plan to convert a bunch of topics to group PMs in order to depreciate some barely used categories. We can’t do that via the UI (only one by one), nor can I find a bulk way to do it.

As far as I can tell this should work via the console:

  1. Identify the topics by their category
  2. Change topic.archetype to private_message
  3. Change topic.subtype to ‘user_to_user’
  4. Make topic.category Null
  5. In topic_groups (or maybe topic_allowed_groups), add a line with the relevant group_id and topic_id

Does this approach make sense? I need to work out the difference between those topic/groups tables first of course. And do it in a sandbox for starters!!

1 Like

That could work. Also, if you don’t know you can do something like

 topics = Topic.where(category: 10)

  topics.update_all(archetype: xxx, topic_category: nil)

Or something like that. If you knew about update_all, this isn’t news.

2 Likes

Thanks Jay! That would be nice and fast for the first half. However, I also have to ensure that they end up as Group PMs.

Any idea what I have to do with the topic_groups / topic_allowed_groups tables? I don’t think that I can get away with an update_all for them.

1 Like

I’ve since then (clearly took a while!) worked out two ways to achieve this. You can do it via the UI, which works well for small Categories (and small groups). It takes a bit of care to avoid firing off unhelpful notifications while you do so, but is very doable.

Alternatively, you can use the Rails Console (assuming you are self-hosted), which is faster and much more powerful.

From the UI

  1. Remove the other group members from the Group (but keep a list of usernames / emails so you can re-add them easily). This will avoid them being notified.
  2. Cycle through each topic in the Category, converting each one into a PM using the admin spanner menu
    • I find this easiest to do by opening each topic in its own tab, and then moving across them one by one
  3. Add the Group to each of the Topics (now PMs)
  4. Add the Group members back into the group
  5. Tidy up / delete all the ‘small posts’ in each Group Message as desired

In the Rails Console

Access the console by entering the container and then calling up the console:

./launcher enter app
  rails c

Set the Category and Group, and then copy the script below into the console:

category_id = =CategoryID= 
group_name = '=GroupName='

group = Group.find_by('lower(name) = ?', group_name.downcase)
about_id = Category.find(category_id).topic_id

topic_ids = Topic.where(category_id: category_id).where.not(id: about_id).pluck(:id)

Topic.where(id: topic_ids).find_each do |topic|
  topic.archetype = Archetype.private_message
  topic.category_id = nil
  topic.subtype = 'user_to_user'
  topic.save!

  TopicAllowedGroup.find_or_create_by!(topic_id: topic.id, group_id: group.id)

  Post.where(topic_id: topic.id).pluck(:user_id).uniq.each do |uid|
    TopicAllowedUser.find_or_create_by!(topic_id: topic.id, user_id: uid)
  end
end

This doesn’t notify the users at all, and preserves the users participating in the posts nicely (just in case they aren’t in the Group).

Of note, you might need to run this subsequently in the Rails Console if you want to delete the Category before the background job recounts everything:

Jobs::CategoryStats.new.execute(category_id: category_id)
Script for the reverse (Group Inbox to Category), click to reveal

This script should send things back in the other direction nicely (untested)

category_id = =CategoryID= 
group_name = '=GroupName='

group = Group.find_by('LOWER(name) = ?', group_name.downcase)
raise "Group not found" unless group

topic_ids = TopicAllowedGroup.where(group_id: group.id).pluck(:topic_id)
topics = Topic.where(id: topic_ids, archetype: Archetype.private_message, deleted_at: nil)

topics.find_each do |topic|
  topic.archetype = Archetype.default  # or "regular"
  topic.subtype = nil
  topic.category_id = target_category_id
  topic.save!

end
1 Like