eBooru/db/populate.rb

316 lines
8.8 KiB
Ruby

# frozen_string_literal: true
# This script populates the database with random data for testing or development purposes.
# Usage: docker exec -it e621ng-e621-1 /app/bin/populate
require "faker"
# Environmental variables that govern how much content to generate
presets = {
users: ENV.fetch("USERS", 0).to_i,
posts: ENV.fetch("POSTS", 0).to_i,
comments: ENV.fetch("COMMENTS", 0).to_i,
favorites: ENV.fetch("FAVORITES", 0).to_i,
forums: ENV.fetch("FORUMS", 0).to_i,
postvotes: ENV.fetch("POSTVOTES", 0).to_i,
pools: ENV.fetch("POOLS", 0).to_i,
}
if presets.values.sum == 0
puts "DEFAULTS"
presets = {
users: 10,
posts: 100,
comments: 100,
favorites: 100,
forums: 100,
postvotes: 100,
pools: 100,
}
end
USERS = presets[:users]
POSTS = presets[:posts]
COMMENTS = presets[:comments]
FAVORITES = presets[:favorites]
FORUMS = presets[:forums]
POSTVOTES = presets[:postvotes]
POOLS = presets[:pools]
DISTRIBUTION = ENV.fetch("DISTRIBUTION", 10).to_i
DEFAULT_PASSWORD = ENV.fetch("PASSWORD", "hexerade")
CurrentUser.user = User.system
def api_request(path)
response = Faraday.get("https://e621.net#{path}", nil, user_agent: "e621ng/seeding")
JSON.parse(response.body)
end
def populate_users(number, password: DEFAULT_PASSWORD)
return [] unless number > 0
puts "* Creating #{number} users\n This may take some time."
output = []
number.times do
user_name = generate_username
puts " - #{user_name}"
user_obj = User.create do |user|
user.name = user_name
user.password = password
user.password_confirmation = password
user.email = "#{user_name}@e621.local"
user.level = User::Levels::MEMBER
user.created_at = Faker::Date.between(from: "2007-02-10", to: 2.weeks.ago)
user.profile_about = Faker::Hipster.paragraph_by_chars(characters: rand(100..2_000), supplemental: false) if Faker::Boolean.boolean(true_ratio: 0.2)
user.profile_artinfo = Faker::Hipster.paragraph_by_chars(characters: rand(100..2_000), supplemental: false) if Faker::Boolean.boolean(true_ratio: 0.2)
end
if user_obj.errors.empty?
output << user_obj
puts " user ##{user_obj.id}"
else
puts " error: #{user_obj.errors.full_messages.join('; ')}"
end
end
output
end
def generate_username
loop do
@username = [
Faker::Adjective.positive.split.each(&:capitalize!),
Faker::Creature::Animal.name.split.each(&:capitalize!),
].concat.join("_")
next unless @username.length >= 3 && @username.length <= 20
next unless User.find_by(name: @username).nil?
break
end
@username
end
def populate_posts(number, users: [], batch_size: 320)
return [] unless number > 0
puts "* Creating #{number} posts"
admin = User.find(1)
users = User.where("users.created_at < ?", 7.days.ago).limit(DISTRIBUTION).order("random()") if users.empty?
output = []
# Generate posts in batches of 200 (by default)
number.times.each_slice(batch_size).map(&:size).each do |count|
posts = api_request("/posts.json?tags=rating:s+order:random+score:>250+-grandfathered_content&limit=#{count}")["posts"]
posts.each do |post|
post["tags"].each do |category, tags|
Tag.find_or_create_by_name_list(tags.map { |tag| "#{category}:#{tag}" })
end
CurrentUser.user = users.sample # Stupid, but I can't be bothered
CurrentUser.user = users.sample unless CurrentUser.user.can_upload_with_reason
puts " - #{CurrentUser.user.name} : #{post['file']['url']}"
Post.transaction do
service = UploadService.new(generate_upload(post))
@upload = service.start!
end
if @upload.invalid? || @upload.post.nil?
puts " #{@upload.errors.full_messages.join('; ')}"
else
puts " post: ##{@upload.post.id}"
CurrentUser.scoped(admin) do
@upload.post.approve!
end
output << @upload.post
end
end
end
output
end
def generate_upload(post)
{
uploader: CurrentUser.user,
uploader_ip_addr: "127.0.0.1",
direct_url: post["file"]["url"],
tag_string: post["tags"].values.flatten.join(" "),
source: post["sources"].join("\n"),
description: post["description"],
rating: post["rating"],
}
end
def fill_avatars(users = [], posts = [])
return if users.empty?
puts "* Filling in #{users.size} avatars"
posts = Post.limit(users.size).order("random()") if posts.empty?
puts posts
users.each do |user|
post = posts.sample
puts "post: #{post}"
puts " - #{user.name} : ##{post.id}"
user.update({ avatar_id: post.id })
end
end
def populate_comments(number, users: [])
return unless number > 0
puts "* Creating #{number} comments"
users = User.where("users.created_at < ?", 14.days.ago).limit(DISTRIBUTION).order("random()") if users.empty?
posts = Post.limit(DISTRIBUTION).order("random()")
number.times do
post = posts.sample
CurrentUser.user = users.sample
comment_obj = Comment.create do |comment|
comment.creator = CurrentUser.user
comment.updater = CurrentUser.user
comment.post = post
comment.body = Faker::Hipster.paragraph_by_chars(characters: rand(100..2_000), supplemental: false)
comment.creator_ip_addr = "127.0.0.1"
end
puts " - ##{comment_obj.id} by #{CurrentUser.user.name}"
end
end
def populate_favorites(number, users: [])
return unless number > 0
puts "* Creating #{number} favorites"
users = User.limit(DISTRIBUTION).order("random()") if users.empty?
number.times do |index|
CurrentUser.user = users[index % DISTRIBUTION]
post = Post.order("random()").first
puts " - ##{post.id} faved by #{CurrentUser.user.name}"
begin
Favorite.create do |fav|
fav.user = CurrentUser.user
fav.post = post
end
rescue StandardError
puts " Favorite already exists"
end
end
end
def populate_forums(number, users: [])
return unless number > 0
number -= 1 # Accounts for the first post in the thread
puts "* Creating a topic with #{number} replies"
users = User.where("users.created_at < ?", 14.days.ago).limit(DISTRIBUTION).order("random()") if users.empty?
category = ForumCategory.find_or_create_by!(name: "General") do |cat|
cat.can_view = 0
end
CurrentUser.user = users.sample
CurrentUser.ip_addr = "127.0.0.1"
forum_topic = ForumTopic.create do |topic|
topic.creator = CurrentUser.user
topic.creator_ip_addr = "127.0.0.1"
topic.title = Faker::Lorem.sentence(word_count: 3, supplemental: true, random_words_to_add: 4)
topic.category = category
topic.original_post_attributes = {
creator: CurrentUser.user,
body: Faker::Lorem.paragraphs.join("\n\n"),
}
end
puts " topic ##{forum_topic.id} by #{CurrentUser.user.name}"
unless forum_topic.valid?
puts " #{forum_topic.errors.full_messages.join('; ')}"
end
number.times do
CurrentUser.user = users.sample
forum_post = ForumPost.create do |post|
post.creator = CurrentUser.user
post.topic_id = forum_topic.id
post.body = Faker::Hipster.paragraph_by_chars(characters: rand(100..2_000), supplemental: false)
end
puts " - #{CurrentUser.user.name} | forum post ##{forum_post.id}"
unless forum_post.valid?
puts " #{forum_post.errors.full_messages.join('; ')}"
end
end
end
def populate_post_votes(number, users: [], posts: [])
return unless number > 0
puts "* Generating votes"
users = User.where("users.created_at < ?", 14.days.ago).limit(DISTRIBUTION).order("random()") if users.empty?
posts = Post.limit(100).order("random()") if posts.empty?
number.times do
CurrentUser.user = users.sample
post = posts.sample
vote = VoteManager.vote!(
user: CurrentUser.user,
post: post,
score: Faker::Boolean.boolean(true_ratio: 0.2) ? -1 : 1,
)
if vote == :need_unvote
puts " error: #{vote}"
next
else
puts " vote ##{vote.id} on post ##{post.id}"
end
end
end
def populate_pools(number, posts: [])
return unless number > 0
puts "* Generating pools"
CurrentUser.user = User.find(1)
posts = Post.limit(number).order("random()") if posts.empty?
pool_obj = Pool.create do |pool|
pool.name = Faker::Lorem.sentence
pool.category = "collection"
pool.post_ids = posts.pluck(:id)
end
puts pool_obj
if pool_obj.errors.empty?
puts " pool ##{pool_obj.id}"
else
puts " error: #{pool_obj.errors.full_messages.join('; ')}"
end
end
puts "Populating the Database"
CurrentUser.user = User.find(1)
CurrentUser.ip_addr = "127.0.0.1"
users = populate_users(USERS)
posts = populate_posts(POSTS, users: users)
fill_avatars(users, posts)
populate_comments(COMMENTS, users: users)
populate_favorites(FAVORITES, users: users)
populate_forums(FORUMS, users: users)
populate_post_votes(POSTVOTES, users: users, posts: posts)
populate_pools(POOLS, posts: posts)