# frozen_string_literal: true require "csv" require File.expand_path(File.dirname(__FILE__) + "/base.rb") # Edit the constants and initialize method for your import data. # Make sure to follow the right format in your CSV files. class ImportScripts::CsvImporter < ImportScripts::Base CSV_FILE_PATH = ENV['CSV_USER_FILE'] || '/var/www/discourse/tmp/users.csv' CSV_CUSTOM_FIELDS = ENV['CSV_CUSTOM_FIELDS'] || '/var/www/discourse/tmp/custom_fields.csv' CSV_EMAILS = ENV['CSV_EMAILS'] || '/var/www/discourse/tmp/emails.csv' CSV_CATEGORIES = ENV['CSV_CATEGORIES'] || '/var/www/discourse/tmp/categories.csv' CSV_TOPICS = ENV['CSV_TOPICS'] || '/var/www/discourse/tmp/topics_new_users.csv' CSV_TOPICS_EXISTING_USERS = ENV['CSV_TOPICS'] || '/var/www/discourse/tmp/topics_existing_users.csv' IMPORT_PREFIX = ENV['IMPORT_PREFIX'] || '2022-08-11' IMPORT_USER_ID_PREFIX = 'csv-user-import-' + IMPORT_PREFIX + '-' IMPORT_CATEGORY_ID_PREFIX = 'csv-category-import-' + IMPORT_PREFIX + '-' IMPORT_TOPIC_ID_PREFIX = 'csv-topic-import-' + IMPORT_PREFIX + '-' IMPORT_TOPIC_ID_EXISITNG_PREFIX = 'csv-topic_existing-import-' + IMPORT_PREFIX + '-' def initialize super @imported_users = load_csv(CSV_FILE_PATH) @imported_emails = load_csv(CSV_EMAILS) @imported_custom_fields = load_csv(CSV_CUSTOM_FIELDS) @imported_custom_fields_names = @imported_custom_fields.headers.drop(1) @imported_categories = load_csv(CSV_CATEGORIES) @imported_topics = load_csv(CSV_TOPICS) @imported_topics_existing_users = load_csv(CSV_TOPICS_EXISTING_USERS) @skip_updates = true end def execute puts "", "Importing from CSV file..." import_users import_categories import_topics import_topics_existing_users puts "", "Done" end def load_csv(path) unless File.exist?(path) puts "File doesn't exist: #{path}" return nil end CSV.parse(File.read(path, encoding: 'bom|utf-8'), headers: true) end def username_for(name) result = name.downcase.gsub(/[^a-z0-9\-\_]/, '') if result.blank? result = Digest::SHA1.hexdigest(name)[0...10] end result end def get_email(id) email = nil @imported_emails.each do |e| if e["user_id"] == id email = e["email"] end end email end def get_custom_fields(id) custom_fields = {} @imported_custom_fields.each do |cf| if cf["user_id"] == id @imported_custom_fields_names.each do |name| custom_fields[name] = cf[name] end end end custom_fields end def import_users puts '', "Importing users" users = [] @imported_users.each do |u| email = get_email(u['id']) custom_fields = get_custom_fields(u['id']) u['email'] = email u['custom_fields'] = custom_fields u['id'] = IMPORT_USER_ID_PREFIX + u['id'] users << u end users.uniq! create_users(users) do |u| { id: u['id'], username: u['username'], email: u['email'], created_at: u['created_at'], custom_fields: u['custom_fields'], } end end def import_categories puts '', "Importing categories" categories = [] @imported_categories.each do |c| c['user_id'] = user_id_from_imported_user_id(IMPORT_USER_ID_PREFIX + c['user_id']) || Discourse::SYSTEM_USER_ID c['id'] = IMPORT_CATEGORY_ID_PREFIX + c['id'] categories << c end categories.uniq! create_categories(categories) do |c| { id: c['id'], user_id: c['user_id'], name: c['name'], description: c['description'] } end end def import_topics puts '', "Importing topics" topics = [] @imported_topics.each do |t| t['user_id'] = user_id_from_imported_user_id(IMPORT_USER_ID_PREFIX + t['user_id']) || Discourse::SYSTEM_USER_ID t['category_id'] = category_id_from_imported_category_id(IMPORT_CATEGORY_ID_PREFIX + t['category_id']) t['id'] = IMPORT_TOPIC_ID_PREFIX + t['id'] topics << t end create_posts(topics) do |t| { id: t['id'], user_id: t['user_id'], title: t['title'], category: t['category_id'], raw: t['raw'] } end end def import_topics_existing_users # Import topics for users that already existed in the DB, not imported during this migration puts '', "Importing topics for existing users" topics = [] @imported_topics_existing_users.each do |t| t['id'] = IMPORT_TOPIC_ID_EXISITNG_PREFIX + t['id'] topics << t end create_posts(topics) do |t| { id: t['id'], user_id: t['user_id'], # This is a Discourse user ID title: t['title'], category: t['category_id'], # This is a Discourse category ID raw: t['raw'] } end end end if __FILE__ == $0 ImportScripts::CsvImporter.new.perform end # == CSV files format # # + File name: users # # headers: id,username # # + File name: emails # # headers: user_id,email # # + File name: custom_fields # # headers: user_id,user_field_1,user_field_2,user_field_3,user_field_4 # # note: the "user_field_1","user_field_2", .. headers are the names of the # custom fields, as defined in Discourse's user_custom_fields table. # # + File name: categories # # headers: id,user_id,name,description # # + File name: topics_new_users # # headers: id,user_id,title,category_id,raw # # + File name: topics_existing_users # # headers: id,user_id,title,category_id,raw # # == Important: except for the topics_existing_users, the IDs in the data can be anything # as long as they are consistent among the files. #