docbrown/db/seeds.rb
Molly Struve e87dead7ad
Use new UserSetting and UserNotificationSettings and Ignore Related User Table Fields (#14121)
* schema file undelete description

* feat: v1 of the script

* Flesh out remaining enums under their categories

* complete UsersSettings data update script

* complete DUS for relevant attributes in users and profiles tables

* complete DUS for users_notification_settings

* alphabetize user_settings sql file

* safeguard against null values for "null: false" settings

* Set up actual UsersSettings DUS and specs files

* fix broken DUS script

* complete specs for UsersSetting DUS

* Address QA of specs

* complete specs for users_notification_settings DUS

* fix the typos (thanks Julianna!)

* begin implementation

* still building

* add missing attribute "email_membership_newsletter"

* complete sync code (except race condition for user profile)

* complete implementation, remains tests

* Address PR review and fix Travis fails

* remove superfluous Profile.new

* fix travis fails

* feat: update the users_notification_setting attributes from the user model

* feat: use the config fonts enums to display the fonts

* feat: loop through the keys

* fix profile = nil blowing up; add specs for notification_setting model

* remove unneeded spec

* remove feed validation until after sync code removed; fixes feed_import spec failures

* remove spec associated with feed_url validation in user_setting model

* fix failing spec 😅

* add TODO

* feat: set the user settings in the user controller  and use it in the customization form

* feat: move some update logic to the users settings controller thats being used from customization

* feat: show the updated values form the users_settingd and not the user instance

* Generalize redirect back to current tab

* still trying to reflect changed theme upon refresh

* customizations take effect on refresh

* remove 'with_feed' scope from user model

Co-authored-by: Jamie Gaskins <jamie@forem.com>

* start with takeover for fields previously in profiles table

* Takeover code for `publishing_from_rss` section in Settings (#13914)

* implement takeover code part 1

* implement takeover code

* fix feed fetch

* need rhymes help

* complete implementation; specs pending

* fix STUPID omission that caused so many headaches 😫

* implement profile fields pointing to users_settings 🎉

* run migrations

* implement inbox type & guidelines takeover code; specs pending (#13911)

* Point changes in notification settings to `users_notification_settings` table (#13910)

* implement takeover code; remains specs

* address PR feedback; remove related sync code

* address PR review feedback

* need help with routing and specs

* address pr review

* addressing pr review

* Treat implementation edge cases and omissions 😅

* fix uncommented comment

* fixing implementation cases

* address more PR review feedback

* fixing notifications use-cases

* refactor settings controller

* more pr review changes

* solving bugs

* fix broken onboarding

* handle eperience_level calls

* more fixes

* remove unneeded mappings

* add To-dos for quety updates

* remove done TODO

* purge done TODOs

* update notification_settings-related queries

* start fixing specs

* fixing specs

* fix notification and lrg_forem specs

* fixing broken specs

* still fixing

* fix line dif and remove reloads from user.rb

* run specs

* silence bullet and other fixes

* remove setting migration scripts and specs, fix more settings for specs

* handle missing user for article builder and fix notification specs

* fix some final controller specs and re-add incorrectly removed specs

* remove deprecated data update scripts and related workers, put travis back

* refactor admin tags mods controller, write/move specs for users notifications settings controller

* schema cleanup and other small refactors for consistency

* set field we can invalidate in spec via active record instead of at the db level

* remove I think an uneccessary hook call from subscribe_to_mailchimp_newsletter

* use bnefore_create to setup settings, please dont blow up the test suite

* mailchimp bot fix

* remove decorator in favor of single model method

Co-authored-by: Arit Amana <msarit@gmail.com>
Co-authored-by: Ridhwana <ridhwana.khan16@gmail.com>
Co-authored-by: Arit Amana <32520970+msarit@users.noreply.github.com>
Co-authored-by: Jamie Gaskins <jamie@forem.com>
2021-07-08 09:31:34 -05:00

585 lines
19 KiB
Ruby
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# rubocop:disable Rails/Output
return if Rails.env.production?
# NOTE: when adding new data, please use the Seeder class to ensure the seed tasks
# stays idempotent.
require Rails.root.join("app/lib/seeder")
# we use this to be able to increase the size of the seeded DB at will
# eg.: `SEEDS_MULTIPLIER=2 rails db:seed` would double the amount of data
seeder = Seeder.new
SEEDS_MULTIPLIER = [1, ENV["SEEDS_MULTIPLIER"].to_i].max
puts "Seeding with multiplication factor: #{SEEDS_MULTIPLIER}\n\n"
##############################################################################
# Default development settings are different from production scenario
Settings::UserExperience.public = true
Settings::General.waiting_on_first_user = false
Settings::Authentication.providers = Authentication::Providers.available
##############################################################################
# Disable Redis cache while seeding
Rails.cache = ActiveSupport::Cache.lookup_store(:null_store)
# Put forem into "starter mode"
if ENV["MODE"] == "STARTER"
Settings::UserExperience.public = false
Settings::General.waiting_on_first_user = true
puts "Seeding forem in starter mode to replicate new creator experience"
exit # We don't need any models if we're launching things from startup.
end
seeder.create_if_none(Organization) do
3.times do
Organization.create!(
name: Faker::TvShows::SiliconValley.company,
summary: Faker::Company.bs,
remote_profile_image_url: logo = Faker::Company.logo,
nav_image: logo,
url: Faker::Internet.url,
slug: "org#{rand(10_000)}",
github_username: "org#{rand(10_000)}",
twitter_username: "org#{rand(10_000)}",
bg_color_hex: Faker::Color.hex_color,
text_color_hex: Faker::Color.hex_color,
)
end
end
##############################################################################
# NOTE: @citizen428 For the time being we want all current DEV profile fields.
# The CSV import is idempotent by itself, since it uses find_or_create_by.
seeder.create("Creating DEV profile fields") do
dev_fields_csv = Rails.root.join("lib/data/dev_profile_fields.csv")
ProfileFields::ImportFromCsv.call(dev_fields_csv)
end
##############################################################################
num_users = 10 * SEEDS_MULTIPLIER
users_in_random_order = seeder.create_if_none(User, num_users) do
roles = %i[trusted chatroom_beta_tester workshop_pass]
num_users.times do |i|
name = Faker::Name.unique.name
user = User.create!(
name: name,
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
website_url: Faker::Internet.url,
twitter_username: Faker::Internet.username(specifier: name),
# Emails limited to 50 characters
email: Faker::Internet.email(name: name, separators: "+", domain: Faker::Internet.domain_word.first(20)),
confirmed_at: Time.current,
registered_at: Time.current,
registered: true,
password: "password",
password_confirmation: "password",
)
if i.zero?
user.add_role(:trusted) # guarantee at least one moderator
elsif i == num_users - 1
next # guarantee at least one user with no role
else
role_index = rand(0..roles.length)
user.add_role(roles[role_index]) if role_index != roles.length # increases chance of more no-role users
end
Identity.create!(
provider: "twitter",
uid: i.to_s,
token: i.to_s,
secret: i.to_s,
user: user,
auth_data_dump: {
"extra" => {
"raw_info" => { "lang" => "en" }
},
"info" => { "nickname" => user.username }
},
)
end
Organization.find_each do |organization|
admins = []
admin_id = User.where.not(id: admins).order(Arel.sql("RANDOM()")).first.id
OrganizationMembership.create!(
user_id: admin_id,
organization_id: organization.id,
type_of_user: "admin",
)
admins << admin_id
2.times do
OrganizationMembership.create!(
user_id: User.where.not(id: OrganizationMembership.pluck(:user_id)).order(Arel.sql("RANDOM()")).first.id,
organization_id: organization.id,
type_of_user: "member",
)
end
end
User.order(Arel.sql("RANDOM()"))
end
seeder.create_if_doesnt_exist(User, "email", "admin@forem.local") do
user = User.create!(
name: "Admin McAdmin",
email: "admin@forem.local",
username: "Admin_McAdmin",
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
website_url: Faker::Internet.url,
confirmed_at: Time.current,
password: "password",
password_confirmation: "password",
)
user.add_role(:super_admin)
user.add_role(:tech_admin)
end
Users::CreateMascotAccount.call unless Settings::General.mascot_user_id
##############################################################################
seeder.create_if_none(Tag) do
tags = %w[beginners career computerscience git go
java javascript linux productivity python security webdev]
tags.each do |tag_name|
Tag.create!(
name: tag_name,
bg_color_hex: Faker::Color.hex_color,
text_color_hex: Faker::Color.hex_color,
supported: true,
)
end
end
##############################################################################
num_articles = 25 * SEEDS_MULTIPLIER
seeder.create_if_none(Article, num_articles) do
num_articles.times do |i|
tags = []
tags << "discuss" if (i % 3).zero?
tags.concat Tag.order(Arel.sql("RANDOM()")).limit(3).pluck(:name)
markdown = <<~MARKDOWN
---
title: #{Faker::Book.title} #{Faker::Lorem.sentence(word_count: 2).chomp('.')}
published: true
cover_image: #{Faker::Company.logo}
tags: #{tags.join(', ')}
---
#{Faker::Hipster.paragraph(sentence_count: 2)}
#{Faker::Markdown.random}
#{Faker::Hipster.paragraph(sentence_count: 2)}
MARKDOWN
Article.create!(
body_markdown: markdown,
featured: true,
show_comments: true,
user_id: User.order(Arel.sql("RANDOM()")).first.id,
)
end
end
##############################################################################
num_comments = 30 * SEEDS_MULTIPLIER
seeder.create_if_none(Comment, num_comments) do
num_comments.times do
attributes = {
body_markdown: Faker::Hipster.paragraph(sentence_count: 1),
user_id: User.order(Arel.sql("RANDOM()")).first.id,
commentable_id: Article.order(Arel.sql("RANDOM()")).first.id,
commentable_type: "Article"
}
Comment.create!(attributes)
end
end
##############################################################################
seeder.create_if_none(Podcast) do
image_file = Rails.root.join("spec/support/fixtures/images/image1.jpeg")
podcast_objects = [
{
title: "CodeNewbie",
description: "",
feed_url: "http://feeds.codenewbie.org/cnpodcast.xml",
itunes_url: "https://itunes.apple.com/us/podcast/codenewbie/id919219256",
slug: "codenewbie",
twitter_username: "CodeNewbies",
website_url: "https://www.codenewbie.org/podcast",
main_color_hex: "2faa4a",
overcast_url: "https://overcast.fm/itunes919219256/codenewbie",
android_url: "https://subscribeonandroid.com/feeds.podtrac.com/q8s8ba9YtM6r",
image: Pathname.new(image_file).open,
published: true
},
{
title: "CodingBlocks",
description: "",
feed_url: "https://www.codingblocks.net/podcast-feed.xml",
slug: "codingblocks",
twitter_username: "CodingBlocks",
website_url: "http://codingblocks.net",
main_color_hex: "111111",
overcast_url: "https://overcast.fm/itunes769189585/coding-blocks",
android_url: "http://subscribeonandroid.com/feeds.podtrac.com/c8yBGHRafqhz",
image: Pathname.new(image_file).open,
published: true
},
{
title: "Talk Python",
description: "",
feed_url: "https://talkpython.fm/episodes/rss",
slug: "talkpython",
twitter_username: "TalkPython",
website_url: "https://talkpython.fm",
main_color_hex: "181a1c",
overcast_url: "https://overcast.fm/itunes979020229/talk-python-to-me",
android_url: "https://subscribeonandroid.com/talkpython.fm/episodes/rss",
image: Pathname.new(image_file).open,
published: true
},
{
title: "Developer on Fire",
description: "",
feed_url: "http://developeronfire.com/rss.xml",
itunes_url: "https://itunes.apple.com/us/podcast/developer-on-fire/id1006105326",
slug: "developeronfire",
twitter_username: "raelyard",
website_url: "http://developeronfire.com",
main_color_hex: "343d46",
overcast_url: "https://overcast.fm/itunes1006105326/developer-on-fire",
android_url: "http://subscribeonandroid.com/developeronfire.com/rss.xml",
image: Pathname.new(image_file).open,
published: true
},
]
podcast_objects.each do |attributes|
podcast = Podcast.create!(attributes)
Podcasts::GetEpisodesWorker.perform_async(podcast_id: podcast.id)
end
end
##############################################################################
seeder.create_if_none(Broadcast) do
broadcast_messages = {
set_up_profile: "Welcome to DEV! 👋 I'm Sloan, the community mascot and I'm here to help get you started. " \
"Let's begin by <a href='/settings'>setting up your profile</a>!",
welcome_thread: "Sloan here again! 👋 DEV is a friendly community. " \
"Why not introduce yourself by leaving a comment in <a href='/welcome'>the welcome thread</a>!",
twitter_connect: "You're on a roll! 🎉 Do you have a Twitter account? " \
"Consider <a href='/settings'>connecting it</a> so we can @mention you if we share your post " \
"via our Twitter account <a href='https://twitter.com/thePracticalDev'>@thePracticalDev</a>.",
facebook_connect: "You're on a roll! 🎉 Do you have a Facebook account? " \
"Consider <a href='/settings'>connecting it</a>.",
github_connect: "You're on a roll! 🎉 Do you have a GitHub account? " \
"Consider <a href='/settings'>connecting it</a> so you can pin any of your repos to your profile.",
customize_feed:
"Hi, it's me again! 👋 Now that you're a part of the DEV community, let's focus on personalizing " \
"your content. You can start by <a href='/tags'>following some tags</a> to help customize your feed! 🎉",
customize_experience:
"Sloan here! 👋 Did you know that that you can customize your DEV experience? " \
"Try changing <a href='settings/customization'>your font and theme</a> and find the best style for you!",
start_discussion:
"Sloan here! 👋 I noticed that you haven't " \
"<a href='https://dev.to/t/discuss'>started a discussion</a> yet. Starting a discussion is easy to do; " \
"just click on 'Create Post' in the sidebar of the tag page to get started!",
ask_question:
"Sloan here! 👋 I noticed that you haven't " \
"<a href='https://dev.to/t/explainlikeimfive'>asked a question</a> yet. Asking a question is easy to do; " \
"just click on 'Create Post' in the sidebar of the tag page to get started!",
discuss_and_ask:
"Sloan here! 👋 I noticed that you haven't " \
"<a href='https://dev.to/t/explainlikeimfive'>asked a question</a> or " \
"<a href='https://dev.to/t/discuss'>started a discussion</a> yet. It's easy to do both of these; " \
"just click on 'Create Post' in the sidebar of the tag page to get started!",
download_app: "Sloan here, with one last tip! 👋 Have you downloaded the DEV mobile app yet? " \
"Consider <a href='https://dev.to/downloads'>downloading</a> it so you can access all " \
"of your favorite DEV content on the go!"
}
broadcast_messages.each do |type, message|
Broadcast.create!(
title: "Welcome Notification: #{type}",
processed_html: message,
type_of: "Welcome",
active: true,
)
end
welcome_thread_content = <<~HEREDOC
---
title: Welcome Thread - v0
published: true
description: Introduce yourself to the community!
tags: welcome
---
Hey there! Welcome to #{Settings::Community.community_name}!
Leave a comment below to introduce yourself to the community!
HEREDOC
Article.create!(
body_markdown: welcome_thread_content,
user: User.dev_account || User.first,
)
end
##############################################################################
seeder.create_if_none(ChatChannel) do
%w[Workshop Meta General].each do |chan|
ChatChannel.create!(
channel_name: chan,
channel_type: "open",
slug: chan,
)
end
# This channel is hard-coded in a few places
ChatChannel.create!(
channel_name: "Tag Moderators",
channel_type: "open",
slug: "tag-moderators",
)
direct_channel = ChatChannels::CreateWithUsers.call(users: User.last(2), channel_type: "direct")
Message.create!(
chat_channel: direct_channel,
user: User.last,
message_markdown: "This is **awesome**",
)
end
##############################################################################
seeder.create_if_none(HtmlVariant) do
HtmlVariant.create!(
name: rand(100).to_s,
group: "badge_landing_page",
html: rand(1000).to_s,
success_rate: 0,
published: true,
approved: true,
user_id: User.first.id,
)
end
##############################################################################
seeder.create_if_none(Badge) do
5.times do
Badge.create!(
title: "#{Faker::Lorem.word} #{rand(100)}",
description: Faker::Lorem.sentence,
badge_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
)
end
users_in_random_order.limit(10).each do |user|
user.badge_achievements.create!(
badge: Badge.order(Arel.sql("RANDOM()")).limit(1).take,
rewarding_context_message_markdown: Faker::Markdown.random,
)
end
end
##############################################################################
seeder.create_if_none(FeedbackMessage) do
mod = User.with_role(:trusted).take
FeedbackMessage.create!(
reporter: User.last,
feedback_type: "spam",
message: Faker::Lorem.sentence,
category: "spam",
status: "Open",
)
FeedbackMessage.create!(
reporter: mod,
feedback_type: "abuse-reports",
message: Faker::Lorem.sentence,
reported_url: "example.com",
category: "harassment",
status: "Open",
)
Reaction.create!(
category: "vomit",
reactable_id: User.last.id,
reactable_type: "User",
user_id: mod.id,
)
3.times do
article_id = Article
.left_joins(:reactions)
.where.not(articles: { id: Reaction.article_vomits.pluck(:reactable_id) })
.order(Arel.sql("RANDOM()"))
.first
.id
Reaction.create!(
category: "vomit",
reactable_id: article_id,
reactable_type: "Article",
user_id: mod.id,
)
end
end
##############################################################################
seeder.create_if_none(ListingCategory) do
categories = [
{
slug: "cfp",
cost: 1,
name: "Conference CFP",
rules: "Currently open for proposals, with link to form."
},
{
slug: "education",
cost: 1,
name: "Education/Courses",
rules: "Educational material and/or schools/bootcamps."
},
{
slug: "jobs",
cost: 25,
name: "Job Listings",
rules: "Companies offering employment right now."
},
{
slug: "forsale",
cost: 1,
name: "Stuff for Sale",
rules: "Personally owned physical items for sale."
},
{
slug: "events",
cost: 1,
name: "Upcoming Events",
rules: "In-person or online events with date included."
},
{
slug: "misc",
cost: 1,
name: "Miscellaneous",
rules: "Must not fit in any other category."
},
].freeze
categories.each { |attributes| ListingCategory.create(attributes) }
end
##############################################################################
seeder.create_if_none(Listing) do
users_in_random_order = User.order(Arel.sql("RANDOM()"))
users_in_random_order.each { |user| Credit.add_to(user, rand(100)) }
users = users_in_random_order.to_a
listings_categories = ListingCategory.ids
listings_categories.each.with_index(1) do |category_id, index|
# rotate users if they are less than the categories
user = users.at(index % users.length)
2.times do
Listing.create!(
user: user,
title: Faker::Lorem.sentence,
body_markdown: Faker::Markdown.random,
location: Faker::Address.city,
organization_id: user.organizations.first&.id,
listing_category_id: category_id,
contact_via_connect: true,
published: true,
originally_published_at: Time.current,
bumped_at: Time.current,
tag_list: Tag.order(Arel.sql("RANDOM()")).first(2).pluck(:name),
)
end
end
end
##############################################################################
seeder.create_if_none(Page) do
5.times do
Page.create!(
title: Faker::Hacker.say_something_smart,
body_markdown: Faker::Markdown.random,
slug: Faker::Internet.slug,
description: Faker::Books::Dune.quote,
template: %w[contained full_within_layout].sample,
)
end
end
##############################################################################
seeder.create_if_none(Sponsorship) do
organizations = Organization.take(3)
organizations.each do |organization|
Sponsorship.create!(
organization: organization,
user: User.order(Arel.sql("RANDOM()")).first,
level: "silver",
blurb_html: Faker::Hacker.say_something_smart,
)
end
end
##############################################################################
seeder.create_if_none(NavigationLink) do
Rake::Task["navigation_links:update"].invoke
end
##############################################################################
puts <<-ASCII
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::::'########::'#######::'########::'########:'##::::'##::::
:::: ##.....::'##.... ##: ##.... ##: ##.....:: ###::'###::::
:::: ##::::::: ##:::: ##: ##:::: ##: ##::::::: ####'####::::
:::: ######::: ##:::: ##: ########:: ######::: ## ### ##::::
:::: ##...:::: ##:::: ##: ##.. ##::: ##...:::: ##. #: ##::::
:::: ##::::::: ##:::: ##: ##::. ##:: ##::::::: ##:.:: ##::::
:::: ##:::::::. #######:: ##:::. ##: ########: ##:::: ##::::
::::..:::::::::.......:::..:::::..::........::..:::::..:::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
All done!
ASCII
# rubocop:enable Rails/Output