<commit_before># frozen_string_literal: true

class Post < ApplicationRecord
  include Websocket

  validate :reject_recent_duplicates
  validates_format_of :link, with: %r{\A\/\/(.*?)\/(questions|a)\/(\d+)\Z}

  serialize :tags, JSON

  has_and_belongs_to_many :reasons
  has_many :feedbacks, dependent: :destroy
  has_many :deletion_logs, dependent: :destroy
  belongs_to :site
  belongs_to :stack_exchange_user
  belongs_to :smoke_detector
  has_many :flag_logs, dependent: :destroy
  has_many :flags, dependent: :destroy
  has_and_belongs_to_many :spam_domains
  has_many :reviews, class_name: 'ReviewResult'

  scope(:includes_for_post_row, -> { includes(:stack_exchange_user).includes(:reasons).includes(feedbacks: [:user, :api_key]) })
  scope(:without_feedback, -> { left_joins(:feedbacks).where(feedbacks: { post_id: nil }) })

  scope(:autoflagged, -> { where(autoflagged: true) })

  scope(:not_autoflagged, -> { where(autoflagged: false) })

  scope(:today, -> { where('created_at > ?', Date.today) })

  scope(:tp, -> { where(is_tp: true) })
  scope(:fp, -> { where(is_fp: true) })
  scope(:naa, -> { where(is_naa: true) })

  # WARNING: This is *very* slow without a limit - use Post.undeleted.limit(n) where possible.
  scope(:undeleted, -> { left_joins(:deletion_logs).where(deletion_logs: { id: nil }) })

  after_commit :parse_domains, on: :create

  after_create do
    ActionCable.server.broadcast 'posts_realtime', row: PostsController.render(locals: { post: Post.last }, partial: 'post').html_safe
    ActionCable.server.broadcast 'topbar', review: Post.without_feedback.count
  end

  after_commit on: :create do
    Rails.logger.warn "[autoflagging] #{id}: after_create begin"

    post = self
    Thread.new do
      Rails.logger.warn "[autoflagging] #{post.id}: thread begin"
      # Trying to autoflag in a different thread while in test
      # can cause race conditions and segfaults. This is bad,
      # so we completely suppress the issue and just don't do that.
      post.autoflag unless Rails.env.test?
    end
  end

  def autoflag
    Rails.logger.warn "[autoflagging] #{id}: Post#autoflag begin"
    return 'Duplicate post' unless Post.where(link: link).count == 1
    return 'Flagging disabled' unless FlagSetting['flagging_enabled'] == '1'
    Rails.logger.warn "[autoflagging] #{id}: not a dupe"

    dry_run = FlagSetting['dry_run'] == '1'
    post = self

    begin
      conditions = post.site.flag_conditions.where(flags_enabled: true)
      available_user_ids = {}
      conditions.each do |condition|
        if condition.validate!(post)
          available_user_ids[condition.user.id] = condition
        end
      end
      Rails.logger.warn "[autoflagging] #{id}: fetched conditions"

      uids = post.site.user_site_settings.where(user_id: available_user_ids.keys).map(&:user_id)
      users = User.where(id: uids, flags_enabled: true).where.not(encrypted_api_token: nil)
      if users.blank?
        Rails.logger.warn "[autoflagging] #{id}: no users available"
        post.send_not_autoflagged
        return 'No users eligible to flag'
      end

      Rails.logger.warn "[autoflagging] #{id}: before revision count"
      post.fetch_revision_count
      unless post.revision_count == 1
        Rails.logger.warn "[autoflagging] #{id}: vandalized"
        post.send_not_autoflagged
        return 'More than one revision'
      end

      Rails.logger.warn "[autoflagging] #{id}: lottery begin"
      max_flags = [post.site.max_flags_per_post, (FlagSetting['max_flags'] || '3').to_i].min

      if post.reasons.pluck(:weight).reduce(:+) >= (FlagSetting['five_flag_experiment_threshold'] || 100_000).to_i && post.site.max_flags_per_post == 3
        Rails.logger.warn "[autoflagging] #{id}: Five flag timing experiment began"
        max_flags = rand(1..5)
        Rails.logger.warn "[autoflagging] #{id}: Five flag timing experiment chose flag count: #{max_flags}"<commit_msg>I can play this game too<commit_after># frozen_string_literal: true

