text:
# frozen_string_literal: true

require 'test_helper'

class FlaggingTest < ActionDispatch::IntegrationTest
  def setup
    # Use a user with flagging enabled and permissive settings

    @user = User.first
    setup_user_with_permissive_flag_settings(@user)

    # Pick a couple of random main sites

    @site = Site.mains.where(max_flags_per_post: 3).order(Arel.sql('RAND()')).last
    @limited_site = Site.mains.where(max_flags_per_post: 1).order(Arel.sql('RAND()')).last

    setup_posts
    setup_webmock
  end

  def setup_posts
    # Post setup

    @stack_id = 1234
    @multi_rev_stack_id = 4321

    Reason.update_all(weight: 10)

    common_attrs = {
      site: @site,
      reasons: Reason.last(2),
      user_reputation: 1,
      stack_exchange_user: StackExchangeUser.new(username: 'asdf',
                                                 reputation: 1,
                                                 site: @site)
    }

    @post = Post.create({
      link: "//#{@site.site_domain}/questions/#{@stack_id}"
    }.reverse_merge!(common_attrs))

    @multi_rev_post = Post.create({
      link: "//#{@site.site_domain}/questions/#{@multi_rev_stack_id}"
    }.reverse_merge!(common_attrs))

    @limited_post = Post.create({
      link: "//#{@limited_site.site_domain}/questions/#{@stack_id}",
      site: @limited_site,
      stack_exchange_user: StackExchangeUser.new(username: 'asdf',
                                                 reputation: 1,
                                                 site: @limited_site)
    }.reverse_merge!(common_attrs))
  end

  def setup_webmock
    WebMock.reset!

    stub_request(:get, %r{https://api.stackexchange.com/2\.2/users/123/associated})
      .to_return(status: 200, body: webmock_file('mod_sites_response'), headers: {})

    @single_rev_stub = stub_request(:get, %r{https://api.stackexchange.com/2\.2/posts/#{@stack_id}/revisions})
                       .to_return(status: 200, body: webmock_file('single_revision_response'), headers: {})

    @multi_rev_stub = stub_request(:get, %r{https://api.stackexchange.com/2\.2/posts/#{@multi_rev_stack_id}/revisions})
                      .to_return(status: 200, body: webmock_file('multi_revision_response'), headers: {})

    @flag_options_stub = stub_request(:get, %r{https://api.stackexchange.com/2\.2/(questions|answers)/\d+/flags/options})
                         .to_return(status: 200, body: webmock_file('flag_options_response'), headers: {})

    @flag_submit_stub = stub_request(:post, %r{https://api.stackexchange.com/2\.2/(questions|answers)/\d+/flags/add})
                        .to_return(status: 200, body: webmock_file('flag_submit_response'), headers: {})
  end

  def webmock_file(name)
    File.open("#{Rails.root}/test/integration/webmock_json_responses/#{name}.json").read
  end

  def setup_user_with_permissive_flag_settings(user)
    user.flags_enabled = true
    user.encrypted_api_token = SecureRandom.uuid
    user.stack_exchange_account_id = 123
    user.save!

    user.add_role :core

    user.user_site_settings.destroy_all

    site_setting = user.user_site_settings.new(max_flags: 100)
    site_setting.sites = Site.mains
    site_setting.save!

    flag_condition = user.flag_conditions.new(min_weight: 10,
                                              max_poster_rep: 1,
                                              min_reason_count: 1,
                                              sites: Site.mains)

    # Ignore any flag accuracy warnings; we're not concerned about them right now
    flag_condition.save(validate: false)
  end

# rubocop:disable Style/BlockComments
=begin
  test 'should update moderator sites' do
    @user.moderator_sites.destroy_all
    assert_equal 0, @user.moderator_sites.count

    @user.update_moderator_sites
    assert_equal 3, @user.moderator_sites.count

    previous_ids = @user.moderator_site_ids
    @user.update_moderator_sites
    assert_equal previous_ids.sort, @user.moderator_site_ids.sort
  end

  test 'should refuse to flag on a site user is a moderator on' do
    @user.moderator_sites.create(site: Site.first)

    success, message = @user.spam_flag(Post.new(site: Site.first))

    assert_not success
    assert_equal message, 'User is a moderator on this site'
  end

  test 'should refuse to flag if user has no access token' do
    @user.api_token = nil

    assert_raise do
      @user.spam_flag(Post.new)
    end
  end

  test 'should set revision count' do
    @post.get_revision_count

    assert_requested @single_rev_stub
    assert_equal 1, @post.revision_count

    @multi_rev_post.get_revision_count

    assert_requested @multi_rev_stub
    assert_operator @multi_rev_post.revision_count, :>, 1
  end

  test "shouldn't flag if flagging is disabled" do
    FlagSetting.find_by(name: 'flagging_enabled').update(value: '0')
    assert_equal 'Flagging disabled', @post.autoflag
  end

  test "shouldn't flag if there are no eligible users" do
    User.update_all(flags_enabled: false)

    assert_equal 'No users eligible to flag', @post.autoflag
  end

  test 'should request revision count' do
    @post.autoflag

    assert_requested @single_rev_stub
  end

  test "shouldn't flag if post has more than one revision" do
    assert_equal 'More than one revision', @multi_rev_post.autoflag
  end

  test 'should flag flaggable post' do
    @post.autoflag

    assert_requested @flag_options_stub, at_least_times: 1
    assert_requested @flag_submit_stub, at_least_times: 1
  end

  test "shouldn't flag if post doesn't have enough weight" do
    Reason.update_all(weight: 1)

    @post.reload.autoflag

    assert_not_requested @flag_submit_stub
  end

  test "shouldn't flag if user has too much rep" do
    @post.stack_exchange_user.reputation = 100_000

    @post.autoflag

    assert_not_requested @flag_submit_stub
  end

  test 'should cast only three flags on flaggable post' do
    10.times do
      user = @user.dup
      user.email = SecureRandom.hex
      user.save!(validate: false)

      setup_user_with_permissive_flag_settings(user)
    end

    @post.autoflag
    assert_requested @flag_submit_stub, times: 3
  end

  test 'should cast only one flag on flaggable post on limited site' do
    10.times do
      user = @user.dup
      user.email = SecureRandom.hex
      user.save!(validate: false)

      setup_user_with_permissive_flag_settings(user)
    end

    @limited_post.autoflag
    assert_requested @flag_submit_stub, times: 1
  end

  test 'should respect max flags setting' do
    10.times do
      user = @user.dup
      user.email = SecureRandom.hex
      user.save!(validate: false)

      setup_user_with_permissive_flag_settings(user)
    end

    FlagSetting.find_by(name: 'max_flags').update(value: '2')

    @post.autoflag
    assert_requested @flag_submit_stub, times: 2
  end

  test 'should respect dry run setting' do
    FlagSetting.find_by(name: 'dry_run').update(value: '1')

    @post.autoflag
    assert_requested @flag_options_stub, at_least_times: 1
    assert_not_requested @flag_submit_stub
  end

  test 'should respect scaled max flag setting' do
    FlagSetting.find_by(name: 'scaled_max_flags').update(value: '0,100,100,100,100,100')

    @post.autoflag
    assert_requested @flag_submit_stub, times: 1
  end
=end
  # rubocop:enable Style/BlockComments
end

repo_name: Charcoal-SE/metasmoke

repo_language: Ruby

mime_type: text/plain


text:
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2020_09_17_100116) do

  create_table "abuse_comments", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "user_id"
    t.bigint "abuse_report_id"
    t.text "text"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["abuse_report_id"], name: "index_abuse_comments_on_abuse_report_id"
    t.index ["user_id"], name: "index_abuse_comments_on_user_id"
  end

  create_table "abuse_contacts", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "name"
    t.string "email"
    t.string "link"
    t.text "details"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "abuse_report_statuses", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "icon"
    t.string "color"
  end

  create_table "abuse_reports", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "user_id"
    t.string "reportable_type"
    t.bigint "reportable_id"
    t.bigint "abuse_contact_id"
    t.bigint "abuse_report_status_id"
    t.text "details"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "uuid"
    t.index ["abuse_contact_id"], name: "index_abuse_reports_on_abuse_contact_id"
    t.index ["abuse_report_status_id"], name: "index_abuse_reports_on_abuse_report_status_id"
    t.index ["reportable_type", "reportable_id"], name: "index_abuse_reports_on_reportable_type_and_reportable_id"
    t.index ["user_id"], name: "index_abuse_reports_on_user_id"
  end

  create_table "announcements", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.text "text"
    t.datetime "expiry"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "api_keys", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "key"
    t.string "app_name"
    t.bigint "user_id"
    t.string "github_link"
    t.boolean "is_trusted"
    t.index ["user_id"], name: "index_api_keys_on_user_id"
  end

  create_table "api_tokens", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "code"
    t.integer "api_key_id"
    t.bigint "user_id"
    t.string "token"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.datetime "expiry"
    t.index ["api_key_id"], name: "index_api_tokens_on_api_key_id"
    t.index ["user_id"], name: "index_api_tokens_on_user_id"
  end

  create_table "audits", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.integer "auditable_id"
    t.string "auditable_type"
    t.integer "associated_id"
    t.string "associated_type"
    t.integer "user_id"
    t.string "user_type"
    t.string "username"
    t.string "action"
    t.text "audited_changes"
    t.integer "version", default: 0
    t.string "comment"
    t.string "remote_address"
    t.string "request_uuid"
    t.datetime "created_at"
    t.index ["associated_id", "associated_type"], name: "associated_index", length: { associated_type: 191 }
    t.index ["auditable_id", "auditable_type"], name: "auditable_index", length: { auditable_type: 191 }
    t.index ["created_at"], name: "index_audits_on_created_at"
    t.index ["request_uuid"], name: "index_audits_on_request_uuid", length: 191
    t.index ["user_id", "user_type"], name: "user_index", length: { user_type: 191 }
  end

  create_table "blazer_audits", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "user_id"
    t.bigint "query_id"
    t.text "statement"
    t.string "data_source"
    t.timestamp "created_at"
    t.index ["query_id"], name: "index_blazer_audits_on_query_id"
    t.index ["user_id"], name: "index_blazer_audits_on_user_id"
  end

  create_table "blazer_checks", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "creator_id"
    t.bigint "query_id"
    t.string "state"
    t.string "schedule"
    t.text "emails"
    t.string "check_type"
    t.text "message"
    t.timestamp "last_run_at"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.text "slack_channels"
    t.index ["creator_id"], name: "index_blazer_checks_on_creator_id"
    t.index ["query_id"], name: "index_blazer_checks_on_query_id"
  end

  create_table "blazer_dashboard_queries", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "dashboard_id"
    t.bigint "query_id"
    t.integer "position"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["dashboard_id"], name: "index_blazer_dashboard_queries_on_dashboard_id"
    t.index ["query_id"], name: "index_blazer_dashboard_queries_on_query_id"
  end

  create_table "blazer_dashboards", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "creator_id"
    t.text "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["creator_id"], name: "index_blazer_dashboards_on_creator_id"
  end

  create_table "blazer_queries", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "creator_id"
    t.string "name"
    t.text "description"
    t.text "statement"
    t.string "data_source"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["creator_id"], name: "index_blazer_queries_on_creator_id"
  end

  create_table "channels_users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "user_id"
    t.string "secret"
    t.string "link"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["user_id"], name: "index_channels_users_on_user_id"
  end

  create_table "commit_statuses", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "commit_sha"
    t.string "status"
    t.string "commit_message"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "ci_url"
  end

  create_table "deletion_logs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.integer "post_id"
    t.boolean "is_deleted"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "api_key_id"
    t.integer "uncertainty"
    t.index ["post_id"], name: "post_id_ix"
  end

  create_table "domain_groups", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "name"
    t.string "regex"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "domain_groups_spam_domains", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "domain_group_id"
    t.bigint "spam_domain_id"
    t.datetime "created_at", default: -> { "CURRENT_TIMESTAMP" }, null: false
    t.datetime "updated_at", default: -> { "CURRENT_TIMESTAMP" }, null: false
  end

  create_table "domain_links", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "left_id"
    t.bigint "right_id"
    t.string "link_type"
    t.text "comments"
    t.bigint "creator_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["creator_id"], name: "index_domain_links_on_creator_id"
    t.index ["left_id"], name: "index_domain_links_on_left_id"
    t.index ["right_id"], name: "index_domain_links_on_right_id"
  end

  create_table "domain_tags", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "name"
    t.text "description"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.boolean "special", default: false
  end

  create_table "domain_tags_posts", primary_key: ["domain_tag_id", "post_id"], options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "domain_tag_id", null: false
    t.bigint "post_id", null: false
  end

  create_table "domain_tags_spam_domains", primary_key: ["domain_tag_id", "spam_domain_id"], options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.integer "domain_tag_id", default: 0, null: false
    t.integer "spam_domain_id", default: 0, null: false
  end

  create_table "dumps", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "file_file_name"
    t.string "file_content_type"
    t.integer "file_file_size"
    t.datetime "file_updated_at"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "emails_addressees", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "name"
    t.string "email_address"
    t.string "manage_key"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["email_address"], name: "index_emails_addressees_on_email_address", unique: true
  end

  create_table "emails_preferences", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "emails_type_id"
    t.bigint "emails_addressee_id"
    t.datetime "next"
    t.integer "frequency"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "consent_via"
    t.text "consent_comment"
    t.string "reference_type"
    t.bigint "reference_id"
    t.boolean "enabled", default: false, null: false
    t.index ["emails_addressee_id"], name: "index_emails_preferences_on_emails_addressee_id"
    t.index ["emails_type_id"], name: "index_emails_preferences_on_emails_type_id"
    t.index ["reference_type", "reference_id"], name: "index_emails_preferences_on_reference_type_and_reference_id"
  end

  create_table "emails_types", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "name"
    t.text "description"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "method_name"
    t.string "mailer"
    t.string "short_name"
  end

  create_table "feedbacks", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "message_link"
    t.string "user_name"
    t.string "user_link"
    t.string "feedback_type"
    t.integer "post_id"
    t.string "post_link"
    t.integer "user_id"
    t.boolean "is_invalidated", default: false
    t.integer "invalidated_by"
    t.datetime "invalidated_at"
    t.integer "chat_user_id"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer "api_key_id"
    t.string "chat_host"
    t.string "legacy_feedback_type"
    t.index ["created_at"], name: "index_feedbacks_on_created_at"
    t.index ["post_id"], name: "index_feedbacks_on_post_id"
    t.index ["user_name"], name: "by_user_name", length: 5
  end

  create_table "flag_conditions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.boolean "flags_enabled", default: true
    t.integer "min_weight"
    t.integer "max_poster_rep"
    t.integer "min_reason_count"
    t.bigint "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["user_id"], name: "index_flag_conditions_on_user_id"
  end

  create_table "flag_conditions_sites", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.integer "flag_condition_id"
    t.integer "site_id"
  end

  create_table "flag_logs", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.boolean "success"
    t.text "error_message"
    t.integer "flag_condition_id"
    t.bigint "user_id"
    t.bigint "post_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.boolean "is_dry_run"
    t.integer "backoff"
    t.integer "site_id"
    t.boolean "is_auto", default: true
    t.integer "api_key_id"
    t.string "flag_type"
    t.text "comment"
    t.index ["api_key_id"], name: "index_flag_logs_on_api_key_id"
    t.index ["created_at"], name: "index_flag_logs_on_created_at"
    t.index ["flag_condition_id"], name: "index_flag_logs_on_flag_condition_id"
    t.index ["flag_type"], name: "index_flag_logs_on_flag_type"
    t.index ["post_id"], name: "index_flag_logs_on_post_id"
    t.index ["site_id"], name: "index_flag_logs_on_site_id"
    t.index ["user_id"], name: "index_flag_logs_on_user_id"
  end

  create_table "flag_settings", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "name"
    t.string "value"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "flags", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "reason"
    t.string "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.boolean "is_completed", default: false
    t.bigint "post_id"
    t.index ["post_id"], name: "index_flags_on_post_id"
  end

  create_table "github_tokens", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "token"
    t.datetime "expires"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "lists_items", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.text "content"
    t.bigint "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.bigint "lists_list_id"
    t.index ["lists_list_id"], name: "index_lists_items_on_lists_list_id"
    t.index ["user_id"], name: "index_lists_items_on_user_id"
  end

  create_table "lists_links", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "lists_item_id"
    t.string "linked_type"
    t.bigint "linked_id"
    t.text "notes"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.boolean "auto", default: false
    t.bigint "user_id"
    t.index ["linked_type", "linked_id"], name: "index_lists_links_on_linked_type_and_linked_id"
    t.index ["lists_item_id"], name: "index_lists_links_on_lists_item_id"
    t.index ["user_id"], name: "index_lists_links_on_user_id"
  end

  create_table "lists_lists", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "name"
    t.text "description"
    t.string "write_privs"
    t.string "manage_privs"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "link_table"
    t.string "link_field"
    t.string "modifier"
    t.boolean "deregexify"
  end

  create_table "moderator_sites", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.integer "user_id"
    t.integer "site_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "post_comments", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "post_id"
    t.bigint "user_id"
    t.text "text"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["post_id"], name: "index_post_comments_on_post_id"
    t.index ["user_id"], name: "index_post_comments_on_user_id"
  end

  create_table "posts", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "title"
    t.text "body", limit: 16777215
    t.string "link"
    t.datetime "post_creation_date"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer "site_id"
    t.string "user_link"
    t.string "username"
    t.text "why", limit: 16777215
    t.integer "user_reputation"
    t.integer "score"
    t.integer "upvote_count"
    t.integer "downvote_count"
    t.integer "stack_exchange_user_id"
    t.boolean "is_tp", default: false
    t.boolean "is_fp", default: false
    t.boolean "is_naa", default: false
    t.integer "revision_count"
    t.datetime "deleted_at"
    t.integer "smoke_detector_id"
    t.boolean "autoflagged", default: false
    t.string "tags"
    t.integer "feedbacks_count"
    t.bigint "native_id"
    t.text "markdown", limit: 16777215
    t.index ["autoflagged"], name: "index_posts_on_autoflagged"
    t.index ["body"], name: "index_posts_on_body", type: :fulltext
    t.index ["created_at"], name: "index_posts_on_created_at"
    t.index ["feedbacks_count"], name: "index_posts_on_feedbacks_count"
    t.index ["is_fp"], name: "index_posts_on_is_fp"
    t.index ["is_naa"], name: "index_posts_on_is_naa"
    t.index ["is_tp"], name: "index_posts_on_is_tp"
    t.index ["link"], name: "index_posts_on_link", length: 191
    t.index ["site_id", "native_id"], name: "index_posts_on_site_id_and_native_id"
  end

  create_table "posts_reasons", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.integer "reason_id"
    t.integer "post_id"
    t.index ["post_id"], name: "index_posts_reasons_on_post_id"
    t.index ["reason_id"], name: "index_posts_reasons_on_reason_id"
  end

  create_table "posts_spam_domains", primary_key: ["post_id", "spam_domain_id"], options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.integer "post_id", default: 0, null: false
    t.integer "spam_domain_id", default: 0, null: false
    t.bigint "added_by_id"
    t.index ["added_by_id"], name: "index_posts_spam_domains_on_added_by_id"
    t.index ["post_id"], name: "index_posts_spam_domains_on_post_id"
    t.index ["spam_domain_id"], name: "index_posts_spam_domains_on_spam_domain_id"
  end

  create_table "query_averages", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "path"
    t.bigint "counter", default: 0, null: false
    t.decimal "average", precision: 14, scale: 3, default: "0.0", null: false
  end

  create_table "reasons", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "reason_name"
    t.string "last_post_title"
    t.boolean "inactive", default: false
    t.integer "weight", default: 0
    t.integer "maximum_weight", limit: 1
    t.text "description"
  end

  create_table "review_items", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "review_queue_id"
    t.string "reviewable_type"
    t.bigint "reviewable_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.boolean "completed"
    t.index ["review_queue_id"], name: "index_review_items_on_review_queue_id"
    t.index ["reviewable_type", "reviewable_id"], name: "index_review_items_on_reviewable_type_and_reviewable_id"
  end

  create_table "review_queues", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "name"
    t.string "privileges"
    t.text "responses"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.text "description"
    t.string "reviewable_type"
  end

  create_table "review_results", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "user_id"
    t.string "result"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.bigint "review_item_id"
    t.index ["review_item_id"], name: "index_review_results_on_review_item_id"
    t.index ["user_id"], name: "index_review_results_on_user_id"
  end

  create_table "roles", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "name"
    t.string "resource_type"
    t.integer "resource_id"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.index ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id", length: { name: 191, resource_type: 191 }
    t.index ["name"], name: "index_roles_on_name", length: 191
  end

  create_table "site_settings", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "name"
    t.string "value"
    t.string "value_type"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "sites", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "site_name"
    t.string "site_url"
    t.string "site_logo"
    t.string "site_domain"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.boolean "flags_enabled", default: false
    t.integer "max_flags_per_post", default: 1
    t.boolean "is_child_meta"
    t.datetime "last_users_update"
    t.string "api_parameter"
    t.boolean "closed", default: false, null: false
    t.boolean "auto_disputed_flags_enabled", default: true
    t.index ["api_parameter"], name: "index_sites_on_api_parameter"
  end

  create_table "sites_spam_waves", primary_key: ["site_id", "spam_wave_id"], options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.bigint "site_id", null: false
    t.bigint "spam_wave_id", null: false
  end

  create_table "sites_user_site_settings", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.integer "site_id"
    t.integer "user_site_setting_id"
  end

  create_table "smoke_detectors", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.datetime "last_ping"
    t.string "name"
    t.string "location"
    t.string "access_token"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.datetime "email_date"
    t.bigint "user_id"
    t.boolean "is_standby", default: false
    t.boolean "force_failover", default: false
    t.boolean "force_pull", default: false
    t.index ["user_id"], name: "index_smoke_detectors_on_user_id"
  end

  create_table "spam_domains", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "domain"
    t.text "whois"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "spam_waves", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "name"
    t.text "conditions", limit: 16777215
    t.bigint "user_id"
    t.datetime "expiry"
    t.integer "max_flags"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["user_id"], name: "index_spam_waves_on_user_id"
  end

  create_table "stack_exchange_users", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.integer "user_id"
    t.string "username"
    t.datetime "last_api_update"
    t.boolean "still_alive", default: true
    t.integer "answer_count"
    t.integer "question_count"
    t.integer "reputation"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "site_id"
  end

  create_table "statistics", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.integer "posts_scanned"
    t.integer "smoke_detector_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "api_quota"
    t.float "post_scan_rate"
  end

  create_table "user_site_settings", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.integer "max_flags"
    t.integer "flags_used", default: 0
    t.bigint "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["user_id"], name: "index_user_site_settings_on_user_id"
  end

  create_table "users", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.string "email"
    t.string "encrypted_password"
    t.datetime "remember_created_at"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.string "username"
    t.integer "stackexchange_chat_id"
    t.integer "meta_stackexchange_chat_id"
    t.integer "stackoverflow_chat_id"
    t.integer "stack_exchange_account_id"
    t.boolean "flags_enabled", default: false
    t.string "encrypted_api_token_legacy"
    t.string "two_factor_token"
    t.boolean "enabled_2fa"
    t.binary "salt"
    t.binary "iv"
    t.boolean "announcement_emails"
    t.boolean "oauth_created"
    t.boolean "eu_resident"
    t.boolean "privacy_accepted"
    t.boolean "token_migrated_legacy", default: false, null: false
    t.boolean "write_authenticated", default: false, null: false
    t.index ["email"], name: "index_users_on_email", unique: true
  end

  create_table "users_roles", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", force: :cascade do |t|
    t.integer "user_id"
    t.integer "role_id"
    t.boolean "pinned", default: false
    t.index ["user_id", "role_id"], name: "index_users_roles_on_user_id_and_role_id"
  end

  add_foreign_key "abuse_comments", "abuse_reports"
  add_foreign_key "abuse_comments", "users"
  add_foreign_key "abuse_reports", "abuse_contacts"
  add_foreign_key "abuse_reports", "abuse_report_statuses"
  add_foreign_key "abuse_reports", "users"
  add_foreign_key "api_keys", "users"
  add_foreign_key "api_tokens", "api_keys"
  add_foreign_key "api_tokens", "users"
  add_foreign_key "channels_users", "users"
  add_foreign_key "domain_links", "spam_domains", column: "left_id"
  add_foreign_key "domain_links", "spam_domains", column: "right_id"
  add_foreign_key "domain_links", "users", column: "creator_id"
  add_foreign_key "emails_preferences", "emails_addressees"
  add_foreign_key "emails_preferences", "emails_types"
  add_foreign_key "flag_conditions", "users"
  add_foreign_key "flag_logs", "api_keys"
  add_foreign_key "flag_logs", "posts"
  add_foreign_key "flag_logs", "sites"
  add_foreign_key "flags", "posts"
  add_foreign_key "lists_items", "lists_lists"
  add_foreign_key "lists_items", "users"
  add_foreign_key "lists_links", "lists_items"
  add_foreign_key "lists_links", "users"
  add_foreign_key "post_comments", "posts"
  add_foreign_key "post_comments", "users"
  add_foreign_key "posts_spam_domains", "users", column: "added_by_id"
  add_foreign_key "review_items", "review_queues"
  add_foreign_key "review_results", "users"
  add_foreign_key "smoke_detectors", "users"
  add_foreign_key "spam_waves", "users"
  add_foreign_key "user_site_settings", "users"
end


text:
# frozen_string_literal: true

Rails.application.eager_load!

Dir.glob(Rails.root.join('db/seeds/**/*.yml')).each do |f|
  basename = Pathname.new(f).relative_path_from(Pathname.new(Rails.root.join('db/seeds'))).to_s
  type = basename.gsub('.yml', '').singularize.classify.constantize
  begin
    data = YAML.load_file f
    type.create data
  rescue StandardError => e
    puts "Got error #{e}. Continuing..."
  end
end

SitesHelper.update_sites


text:
# frozen_string_literal: true

EXCLUDE_TABLES = %w[ar_internal_metadata flags github_tokens schema_migrations emails_addressees emails_preferences].freeze
EXCLUDE_COLUMNS = {
  'api_keys' => ['key'],
  'api_tokens' => %w[code token],
  'audits' => ['remote_address'],
  'smoke_detectors' => ['access_token'],
  'users' => %w[email encrypted_password reset_password_token encrypted_api_token two_factor_token enabled_2fa salt iv eu_resident privacy_accepted]
}.freeze

tables = ActiveRecord::Base.connection.tables
database = ActiveRecord::Base.connection.current_database
queries = []

queries << ['CREATE USER', "CREATE USER IF NOT EXISTS metasmoke_blazer@localhost IDENTIFIED BY 'zFpc8tw7CdAuXizX';"]

tables.each do |t|
  next if EXCLUDE_TABLES.include? t
  columns = ActiveRecord::Base.connection.columns(t).map(&:name) - (EXCLUDE_COLUMNS[t] || [])
  queries << ["CREATE VIEW p_#{t}", "CREATE OR REPLACE VIEW p_#{t} AS SELECT #{columns.join(', ')} FROM #{t};"]
  queries << ["GRANT SELECT ON #{database}.p_#{t}", "GRANT SELECT ON #{database}.p_#{t} TO metasmoke_blazer@localhost;"]
end

queries.each do |q|
  puts q[0]
  ActiveRecord::Base.connection.execute q[1]
end


text:
# frozen_string_literal: true

# Rails is stupid and likes making tables' default charset latin1. When you tell it to use utf8mb4 like it should, it
# decides that all the text columns are going to be latin1_swedish_ci instead, obviously. So, this script is here to
# reset all the table and column collations so that you can run it and then db:schema:dump to get a corrected schema.
#
# Lifesaving reference: https://stackoverflow.com/q/1294117/3160466

tables_columns = ActiveRecord::Base.connection.tables.map { |t| [t, ActiveRecord::Base.connection.columns(t)] }.to_h

tables_columns.each do |t, cs|
  puts t
  puts '  CONVERT TO CHARACTER SET utf8mb4...'
  ActiveRecord::Base.connection.execute "ALTER TABLE `#{t}` CONVERT TO CHARACTER SET utf8mb4;"

  puts '  ALTER DEFAULT CHARSET/COLLATE...'
  ActiveRecord::Base.connection.execute "ALTER TABLE `#{t}` DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;"

  puts '  ALTER MODIFY...'
  cs.select { |c| %i[string text].include? c.type }.each do |c|
    puts "    #{c.name}"
    ActiveRecord::Base.connection.execute "ALTER TABLE `#{t}` MODIFY `#{c.name}` #{c.sql_type} CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
  end
end


text:
- location: Scope/Location
  access_token: 9b104345-ef52-41ce-86cc-003831e3a241
  last_ping: 1980-01-01T00:00:00Z


text:
- name: flagging_enabled
  value: 0
- name: registration_enabled
  value: 0
- name: min_accuracy
  value: 99.75
- name: min_post_count
  value: 1000
- name: dry_run
  value: 0
- name: scaled_max_flags
  value: 0,0,0,99.9,101,101
- name: max_flags
  value: 4


text:
- name: require_auth_all_pages
  value_type: boolean
  value: 0
- name: registration_enabled
  value_type: boolean
  value: 1
- name: new_account_messages_enabled
  value_type: boolean
  value: 1
- name: make_trolls_lives_hard
  value_type: boolean
  value: 0
- name: auto_flagger_role
  value_type: boolean
  value: 1
- name: log_query_timings
  value_type: boolean
  value: 0
- name: min_wave_accuracy
  value_type: float
  value: 95.0
- name: min_wave_post_count
  value_type: number
  value: 10
- name: review_old_posts_days_threshold
  value_type: number
  value: 60
- name: review_old_posts_weight_threshold
  value_type: number
  value: 5


text:
# frozen_string_literal: true

class AddOauthCreatedToUsers < ActiveRecord::Migration[5.1]
  def change
    add_column :users, :oauth_created, :boolean

    User.all.each do |u|
      u.update(oauth_created: true) if /\d+@se-oauth\.metasmoke/.match? u.email
    end
  end
end


text:
# frozen_string_literal: true

class DeviseCreateUsers < ActiveRecord::Migration[4.2]
  def change
    create_table(:users) do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ''
      t.string :encrypted_password, null: false, default: ''

      ## Recoverable
      # t.string   :reset_password_token
      # t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at

      t.timestamps
    end

    add_index :users, :email, unique: true
    # add_index :users, :reset_password_token, unique: true
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,         unique: true
  end
end


text:
# frozen_string_literal: true

class CreateFlagConditions < ActiveRecord::Migration[5.0]
  def change
    create_table :flag_conditions do |t|
      t.boolean :flags_enabled
      t.integer :min_weight
      t.integer :max_poster_rep
      t.integer :min_reason_count
      t.references :user, foreign_key: true

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class EncryptAPITokens < ActiveRecord::Migration[5.1]
  def up
    encryption_key = AppConfig['stack_exchange']['token_aes_key']

    User.where.not(api_token: nil).each do |u|
      puts u
      u.encrypted_api_token = AESCrypt.encrypt(u.api_token, encryption_key)
      u.save!
    end
  end

  def down; end
end


text:
# frozen_string_literal: true

class CheckUserInvalidations < ActiveRecord::Migration[5.0]
  def change
    Feedback.pluck(:user_name).uniq.each do |username|
      total_count = Feedback.unscoped.where(user_name: username).count
      invalid_count = Feedback.unscoped.where(user_name: username, is_invalidated: true).count
      next unless invalid_count > (0.04 * total_count) + 4
      ignored = IgnoredUser.new
      ignored.user_name = username
      ignored.is_ignored = true
      ignored.save
    end
  end
end


text:
# frozen_string_literal: true

class PrepareForLoggingHell < ActiveRecord::Migration[5.2]
  def change
    File.delete Rails.root.join('log/query_times.log')
    SiteSetting.create name: 'log_query_timings', value_type: 'boolean', value: 0
  end
end


text:
class AddAddedByToPostsSpamDomains < ActiveRecord::Migration[5.2]
  def change
    add_reference :posts_spam_domains, :added_by, foreign_key: { to_table: :users }
  end
end


text:
# frozen_string_literal: true

class AddIndexesToPostsTable < ActiveRecord::Migration[5.2]
  def change
    add_index :posts, :is_tp
    add_index :posts, :is_fp
    add_index :posts, :is_naa
  end
end


text:
# frozen_string_literal: true

class AddIsChildMetaToSites < ActiveRecord::Migration[5.0]
  def change
    add_column :sites, :is_child_meta, :boolean
    SitesHelper.updateSites
  end
end


text:
# frozen_string_literal: true

class AddIsInvalidatedToFeedbacks < ActiveRecord::Migration[5.0]
  def change
    add_column :feedbacks, :is_invalidated, :boolean, default: 0
  end
end


text:
# frozen_string_literal: true

class AddCompletedToReviewItems < ActiveRecord::Migration[5.2]
  def change
    add_column :review_items, :completed, :boolean
  end
end


text:
# frozen_string_literal: true

class AddCryptFieldsToUsers < ActiveRecord::Migration[5.1]
  def change
    add_column :users, :salt, :binary
    add_column :users, :iv, :binary
  end
end


text:
# frozen_string_literal: true

class AddAppNameToAPIKeys < ActiveRecord::Migration[5.0]
  def change
    add_column :api_keys, :app_name, :string
  end
end


text:
# frozen_string_literal: true

class AddSpecialToDomainTags < ActiveRecord::Migration[5.2]
  def change
    add_column :domain_tags, :special, :boolean, default: false
  end
end


text:
# frozen_string_literal: true

class AddUserToAPIKeys < ActiveRecord::Migration[5.0]
  def change
    add_reference :api_keys, :user, foreign_key: true
  end
end


text:
# frozen_string_literal: true

class CreateAPITokens < ActiveRecord::Migration[5.0]
  def change
    create_table :api_tokens do |t|
      t.string :code
      t.references :api_key, foreign_key: true
      t.references :user, foreign_key: true
      t.string :token

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class UpdateFeedbackCountsAgain < ActiveRecord::Migration[5.2]
  def change
    counts = Post.left_joins(:feedbacks).group(Arel.sql('posts.id')).count
    count_values = counts.map { |k, v| "(#{k}, #{v})" }.join(', ')
    sql = "INSERT INTO posts (id, feedbacks_count) VALUES #{count_values} ON DUPLICATE KEY UPDATE feedbacks_count = VALUES(feedbacks_count);"
    ActiveRecord::Base.connection.execute sql
  end
end


text:
# frozen_string_literal: true

class AddAPIKeyIdToDeletionLogs < ActiveRecord::Migration[5.1]
  def change
    add_column :deletion_logs, :api_key_id, :integer
  end
end


text:
class CreateEmailsTypes < ActiveRecord::Migration[5.2]
  def change
    create_table :emails_types do |t|
      t.string :name
      t.text :description

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class AddModeratorSitesToUser < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :moderator_sites, :text
  end
end


text:
class FixEmailsAddressees < ActiveRecord::Migration[5.2]
  def change
    Emails::Preference.destroy_all
    Emails::Addressee.destroy_all
    add_index :emails_addressees, :email_address, unique: true
  end
end


text:
class AddWaveAccuracySiteSetting < ActiveRecord::Migration[5.2]
  def change
    SiteSetting.create name: 'min_wave_accuracy', value_type: 'float', value: '90.00'
    SiteSetting.create name: 'min_wave_post_count', value_type: 'number', value: '10'
  end
end


text:
# frozen_string_literal: true

class AddUncertaintyToDeletionLogs < ActiveRecord::Migration[5.2]
  def change
    add_column :deletion_logs, :uncertainty, :integer
  end
end


text:
# frozen_string_literal: true

class RunCreateViewsDomainTagsPosts < ActiveRecord::Migration[5.2]
  def change
    success = system 'rails runner db/scripts/create_views.rb'
    raise StandardError, 'View script had non-zero exit code' unless success
  end
end


text:
# frozen_string_literal: true

class AddTimestampsToFeedbacks < ActiveRecord::Migration[5.0]
  def change
    add_column(:feedbacks, :created_at, :datetime)
    add_column(:feedbacks, :updated_at, :datetime)
  end
end


text:
# frozen_string_literal: true

class AddUserToSmokeDetectors < ActiveRecord::Migration[5.0]
  def change
    add_reference :smoke_detectors, :user, foreign_key: true
  end
end


text:
# frozen_string_literal: true

class AddAutoflaggedToPosts < ActiveRecord::Migration[5.1]
  def change
    add_column :posts, :autoflagged, :boolean, default: false

    Post.where(id: (Post.left_joins(:flag_logs)
        .joins("LEFT JOIN (SELECT posts.id AS 'post_id', COUNT(DISTINCT flag_logs.id) AS 'autoflag_count' FROM posts INNER JOIN flag_logs " \
        'ON flag_logs.post_id = posts.id WHERE flag_logs.is_auto = 1 GROUP BY posts.id) AS flag_counts ON flag_counts.post_id = posts.id')
        .where(flag_counts: { autoflag_count: 0 }) +
      Post.left_joins(:flag_logs).where(flag_logs: { post_id: nil })).map(&:id)).update_all(autoflagged: false)
    Post.joins(:flag_logs).where(flag_logs: { is_auto: true }).update_all(autoflagged: true)
  end
end


text:
# frozen_string_literal: true

class DropPullRequests < ActiveRecord::Migration[5.0]
  def change
    drop_table :pull_requests
  end
end


text:
# frozen_string_literal: true

class ExonerateOldSpammers < ActiveRecord::Migration[5.1]
  def change
    StackExchangeUser.where('id < 51476').update_all(still_alive: false)
  end
end


text:
# frozen_string_literal: true

class CreateCommitStatuses < ActiveRecord::Migration[5.0]
  def change
    create_table :commit_statuses do |t|
      t.string :commit_sha
      t.string :status
      t.string :commit_message

      t.timestamps
    end
  end
end


text:
class AddReferencedObjectToEmailsPreferences < ActiveRecord::Migration[5.2]
  def change
    add_reference :emails_preferences, :reference, polymorphic: true
  end
end


text:
# frozen_string_literal: true

class AddUsernameToPosts < ActiveRecord::Migration[4.2]
  def change
    add_column :posts, :username, :string
  end
end


text:
# frozen_string_literal: true

class AddTokenMigratedFlagToUser < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :token_migrated, :bool, default: false, null: false
  end
end


text:
# frozen_string_literal: true

class AddFlagPropertiesToUser < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :flags_enabled, :boolean, default: false
  end
end


text:
class AddShortNameToEmailsTypes < ActiveRecord::Migration[5.2]
  def change
    add_column :emails_types, :short_name, :string
  end
end


text:
# frozen_string_literal: true

class AddChatIdsToUser < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :stackexchange_chat_id, :integer
    add_column :users, :meta_stackexchange_chat_id, :integer
    add_column :users, :stackoverflow_chat_id, :integer
  end
end


text:
# frozen_string_literal: true

class CreateDumps < ActiveRecord::Migration[5.2]
  def change
    create_table :dumps do |t|
      t.attachment :file
      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class AddWeightToReason < ActiveRecord::Migration[5.0]
  def change
    add_column :reasons, :weight, :int, default: 0
    # Weight is an integer between 0 and 100, defined as (tp count) / (total)

    ReasonsHelper.calculate_weights_for_flagging
  end
end


text:
# frozen_string_literal: true

class RemoveBlacklistedWebsites < ActiveRecord::Migration[5.0]
  def change
    drop_table :blacklisted_websites
  end
end


text:
# frozen_string_literal: true

class AddFlagAccuracyLimitDefaults < ActiveRecord::Migration[5.0]
  def change
    FlagSetting.create(
      name: 'min_accuracy',
      value: '99'
    )

    FlagSetting.create(
      name: 'min_post_count',
      value: '1000'
    )
  end
end


text:
# frozen_string_literal: true

class UpdateReviewResultForMultipleQueues < ActiveRecord::Migration[5.2]
  def change
    ReviewResult.all.delete_all # easier than the alternative of trying to create queues and items for them all
    add_reference :review_results, :review_item, index: true
    remove_column :review_results, :feedback_id
    remove_column :review_results, :post_id
  end
end


text:
# frozen_string_literal: true

class AddPostScanRateToStatistic < ActiveRecord::Migration[5.0]
  def change
    add_column :statistics, :post_scan_rate, :float
  end
end


text:
# frozen_string_literal: true

class ConvertSiteNamesToUtf8 < ActiveRecord::Migration[5.0]
  def up
    execute 'ALTER TABLE `sites` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin'

    SitesHelper.updateSites
  end

  def down; end
end


text:
# frozen_string_literal: true

class RunCreateViews < ActiveRecord::Migration[5.2]
  def change
    success = system 'rails runner db/scripts/create_views.rb'
    raise StandardError, 'View script had non-zero exit code' unless success
  end
end


text:
class TryViewsAgain < ActiveRecord::Migration[5.2]
  def change
    success = system 'rails runner db/scripts/create_views.rb'
    raise StandardError, 'View script had non-zero exit code' unless success
  end
end


text:
# frozen_string_literal: true

class CreatePostComments < ActiveRecord::Migration[5.2]
  def change
    create_table :post_comments do |t|
      t.references :post, foreign_key: true
      t.references :user, foreign_key: true
      t.text :text

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class AddCoreThresholdSiteSetting < ActiveRecord::Migration[5.2]
  def change
    SiteSetting.create(name: 'core_threshold', value_type: 'number', value: '60')
  end
end


text:
# frozen_string_literal: true

class AddAPIKeyToFlagLogs < ActiveRecord::Migration[5.1]
  def change
    add_reference :flag_logs, :api_key, foreign_key: true, type: :integer
  end
end


text:
# frozen_string_literal: true

class IndexFlagLogsOnCreatedAt < ActiveRecord::Migration[5.0]
  def change
    add_index :flag_logs, :created_at
  end
end


text:
# frozen_string_literal: true

class AddSiteSettings < ActiveRecord::Migration[5.2]
  def change
    SiteSetting.create name: 'registration_enabled', value_type: 'boolean', value: 1
    SiteSetting.create name: 'new_account_messages_enabled', value_type: 'boolean', value: 1
    SiteSetting.create name: 'make_trolls_lives_hard', value_type: 'boolean', value: 0
  end
end


text:
# frozen_string_literal: true
class IndexPostsOnStackExchangeUserId < ActiveRecord::Migration[5.2]
  def change
    add_index :posts, :stack_exchange_user_id
  end
end


text:
# frozen_string_literal: true

class AddAPIQuotaToStatistics < ActiveRecord::Migration[5.0]
  def change
    add_column :statistics, :api_quota, :integer
  end
end


text:
# frozen_string_literal: true

class AddWhyToPost < ActiveRecord::Migration[4.2]
  def change
    add_column :posts, :why, :text
  end
end


text:
# frozen_string_literal: true

class AddInvalidatedByToFeedback < ActiveRecord::Migration[5.0]
  def change
    add_column :feedbacks, :invalidated_by, :integer
  end
end


text:
# frozen_string_literal: true

class RetroactiveUsernameRefresh < ActiveRecord::Migration[5.1]
  def up
    User.where(username: ['1', '', nil]).each(&:get_username)
  end

  def down; end
end


text:
class FixRoleName < ActiveRecord::Migration[5.2]
  def change
    Role.where(name: 'code_admin').update(name: 'blacklist_manager')
  end
end


text:
class AddMethodNameToEmailsTypes < ActiveRecord::Migration[5.2]
  def change
    add_column :emails_types, :method_name, :string
  end
end


text:
# frozen_string_literal: true

class AddAdminFlagsReviewQueue < ActiveRecord::Migration[5.2]
  def change
    q = ReviewQueue.create name: 'admin-flags', privileges: 'admin', responses: [%w[Dismiss dismiss]],
                           description: 'Review "needs admin attention" flags from users on metasmoke posts.'
    Flag.where.not(is_completed: true).each do |f|
      ReviewItem.create queue: q, reviewable: f, completed: false
    end
  end
end


text:
# frozen_string_literal: true

class AddUserIdToPosts < ActiveRecord::Migration[4.2]
  def change
    add_column :posts, :stack_exchange_user_id, :integer
  end
end


text:
# frozen_string_literal: true

class AddUuidToAbuseReports < ActiveRecord::Migration[5.2]
  def change
    add_column :abuse_reports, :uuid, :string
  end
end


text:
# frozen_string_literal: true

class CreateStatistics < ActiveRecord::Migration[5.0]
  def change
    create_table :statistics do |t|
      t.integer :posts_scanned
      t.integer :smoke_detector_id

      t.timestamps
    end
  end
end


text:
class ChangeDomainGroupEmailType < ActiveRecord::Migration[5.2]
  def change
    Emails::Type['domain-group-summary']&.update(mailer: 'DomainGroups')
  end
end


text:
# frozen_string_literal: true

# This is what's known as a PSC migration: https://chat.stackexchange.com/transcript/message/41984495#41984495
class ChangePrimaryKeyTypesWtfMySql < ActiveRecord::Migration[5.1]
  def change
    database = Rails.configuration.database_configuration[Rails.env]['database']
    constraints_query = %{
      select table_name, column_name, constraint_name, referenced_table_name, referenced_column_name
      from information_schema.key_column_usage where referenced_table_name is not null and referenced_column_name is not null and
      constraint_schema = '#{database}' and referenced_table_name in ('posts', 'users', 'feedbacks') and referenced_column_name = 'id';
    }
    constraints_result = ActiveRecord::Base.connection.execute constraints_query
    constraints = constraints_result.to_a

    # Drop foreign keys so we can alter the columns they affect
    constraints.each do |c|
      table = c[0]
      name = c[2]
      ActiveRecord::Base.connection.execute "alter table #{table} drop foreign key #{name}"
    end

    # Change primary key types
    change_column :posts, :id, :bigint, auto_increment: true
    change_column :users, :id, :bigint, auto_increment: true
    change_column :feedbacks, :id, :bigint, auto_increment: true

    # Change referencing column types
    constraints.each do |c|
      table, column = c[0...2]
      change_column table.to_sym, column.to_sym, :bigint
    end

    # Restore foreign keys
    constraints.each do |c|
      table, column, name, ref_table, ref_column = c
      query = "alter table #{table} add constraint #{name} foreign key (#{column}) references #{ref_table}(#{ref_column})"
      ActiveRecord::Base.connection.execute query
    end
  end
end


text:
# frozen_string_literal: true

class MigrateToRolesBasedPermissions < ActiveRecord::Migration[5.0]
  def up
    User.all.each do |u|
      u.add_role :reviewer if u.is_approved
      u.add_role :admin if u.is_admin
      u.add_role :code_admin if u.is_code_admin
    end
  end

  def down
    Role.destroy_all
  end
end


text:
class AddEnabledToEmailsPreferences < ActiveRecord::Migration[5.2]
  def change
    add_column :emails_preferences, :enabled, :boolean, null: false, default: 0
  end
end


text:
# frozen_string_literal: true

class UpdatePostFeedbackCaches < ActiveRecord::Migration[5.0]
  def up
    posts = Post.where(id: Feedback.where("feedback_type LIKE '%naa%'").pluck(:post_id))
    posts.includes(:feedbacks).all.each(&:update_feedback_cache)
  end
end


text:
# frozen_string_literal: true

class AddIsTpAndIsFpToPosts < ActiveRecord::Migration[4.2]
  def change
    add_column :posts, :is_tp, :boolean, default: false
    add_column :posts, :is_fp, :boolean, default: false

    tp_posts = Feedback.joins(:post).where('feedback_type LIKE \'%tp%\'').group(Arel.sql('posts.id')).count.map { |k, _v| k }
    fp_posts = Feedback.joins(:post).where('feedback_type LIKE \'%fp%\'').group(Arel.sql('posts.id')).count.map { |k, _v| k }

    Post.where(id: tp_posts).update_all is_tp: true
    Post.where(id: fp_posts).update_all is_fp: true
  end
end


text:
class AddIndexesToPostsSpamDomains < ActiveRecord::Migration[5.2]
  def change
    add_index :posts_spam_domains, :post_id
    add_index :posts_spam_domains, :spam_domain_id
  end
end


text:
# frozen_string_literal: true

class CreateSites < ActiveRecord::Migration[4.2]
  def change
    create_table :sites do |t|
      t.string :site_name
      t.string :site_url
      t.string :site_logo
      t.string :site_domain

      t.timestamps null: false
    end
  end
end


text:
# frozen_string_literal: true

class MoveAllReasonsToSentenceCase < ActiveRecord::Migration[4.2]
  def change
    Reason.all.each do |reason|
      next unless reason.reason_name.downcase == reason.reason_name
      sentence_case_reason = Reason.find_or_create_by(reason_name: reason.reason_name.humanize)

      sentence_case_reason.posts << reason.posts
      sentence_case_reason.save!

      reason.destroy!
    end
  end
end


text:
# frozen_string_literal: true

class AddIsIgnoredToFeedbacks < ActiveRecord::Migration[5.0]
  def change
    add_column :feedbacks, :is_ignored, :boolean, default: false
  end
end


text:
# frozen_string_literal: true

class AddBackoffToFlagLog < ActiveRecord::Migration[5.0]
  def change
    add_column :flag_logs, :backoff, :integer
  end
end


text:
# frozen_string_literal: true

class AddAPIKeyToFeedback < ActiveRecord::Migration[5.0]
  def change
    add_column :feedbacks, :api_key_id, :integer
  end
end


text:
# frozen_string_literal: true

class ExtractDomainNamesFromPosts < ActiveRecord::Migration[5.1]
  def change
    Post.all.each(&:parse_domains)
  end
end


text:
# frozen_string_literal: true

class AddAPITokenToUser < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :api_token, :string
  end
end


text:
# frozen_string_literal: true

class RenameSystemToSmokeDetector < ActiveRecord::Migration[5.2]
  def up
    User.where(id: -1)&.update_all(username: 'SmokeDetector')
  end

  def down
    User.where(id: -1)&.update_all(username: 'System')
  end
end


text:
# frozen_string_literal: true

class ChangeAnnouncementsTextToTextType < ActiveRecord::Migration[5.1]
  def change
    change_column :announcements, :text, :text
  end
end


text:
class MoveConsentFields < ActiveRecord::Migration[5.2]
  def change
    remove_column :emails_addressees, :consent_via
    remove_column :emails_addressees, :consent_comment
    add_column :emails_preferences, :consent_via, :string, null: false
    add_column :emails_preferences, :consent_comment, :text
  end
end


text:
# frozen_string_literal: true

class AddMaximumWeightToReason < ActiveRecord::Migration[5.1]
  def change
    add_column :reasons, :maximum_weight, :tinyint, default: nil
  end
end


text:
# frozen_string_literal: true

class CreateSmokeDetectors < ActiveRecord::Migration[4.2]
  def change
    create_table :smoke_detectors do |t|
      t.datetime :last_ping
      t.string :name
      t.string :location
      t.string :access_token

      t.timestamps null: false
    end
  end
end


text:
class SetPostsMarkdownColToUtf8Mb4 < ActiveRecord::Migration[5.2]
  def change
    execute "ALTER TABLE `posts` MODIFY `markdown` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
  end
end


text:
# frozen_string_literal: true

class CreateGithubTokens < ActiveRecord::Migration[5.0]
  def change
    create_table :github_tokens do |t|
      t.string :token
      t.datetime :expires

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class CreateReviewQueues < ActiveRecord::Migration[5.2]
  def change
    create_table :review_queues do |t|
      t.string :name
      t.string :privileges
      t.text :responses

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class RolifyCreateRoles < ActiveRecord::Migration[4.2]
  def change
    create_table(:roles) do |t|
      t.string :name
      t.references :resource, polymorphic: true

      t.timestamps
    end

    create_table(:users_roles, id: false) do |t|
      t.references :user
      t.references :role
    end

    add_index(:roles, :name)
    add_index(:roles, %i[name resource_type resource_id])
    add_index(:users_roles, %i[user_id role_id])
  end
end


text:
# frozen_string_literal: true

class AddSiteToFlagLogs < ActiveRecord::Migration[5.0]
  def change
    add_reference :flag_logs, :site, foreign_key: true

    FlagLog.all.each do |fl|
      fl.update(site: fl.post.site)
    end
  end
end


text:
# frozen_string_literal: true

class FixReasonsWithParenthesis < ActiveRecord::Migration[4.2]
  def change
    Reason.where("reason_name LIKE '%(%'").each do |reason|
      new_reason = Reason.find_or_create_by(reason_name: reason.reason_name.split('(').first.strip.humanize)

      reason.posts.each do |post|
        post.reasons.delete(reason)
        post.reasons << new_reason
        post.save!
      end
    end
  end
end


text:
# frozen_string_literal: true

class AddIsStandbyToSmokeDetector < ActiveRecord::Migration[5.0]
  def change
    add_column :smoke_detectors, :is_standby, :boolean, default: false
  end
end


text:
# frozen_string_literal: true

class AddIsCodeAdminToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :is_code_admin, :boolean, default: false
  end
end


text:
class AddMarkdownToPosts < ActiveRecord::Migration[5.2]
  def change
    add_column :posts, :markdown, :text, limit: 65535, null: true
  end
end


text:
# frozen_string_literal: true

class AddDeletedAtToPosts < ActiveRecord::Migration[5.0]
  def change
    add_column :posts, :deleted_at, :datetime
  end
end


text:
# frozen_string_literal: true

class CreateInitialSiteSettings < ActiveRecord::Migration[5.2]
  def change
    SiteSetting.create(name: 'require_auth_all_pages', value_type: 'boolean', value: '0')
  end
end


text:
# frozen_string_literal: true

class CreateSpamDomains < ActiveRecord::Migration[5.1]
  def change
    create_table :spam_domains do |t|
      t.string :domain
      t.text :whois

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class IndexFeedbacksOnUserName < ActiveRecord::Migration[5.0]
  def change
    add_index(:feedbacks, :user_name, name: 'by_user_name', length: 5)
  end
end


text:
# frozen_string_literal: true

class CreatePostsSpamDomains < ActiveRecord::Migration[5.1]
  def change
    create_table :posts_spam_domains, id: false do |t|
      t.integer :post_id
      t.integer :spam_domain_id
    end

    execute 'ALTER TABLE posts_spam_domains ADD PRIMARY KEY (post_id, spam_domain_id);'
  end
end


text:
# frozen_string_literal: true

class AddPostToFlags < ActiveRecord::Migration[5.0]
  def change
    add_reference :flags, :post, foreign_key: true
  end
end


text:
class AddSlackChannelsToBlazerChecks < ActiveRecord::Migration[5.2]
  def change
    add_column :blazer_checks, :slack_channels, :text
  end
end


text:
class PopulateFeedbacksToRedis < ActiveRecord::Migration[5.2]
  def change
    # If this line isn't here, travis thinks we died
    ActiveRecord::Base.logger = Logger.new(STDOUT)
    Feedback.populate_redis_meta
  end
end


text:
# frozen_string_literal: true

class AddAnnouncementEmailsToUsers < ActiveRecord::Migration[5.1]
  def change
    add_column :users, :announcement_emails, :boolean
  end
end


text:
# frozen_string_literal: true

class AddGdprFieldsToUser < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :eu_resident, :boolean
    add_column :users, :privacy_accepted, :boolean
  end
end


text:
# frozen_string_literal: true

class AddSiteToPost < ActiveRecord::Migration[4.2]
  def change
    add_column :posts, :site_id, :integer
  end
end


text:
# frozen_string_literal: true

class AddReviewableTypeToReviewQueues < ActiveRecord::Migration[5.2]
  def change
    add_column :review_queues, :reviewable_type, :string

    ReviewQueue['posts'].update(reviewable_type: 'Post')
    ReviewQueue['untagged-domains'].update(reviewable_type: 'SpamDomain')
  end
end


text:
# frozen_string_literal: true

class AddLegacyFeedbackTypesToBlazer < ActiveRecord::Migration[5.2]
  def change
    success = system 'rails runner db/scripts/create_views.rb'
    raise StandardError, 'View script had non-zero exit code' unless success
  end
end


text:
# frozen_string_literal: true

class CreateDomainTagsSpamDomains < ActiveRecord::Migration[5.1]
  def change
    create_table :domain_tags_spam_domains, id: false do |t|
      t.integer :domain_tag_id
      t.integer :spam_domain_id
    end

    execute 'ALTER TABLE domain_tags_spam_domains ADD PRIMARY KEY (domain_tag_id, spam_domain_id);'
  end
end


text:
# frozen_string_literal: true

class GivePeopleWithSmokeDetectorKeysAccessToManagementPage < ActiveRecord::Migration[5.1]
  def up
    SmokeDetector.all.map(&:user).compact.each { |u| u.add_role(:smoke_detector_runner) }
  end

  def down
    user.with_role(:smoke_detector_runner).each { |u| u.remove_role(:smoke_detector_runner) }
  end
end


text:
# frozen_string_literal: true

class AddIsAdminToUsers < ActiveRecord::Migration[4.2]
  def change
    add_column :users, :is_admin, :boolean, null: false, default: false
  end
end


text:
# frozen_string_literal: true

class RemoveSiteIdFromUserSiteSettings < ActiveRecord::Migration[5.1]
  def change
    remove_reference :user_site_settings, :site, foreign_key: true
  end
end


text:
# frozen_string_literal: true

class CreatePullRequests < ActiveRecord::Migration[5.0]
  def change
    create_table :pull_requests do |t|
      t.boolean :has_review
      t.string :last_commit_sha

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class AddFlagPropertiesToSite < ActiveRecord::Migration[5.0]
  def change
    add_column :sites, :flags_enabled, :boolean, default: false
    add_column :sites, :max_flags_per_post, :int, default: 1
  end
end


text:
# frozen_string_literal: true

class RerunMoveAllReasonsToSentenceCase < ActiveRecord::Migration[4.2]
  def change
    Reason.all.each do |reason|
      next unless reason.reason_name.downcase == reason.reason_name
      sentence_case_reason = Reason.find_or_create_by(reason_name: reason.reason_name.humanize)

      sentence_case_reason.posts << reason.posts
      sentence_case_reason.save!

      reason.destroy!
    end
  end
end


text:
# frozen_string_literal: true

class CreateSitesUserSiteSettingsJoinTable < ActiveRecord::Migration[5.0]
  def change
    create_table 'sites_user_site_settings' do |t|
      t.integer :site_id
      t.integer :user_site_setting_id
    end
  end
end


text:
# frozen_string_literal: true

class AddEmailDateToSmokeDetector < ActiveRecord::Migration[4.2]
  def change
    add_column :smoke_detectors, :email_date, :datetime
  end
end


text:
# frozen_string_literal: true

class SetCachedTitles < ActiveRecord::Migration[4.2]
  def change
    Reason.all.each do |reason|
      reason.last_post_title = reason.posts.last.title
      reason.save!
    end
  end
end


text:
# frozen_string_literal: true

class CreateFlagSettings < ActiveRecord::Migration[5.0]
  def change
    create_table :flag_settings do |t|
      t.string :name
      t.string :value

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class RerunPostFeedbackCacheFix < ActiveRecord::Migration[5.0]
  def change
    require "#{Rails.root}/db/migrate/20160402185509_retroactively_fix_post_feedback_cache.rb"
    RetroactivelyFixPostFeedbackCache.new.change
  end
end


text:
# frozen_string_literal: true

class AddIconAndColorToAbuseReportStatuses < ActiveRecord::Migration[5.2]
  def change
    add_column :abuse_report_statuses, :icon, :string
    add_column :abuse_report_statuses, :color, :string
  end
end


text:
class CreateSitesSpamWaves < ActiveRecord::Migration[5.2]
  def change
    create_table :sites_spam_waves, id: false do |t|
      t.bigint :site_id, null: false
      t.bigint :spam_wave_id, null: false
    end
    execute 'ALTER TABLE sites_spam_waves ADD PRIMARY KEY (site_id, spam_wave_id)'
  end
end


text:
# frozen_string_literal: true

class UpdateAllFeedbackCaches < ActiveRecord::Migration[5.2]
  def change
    Post.all.each do |post|
      post.update(link: post.link.scan(%r{.*(\/\/(.*?)\/(questions|a)\/\d+).*})[0]) unless post.valid?
      post.update_feedback_cache
    end
  end
end


text:
# frozen_string_literal: true

class AddRevisionCountToPost < ActiveRecord::Migration[5.0]
  def change
    add_column :posts, :revision_count, :int
  end
end


text:
# frozen_string_literal: true

class AddTwoFactorTokenToUsers < ActiveRecord::Migration[5.1]
  def change
    add_column :users, :two_factor_token, :string
  end
end


text:
# frozen_string_literal: true

class AddFiveFlagExperimentSetting < ActiveRecord::Migration[5.2]
  def change
    FlagSetting.create(
      name: 'five_flag_experiment_threshold',
      value: '10000'
    )
  end
end


text:
# frozen_string_literal: true

class SetupPostsReviewQueue < ActiveRecord::Migration[5.2]
  def change
    ReviewQueue.create name: 'posts', privileges: 'reviewer', responses: [['True Positive', 'tp'], ['False Positive', 'fp'], %w[NAA naa]]
  end
end


text:
# frozen_string_literal: true

class RemoveModeratorSitesFromUser < ActiveRecord::Migration[5.0]
  def change
    remove_column :users, :moderator_sites
  end
end


text:
# frozen_string_literal: true

class AddNativeIdToPosts < ActiveRecord::Migration[5.2]
  def change
    add_column :posts, :native_id, :bigint

    native_ids = Post.where.not(link: nil).map do |p|
      match = %r{\/(?:q(?:uestions)?|a(?:nswers)?)\/(\d+)}.match(p.link)
      if match
        [p.id, match[1]]
      end.compact
    end
    native_id_values = native_ids.map { |n| "(#{n[0]}, #{n[1]})" }.join(', ')
    sql = "INSERT INTO posts (id, native_id) VALUES #{native_id_values} ON DUPLICATE KEY UPDATE native_id = VALUES(native_id);"

    ActiveRecord::Base.connection.execute sql if native_ids.size >= 1
  end
end


text:
class CreateSpamWaves < ActiveRecord::Migration[5.2]
  def change
    create_table :spam_waves do |t|
      t.string :name
      t.text :conditions
      t.references :user, foreign_key: true
      t.datetime :expiry
      t.integer :max_flags

      t.timestamps
    end
  end
end


text:
class RemoveFlagConditionsLogsKey < ActiveRecord::Migration[5.2]
  def change
    remove_foreign_key :flag_logs, :flag_conditions
  end
end


text:
# frozen_string_literal: true

class CreatePostsAndReasons < ActiveRecord::Migration[4.2]
  def change
    create_table :posts do |t|
      t.string :title
      t.text :body
      t.string :link
      t.timestamp :post_creation_date
    end

    create_table :reasons do |t|
      t.string :reason_name
    end

    create_table :posts_reasons, id: false do |t|
      t.belongs_to :reason, index: true
      t.belongs_to :post, index: true
    end
  end
end


text:
# frozen_string_literal: true

class AddAPIParameterToSites < ActiveRecord::Migration[5.2]
  def change
    add_column :sites, :api_parameter, :string
  end
end


text:
class CreateDomainGroupsSpamDomains < ActiveRecord::Migration[5.2]
  def change
    create_table :domain_groups_spam_domains, id: false, primary_key: [:domain_group_id, :spam_domain_id] do |t|
      t.bigint :domain_group_id
      t.bigint :spam_domain_id
    end
  end
end


text:
# frozen_string_literal: true

class ConvertFlagLogErrorMessageToText < ActiveRecord::Migration[5.0]
  def up
    change_column :flag_logs, :error_message, :text
  end

  def down
    change_column :flag_logs, :error_message, :string
  end
end


text:
# frozen_string_literal: true

class AddPropertyToFlagLogs < ActiveRecord::Migration[5.1]
  def change
    add_column :flag_logs, :is_auto, :boolean, default: true
  end
end


text:
# frozen_string_literal: true

class CreateAbuseReports < ActiveRecord::Migration[5.2]
  def change
    create_table :abuse_reports do |t|
      t.references :user, foreign_key: true
      t.references :reportable, polymorphic: true
      t.references :abuse_contact, foreign_key: true, null: true
      t.references :abuse_report_status, foreign_key: true, null: true
      t.text :details

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class InstallAudited < ActiveRecord::Migration[4.2]
  def self.up
    create_table :audits, force: true do |t|
      t.column :auditable_id, :integer
      t.column :auditable_type, :string
      t.column :associated_id, :integer
      t.column :associated_type, :string
      t.column :user_id, :integer
      t.column :user_type, :string
      t.column :username, :string
      t.column :action, :string
      t.column :audited_changes, :text
      t.column :version, :integer, default: 0
      t.column :comment, :string
      t.column :remote_address, :string
      t.column :request_uuid, :string
      t.column :created_at, :datetime
    end

    add_index :audits, %i[auditable_id auditable_type], name: 'auditable_index'
    add_index :audits, %i[associated_id associated_type], name: 'associated_index'
    add_index :audits, %i[user_id user_type], name: 'user_index'
    add_index :audits, :request_uuid
    add_index :audits, :created_at
  end

  def self.down
    drop_table :audits
  end
end


text:
# frozen_string_literal: true

class DropSigninStatsColumns < ActiveRecord::Migration[5.1]
  def change
    remove_column :users, :sign_in_count, :integer
    remove_column :users, :current_sign_in_at, :datetime
    remove_column :users, :last_sign_in_at, :datetime
  end
end


text:
# frozen_string_literal: true

class AddAutoDisputedFlagsEnabledToSites < ActiveRecord::Migration[5.2]
  def change
    add_column :sites, :auto_disputed_flags_enabled, :boolean, default: true
  end
end


text:
class AddWriteAuthenticatedToUser < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :write_authenticated, :boolean, null: false, default: false
    rename_column :users, :encrypted_api_token, :encrypted_api_token_legacy
    reversible do |dir|
      dir.up do
        User.where(flags_enabled:true).each do |u|
          u.update(write_authenticated: true)
        end
      end
    end
  end
end


text:
# frozen_string_literal: true

class MakeUsersRecoverable < ActiveRecord::Migration[4.2]
  def change
    change_table :users do |t|
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at
    end
  end
end


text:
# frozen_string_literal: true

class CreateFlags < ActiveRecord::Migration[5.0]
  def change
    create_table :flags do |t|
      t.string :reason
      t.string :user_id

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class MoveUsersToStackExchangeUser < ActiveRecord::Migration[4.2]
  def change
    Post.where('stack_exchange_user_id IS NULL').each do |post|
      next if post.user_link.nil?

      begin
        user_id = post.user_link.scan(%r{/users/(\d*)/}).first.first
      rescue
        next
      end

      hash = { site_id: post.site_id, user_id: user_id }
      se_user = StackExchangeUser.find_or_create_by(hash)
      se_user.reputation = post.user_reputation
      se_user.username = post.username

      post.stack_exchange_user = se_user

      se_user.save!
      post.save!
    end
  end
end


text:
# frozen_string_literal: true

class AddUserReputationAndScoreToPost < ActiveRecord::Migration[4.2]
  def change
    add_column :posts, :user_reputation, :integer
    add_column :posts, :score, :integer
    add_column :posts, :upvote_count, :integer
    add_column :posts, :downvote_count, :integer
  end
end


text:
# frozen_string_literal: true

class CreateFlagLogs < ActiveRecord::Migration[5.0]
  def change
    create_table :flag_logs do |t|
      t.boolean :success
      t.string :error_message
      t.references :flag_condition, foreign_key: true
      t.references :user, foreign_key: true
      t.references :post, foreign_key: true

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class RemoveLegacyPermissionsAttributes < ActiveRecord::Migration[5.0]
  def change
    remove_column :users, :is_approved
    remove_column :users, :is_admin
    remove_column :users, :is_code_admin
  end
end


text:
# frozen_string_literal: true

class SetSiteOnExistingPosts < ActiveRecord::Migration[4.2]
  def change
    Post.all.each do |post|
      next if post.link.nil?

      post.site = Site.find_by(site_domain: URI.parse(post.link).host)
      post.save!
    end
  end
end


text:
# frozen_string_literal: true

class CreateReviewResults < ActiveRecord::Migration[5.1]
  def change
    create_table :review_results do |t|
      t.references :post, foreign_key: true
      t.references :user, foreign_key: true
      t.references :feedback, foreign_key: true
      t.string :result

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class IndexPostsUidRelation < ActiveRecord::Migration[5.2]
  def change
    add_index :posts, %i[site_id native_id]
    add_index :sites, :api_parameter
  end
end


text:
# frozen_string_literal: true

class AddFlagTypeToFlagLogs < ActiveRecord::Migration[5.2]
  def change
    add_column :flag_logs, :flag_type, :string, default: 'spam'
    add_column :flag_logs, :comment, :text
    add_index :flag_logs, :flag_type
  end
end


text:
# frozen_string_literal: true

class AddGithubLinkToAPIKeys < ActiveRecord::Migration[5.0]
  def change
    add_column :api_keys, :github_link, :string
  end
end


text:
# frozen_string_literal: true

class UpgradeAesCrypt < ActiveRecord::Migration[5.1]
  def change
    users = User.all.where.not(encrypted_api_token: nil)
    users.each do |user|
      decrypted = AESCrypt::Migrator.decrypt_from_v1(user.encrypted_api_token, AppConfig['stack_exchange']['token_aes_key'])
      salt, iv, encrypted = AESCrypt.encrypt(decrypted, AppConfig['stack_exchange']['token_aes_key'])
      user.update(encrypted_api_token: encrypted, salt: salt, iv: iv)
    end
  end
end


text:
# frozen_string_literal: true

class MigrateToCachedDeletionTimes < ActiveRecord::Migration[5.0]
  def change
    sql = <<-SQL
      UPDATE `posts`
      INNER JOIN
        (SELECT `deletion_logs`.`post_id`, min(`deletion_logs`.`created_at`) as min_dl FROM `deletion_logs` WHERE `is_deleted` = true GROUP BY `deletion_logs`.`post_id`) dl on dl.post_id = posts.id
      SET `posts`.`deleted_at` = dl.min_dl
    SQL

    ActiveRecord::Base.connection.execute(sql)
  end
end


text:
class ConvertSpamWavesToUtf8mb4 < ActiveRecord::Migration[5.2]
  def change
      execute 'ALTER TABLE `spam_waves` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin'
  end
end


text:
# frozen_string_literal: true

class IndexPostsOnLink < ActiveRecord::Migration[5.0]
  def change
    add_index :posts, :link
  end
end


text:
# frozen_string_literal: true

class AddForceFailoverToSmokeDetector < ActiveRecord::Migration[5.0]
  def change
    add_column :smoke_detectors, :force_failover, :boolean, default: false
  end
end


text:
# frozen_string_literal: true

class AddSmokeDetectorIdToPost < ActiveRecord::Migration[5.1]
  def change
    add_column :posts, :smoke_detector_id, :integer
  end
end


text:
class AddFulltextIndexToPosts < ActiveRecord::Migration[5.2]
  def change
    # This is going to take FUCKING FOREVER on prod. Prepare for downtime.
    add_index :posts, :body, type: :fulltext
  end
end


text:
# frozen_string_literal: true

class RetroactivelyFixPostFeedbackCache < ActiveRecord::Migration[4.2]
  def change
    Post.update_all(is_tp: false, is_fp: false)

    all_feedbacks = Feedback.all

    tp_posts = all_feedbacks.select(&:is_positive?).map(&:post_id).uniq

    fp_posts = all_feedbacks.select(&:is_negative?).map(&:post_id).uniq

    Post.where(id: tp_posts).update_all is_tp: true
    Post.where(id: fp_posts).update_all is_fp: true
  end
end


text:
# frozen_string_literal: true

class CreateIgnoredUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :ignored_users do |t|
      t.string :user_name
      t.references :user, foreign_key: true

      t.timestamps
    end
  end
end


text:
class CreateEmailsPreferences < ActiveRecord::Migration[5.2]
  def change
    create_table :emails_preferences do |t|
      t.references :emails_type, foreign_key: true
      t.references :emails_addressee, foreign_key: true
      t.datetime :next
      t.integer :frequency

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class RemoveAPITokenFromUsers < ActiveRecord::Migration[5.1]
  def change
    remove_column :users, :api_token
  end
end


text:
# frozen_string_literal: true

class AddTimestampsToPosts < ActiveRecord::Migration[4.2]
  def change
    change_table(:posts, &:timestamps)
  end
end


text:
# frozen_string_literal: true

class CreateUserSiteSettings < ActiveRecord::Migration[5.0]
  def change
    create_table :user_site_settings do |t|
      t.integer :max_flags
      t.integer :flags_used
      t.references :user, foreign_key: true
      t.references :site, foreign_key: true

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class AddAutoFlaggerSiteSetting < ActiveRecord::Migration[5.2]
  def change
    SiteSetting.create name: 'auto_flagger_role', value_type: 'boolean', value: '1'
  end
end


text:
# frozen_string_literal: true

class AddInvalidatedAtToFeedbacks < ActiveRecord::Migration[5.0]
  def change
    add_column :feedbacks, :invalidated_at, :datetime
  end
end


text:
# frozen_string_literal: true

class AddIsTrustedToAPIKeys < ActiveRecord::Migration[5.1]
  def change
    add_column :api_keys, :is_trusted, :boolean
  end
end


text:
# frozen_string_literal: true

class AddUsernameToUser < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :username, :string
  end
end


text:
# frozen_string_literal: true

class AddClosedStateToSites < ActiveRecord::Migration[5.2]
  def change
    add_column :sites, :closed, :boolean, default: false, null: false
  end
end


text:
# frozen_string_literal: true

class AddCoreTimePeriodSiteSetting < ActiveRecord::Migration[5.2]
  def change
    SiteSetting.create(name: 'core_time_period', value_type: 'number', value: '30')
  end
end


text:
# frozen_string_literal: true

class BackfillFeedbackUsernames < ActiveRecord::Migration[5.1]
  class Feedback < ApplicationRecord
  end
  class User < ApplicationRecord
  end

  def change
    users = User.all.map { |u| [u.id, u.username] }.to_h
    Feedback.where(user_name: nil).where.not(user_id: nil).each do |f|
      f.update(user_name: users[f.user_id])
    end
  end
end


text:
class RemoveForeignKeyConstraintForFlagLogs < ActiveRecord::Migration[5.2]
  def change
    remove_foreign_key :flag_logs, name: :fk_rails_d6d60057f2
  end
end


text:
# frozen_string_literal: true

class CreateAbuseReportStatuses < ActiveRecord::Migration[5.2]
  def change
    create_table :abuse_report_statuses do |t|
      t.string :name
      t.text :details
      t.integer :ordering

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class AddIndexOnCreationDateToPosts < ActiveRecord::Migration[5.0]
  def change
    add_index :posts, :created_at
  end
end


text:
# frozen_string_literal: true

class CancelOldUntaggedReviews < ActiveRecord::Migration[5.2]
  def change
    # We're not getting them done and it's negatively affecting review speed in other queues, sooo... yup.
    ReviewQueue['untagged-domains']&.items&.update_all(completed: true)
  end
end


text:
# frozen_string_literal: true

class AddUntaggedSpamDomainsReviewQueue < ActiveRecord::Migration[5.2]
  def change
    q = ReviewQueue.create name: 'untagged-domains', privileges: 'core', responses: [["I'm Done", 'done']],
                           description: 'Review and add tags to domains that have been seen a few times.'

    SpamDomain.joins(:posts).left_joins(:domain_tags).group(Arel.sql('spam_domains.id')).having(Arel.sql('COUNT(DISTINCT posts.id) >= 3'))
              .where(domain_tags: { id: nil }).each do |d|
      ReviewItem.create(reviewable: d, queue: q, completed: false)
    end
  end
end


text:
class AddDescriptionToReasons < ActiveRecord::Migration[5.2]
  def change
    add_column :reasons, :description, :text
  end
end


text:
# frozen_string_literal: true

class RetroactivelySetSiteIdOnFlagLog < ActiveRecord::Migration[5.0]
  def change
    FlagLog.joins(:post).update_all('flag_logs.site_id = posts.site_id')
  end
end


text:
# frozen_string_literal: true

class AddCiurlToCommitStatus < ActiveRecord::Migration[5.0]
  def change
    add_column :commit_statuses, :ci_url, :string
  end
end


text:
class AddTimestampsToDomainGroupsSpamDomains < ActiveRecord::Migration[5.2]
  def change
    change_table :domain_groups_spam_domains do |t|
      t.timestamps default: -> { 'current_timestamp' }
    end
  end
end


text:
# frozen_string_literal: true

class AddCreatedAtIndexToFeedbacks < ActiveRecord::Migration[5.2]
  def change
    add_index :feedbacks, :created_at
  end
end


text:
# frozen_string_literal: true

class AddUuidToExistingAbuseReports < ActiveRecord::Migration[5.2]
  def change
    AbuseReport.all.each do |ar|
      ar.update uuid: SecureRandom.uuid
    end
  end
end


text:
# frozen_string_literal: true

class AddChatUserIdToFeedbacks < ActiveRecord::Migration[5.0]
  def change
    add_column :feedbacks, :chat_user_id, :integer
  end
end


text:
# frozen_string_literal: true

class AddIsNaaToPosts < ActiveRecord::Migration[5.0]
  def change
    add_column :posts, :is_naa, :boolean, default: false
  end
end


text:
# frozen_string_literal: true

class CreateDomainTags < ActiveRecord::Migration[5.1]
  def change
    create_table :domain_tags do |t|
      t.string :name
      t.text :description

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class SetDefaultValueOnFlagConditionEnabled < ActiveRecord::Migration[5.0]
  def up
    change_column :flag_conditions, :flags_enabled, :boolean, default: true

    FlagCondition.where(flags_enabled: nil).update_all(flags_enabled: true)
  end

  def down
    change_column :flag_conditions, :flags_enabled, :boolean, default: true
  end
end


text:
class CreateDomainGroupSummaryEmailType < ActiveRecord::Migration[5.2]
  def change
    Emails::Type.create(
      name: 'Domain group summaries',
      short_name: 'domain-group-summary',
      description: 'Round-up of spam that\'s been seen under a particular domain or group of domains.',
      mailer: 'DomainGroup',
      method_name: 'domain_group_summary'
    )
  end
end


text:
# frozen_string_literal: true

class AddInactiveToReasons < ActiveRecord::Migration[5.0]
  def change
    add_column :reasons, :inactive, :boolean, default: false
  end
end


text:
# frozen_string_literal: true

class CreateDomainLinks < ActiveRecord::Migration[5.2]
  def change
    create_table :domain_links do |t|
      t.bigint :left_id
      t.bigint :right_id
      t.string :link_type
      t.text :comments
      t.bigint :creator_id

      t.timestamps
    end

    add_foreign_key :domain_links, :spam_domains, column: :left_id
    add_foreign_key :domain_links, :spam_domains, column: :right_id
    add_foreign_key :domain_links, :users, column: :creator_id
    add_index :domain_links, :left_id
    add_index :domain_links, :right_id
    add_index :domain_links, :creator_id
  end
end


text:
# frozen_string_literal: true

class RemoveReadOnlyTokensFromUsers < ActiveRecord::Migration[5.0]
  def change
    users_to_clear = []

    User.pluck(:api_token).select(&:present?).each_slice(20) do |tokens|
      url = "https://api.stackexchange.com/2.2/access-tokens/#{tokens.join(';')}?key=#{AppConfig['stack_exchange']['key']}"
      items = JSON.parse(Net::HTTP.get_response(URI.parse(url)).body)['items']
      items.each do |i|
        unless i.include?('scope') && i['scope'].include?('write_access')
          users_to_clear << i['account_id']
        end
      end
    end

    User.where(stack_exchange_account_id: users_to_clear).update_all(encrypted_api_token: nil)
  end
end


text:
# frozen_string_literal: true

class CreateQueryAverages < ActiveRecord::Migration[5.2]
  def change
    create_table :query_averages do |t|
      t.string :path, null: false
      t.bigint :counter, default: 0, null: false
      t.decimal :average, default: 0, null: false, precision: 14, scale: 3
    end
  end
end


text:
class CreateEmailsAddressees < ActiveRecord::Migration[5.2]
  def change
    create_table :emails_addressees do |t|
      t.string :name
      t.string :email_address
      t.string :consent_via
      t.text :consent_comment
      t.string :manage_key

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class AddFeedbacksCountToPosts < ActiveRecord::Migration[5.2]
  def change
    add_column :posts, :feedbacks_count, :integer
    add_index :posts, :feedbacks_count
  end
end


text:
# frozen_string_literal: true

class AddUserIdToFeedbacks < ActiveRecord::Migration[4.2]
  def change
    add_column :feedbacks, :user_id, :integer
  end
end


text:
# frozen_string_literal: true

class AddIdToUsersRoles < ActiveRecord::Migration[5.2]
  def change
    add_column :users_roles, :id, :primary_key
  end
end


text:
# frozen_string_literal: true

class AddUserLinkToPosts < ActiveRecord::Migration[4.2]
  def change
    add_column :posts, :user_link, :string
  end
end


text:
# frozen_string_literal: true

class CreateModeratorSites < ActiveRecord::Migration[5.0]
  def change
    create_table :moderator_sites do |t|
      t.integer :user_id
      t.integer :site_id

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class AddSiteIdToStackExchangeUsers < ActiveRecord::Migration[4.2]
  def change
    add_column :stack_exchange_users, :site_id, :integer
  end
end


text:
# frozen_string_literal: true

class RemoveIgnoredUsers < ActiveRecord::Migration[5.1]
  def change
    remove_column :feedbacks, :is_ignored
    drop_table :ignored_users
  end
end


text:
# frozen_string_literal: true

class RemoveSpacesFromExistingReasons < ActiveRecord::Migration[4.2]
  def change
    Reason.all.each do |reason|
      reason.reason_name = reason.reason_name.strip
      reason.save!
    end
  end
end


text:
# frozen_string_literal: true

class CreateDomainTagsPosts < ActiveRecord::Migration[5.2]
  def change
    create_table :domain_tags_posts, id: false do |t|
      t.bigint :domain_tag_id, null: false
      t.bigint :post_id, null: false
    end

    execute 'ALTER TABLE domain_tags_posts ADD PRIMARY KEY (domain_tag_id, post_id);'
  end
end


text:
# frozen_string_literal: true

class CreateDeletionLogs < ActiveRecord::Migration[5.0]
  def change
    create_table :deletion_logs do |t|
      t.integer :post_id
      t.boolean :is_deleted

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class AddChatHostToFeedback < ActiveRecord::Migration[5.0]
  def change
    add_column :feedbacks, :chat_host, :string
  end
end


text:
# frozen_string_literal: true

class UpdatePostFeedbackCounts < ActiveRecord::Migration[5.2]
  def change
    counts = Post.left_joins(:feedbacks).group(Arel.sql('posts.id')).count
    count_values = counts.map { |k, v| "(#{k}, #{v})" }.join(', ')
    sql = "INSERT INTO posts (id, feedbacks_count) VALUES #{count_values} ON DUPLICATE KEY UPDATE feedbacks_count = VALUES(feedbacks_count);"
    ActiveRecord::Base.connection.execute sql
  end
end


text:
# frozen_string_literal: true

class RunCreateViewsForReview < ActiveRecord::Migration[5.2]
  def change
    success = system 'rails runner db/scripts/create_views.rb'
    raise StandardError, 'View script had non-zero exit code' unless success
  end
end


text:
# frozen_string_literal: true

class RemoveDetailsFromAbuseReportStatuses < ActiveRecord::Migration[5.2]
  def change
    remove_column :abuse_report_statuses, :details, :text
  end
end


text:
class FixOldLostLogs < ActiveRecord::Migration[5.2]
  def change
    redis(logger: true).rename "requests/status/", "requests/status/INC" if redis(logger: true).exists "requests/status/"
  end
end


text:
# frozen_string_literal: true

class ExtractDomainNamesFromPostsAndTitles < ActiveRecord::Migration[5.1]
  def change
    Post.all.each(&:parse_domains)
  end
end


text:
# frozen_string_literal: true

class AddIndexToPostsAutoflagged < ActiveRecord::Migration[5.2]
  def change
    add_index :posts, :autoflagged
  end
end


text:
# frozen_string_literal: true

class AddUniqueConstraintToUsername < ActiveRecord::Migration[5.0]
  def change
    add_index :users, [:username], unique: true
  end
end


text:
class AddForcePullToSmokeDetectors < ActiveRecord::Migration[5.2]
  def change
    add_column :smoke_detectors, :force_pull, :boolean, default: false
  end
end


text:
# frozen_string_literal: true

class CreateChannelsUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :channels_users do |t|
      t.references :user, foreign_key: true
      t.string :secret
      t.string :link

      t.timestamps
    end
  end
end


text:
# frozen_string_literal: true

class AddNeedsAdminAndReasonToPosts < ActiveRecord::Migration[5.0]
  def change
    add_column :posts, :needs_admin, :boolean, default: false
    add_column :posts, :admin_reason, :string
  end
end


text:
# frozen_string_literal: true

class AddExpiryToAPITokens < ActiveRecord::Migration[5.0]
  def change
    add_column :api_tokens, :expiry, :timestamp
  end
end


text:
# frozen_string_literal: true

class RunCreateViewsAgain < ActiveRecord::Migration[5.2]
  def change
    success = system 'rails runner db/scripts/create_views.rb'
    raise StandardError, 'View script had non-zero exit code' unless success
  end
end


text:
# frozen_string_literal: true

class AddNumberToPullRequests < ActiveRecord::Migration[5.0]
  def change
    add_column :pull_requests, :number, :integer
  end
end


text:
# frozen_string_literal: true

class RemoveUsernameUniqueIndex < ActiveRecord::Migration[5.1]
  def change
    remove_index :users, :username
  end
end


text:
# frozen_string_literal: true

class CreateAPIKeys < ActiveRecord::Migration[5.0]
  def change
    create_table :api_keys, &:timestamps
  end
end


text:
# frozen_string_literal: true

class FixInvalidFeedbacks < ActiveRecord::Migration[5.2]
  def change
    add_column :feedbacks, :legacy_feedback_type, :string

    reversible do |dir|
      dir.up do
        Feedback.where.not(feedback_type: Feedback::VALID_TYPES).each do |feedback|
          original_feedback = feedback.feedback_type
          normalized_feedback = case feedback.feedback_type.downcase
                                when /true[a-z][-=].*/ then 'tpu-'
                                when /true[a-z].*/ then 'tpu'
                                when /true[-=].*/ then 'tp-'
                                when /true.*/ then 'tp'
                                when /false[a-z][-=].*/ then 'fpu-'
                                when /false[a-z].*/ then 'fpu'
                                when /false[-=].*/ then 'fp-'
                                when /false.*/ then 'fp'
                                when /tp[a-z][-=].*/ then 'tpu-'
                                when /tp[a-z].*/ then 'tpu'
                                when /fp[a-z][-=].*/ then 'tpu-'
                                when /fp[a-z].*/ then 'tpu'
                                when /naa[-=].*/ then 'naa-'
                                when /naa.*/ then 'naa'
                                when /n/ then 'naa-'
                                when /f/ then 'fp-'
                                when /k/ then 'tp-'
                                else; 'invalid'
                                end
          puts "#{original_feedback}\t#{normalized_feedback}"
          # Skip validation
          feedback.update_columns(feedback_type: normalized_feedback, legacy_feedback_type: original_feedback)
          puts "Fixed #{Feedback.where.not(legacy_feedback_type: nil).count} feedbacks"
        end
      end
      dir.down do
        Feedback.where.not(legacy_feedback_type: nil).each do |feedback|
          feedback.update(feedback_type: feedback.legacy_feedbacks, legacy_feedback_type: nil)
        end
      end
    end
  end
end


text:
# frozen_string_literal: true

class CreateFeedback < ActiveRecord::Migration[4.2]
  def change
    create_table :feedbacks do |t|
      t.string :message_link
      t.string :user_name
      t.string :user_link
      t.string :feedback_type
      t.belongs_to :post, index: true
    end
  end
end