class Post < ApplicationRecord
  include Websocket

  validate :reject_recent_duplicates
  validates_format_of :link, with: %r{\A\/\/(.*?)\/(questions|a)\/(\d+)\Z}

  serialize :tags, JSON

  has_and_belongs_to_many :reasons
  has_many :feedbacks, dependent: :destroy
  has_many :deletion_logs, dependent: :destroy
  belongs_to :site
  belongs_to :stack_exchange_user
  belongs_to :smoke_detector
  has_many :flag_logs, dependent: :destroy
  has_many :flags, dependent: :destroy
  has_and_belongs_to_many :spam_domains
  has_many :reviews, class_name: 'ReviewResult'

  scope(:includes_for_post_row, -> { includes(:stack_exchange_user).includes(:reasons).includes(feedbacks: [:user, :api_key]) })
  scope(:without_feedback, -> { left_joins(:feedbacks).where(feedbacks: { post_id: nil }) })

  scope(:autoflagged, -> { where(autoflagged: true) })

  scope(:not_autoflagged, -> { where(autoflagged: false) })

  scope(:today, -> { where('created_at > ?', Date.today) })

  scope(:tp, -> { where(is_tp: true) })
  scope(:fp, -> { where(is_fp: true) })
  scope(:naa, -> { where(is_naa: true) })

  # WARNING: This is *very* slow without a limit - use Post.undeleted.limit(n) where possible.
  scope(:undeleted, -> { left_joins(:deletion_logs).where(deletion_logs: { id: nil }) })

  after_commit :parse_domains, on: :create

  after_create do
    ActionCable.server.broadcast 'posts_realtime', row: PostsController.render(locals: { post: Post.last }, partial: 'post').html_safe
    ActionCable.server.broadcast 'topbar', review: Post.without_feedback.count
  end

  after_commit on: :create do
    Rails.logger.warn "[autoflagging] #{id}: after_create begin"

    post = self
    Thread.new do
      Rails.logger.warn "[autoflagging] #{post.id}: thread begin"
      # Trying to autoflag in a different thread while in test
      # can cause race conditions and segfaults. This is bad,
      # so we completely suppress the issue and just don't do that.
      post.autoflag unless Rails.env.test?
    end
  end

  def autoflag
    Rails.logger.warn "[autoflagging] #{id}: Post#autoflag begin"
    return 'Duplicate post' unless Post.where(link: link).count == 1
    return 'Flagging disabled' unless FlagSetting['flagging_enabled'] == '1'
    Rails.logger.warn "[autoflagging] #{id}: not a dupe"

    dry_run = FlagSetting['dry_run'] == '1'
    post = self

    begin
      conditions = post.site.flag_conditions.where(flags_enabled: true)
      available_user_ids = {}
      conditions.each do |condition|
        if condition.validate!(post)
          available_user_ids[condition.user.id] = condition
        end
      end
      Rails.logger.warn "[autoflagging] #{id}: fetched conditions"

      uids = post.site.user_site_settings.where(user_id: available_user_ids.keys).map(&:user_id)
      users = User.where(id: uids, flags_enabled: true).where.not(encrypted_api_token: nil)
      if users.blank?
        Rails.logger.warn "[autoflagging] #{id}: no users available"
        post.send_not_autoflagged
        return 'No users eligible to flag'
      end

      Rails.logger.warn "[autoflagging] #{id}: before revision count"
      post.fetch_revision_count
      unless post.revision_count == 1
        Rails.logger.warn "[autoflagging] #{id}: vandalized"
        post.send_not_autoflagged
        return 'More than one revision'
      end

      Rails.logger.warn "[autoflagging] #{id}: lottery begin"
      max_flags = [post.site.max_flags_per_post, (FlagSetting['max_flags'] || '3').to_i].min

      if post.reasons.pluck(:weight).reduce(:+) >= (FlagSetting['five_flag_experiment_threshold'] || 10_000).to_i && post.site.max_flags_per_post == 3
        Rails.logger.warn "[autoflagging] #{id}: Five flag timing experiment began"
        max_flags = rand(1..5)
        Rails.logger.warn "[autoflagging] #{id}: Five flag timing experiment chose flag count: #{max_flags}"
{'fraction_non_alphanumeric': 0.12801625603251207, 'fraction_numerical': 0.003429006858013716, 'mean_word_length': 3.831288343558282, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3554314', 'n_tokens_mistral': 2653, 'n_tokens_neox': 2559, 'n_words': 590}