<commit_before>require 'state_machine'
# coding: utf-8
class Backer < ActiveRecord::Base
  schema_associations

  delegate :display_value, :display_confirmed_at, to: :decorator

  validates_presence_of :project, :user, :value
  validates_numericality_of :value, greater_than_or_equal_to: 10.00
  validate :reward_must_be_from_project
  validate :value_must_be_at_least_rewards_value
  validate :should_not_back_if_maximum_backers_been_reached, on: :create
  validate :project_should_be_online, on: :create

  scope :available_to_count, ->{ with_states(['confirmed', 'requested_refund', 'refunded']) }
  scope :available_to_display, ->{ with_states(['confirmed', 'requested_refund', 'refunded', 'waiting_confirmation']) }
  scope :by_id, ->(id) { where(id: id) }
  scope :by_key, ->(key) { where(key: key) }
  scope :by_user_id, ->(user_id) { where(user_id: user_id) }
  scope :user_name_contains, ->(term) { joins(:user).where("unaccent(upper(users.name)) LIKE ('%'||unaccent(upper(?))||'%')", term) }
  scope :user_email_contains, ->(term) { joins(:user).where("unaccent(upper(users.email)) LIKE ('%'||unaccent(upper(?))||'%')", term) }
  scope :payer_email_contains, ->(term) { where("unaccent(upper(payer_email)) LIKE ('%'||unaccent(upper(?))||'%')", term) }
  scope :project_name_contains, ->(term) { joins(:project).where("unaccent(upper(projects.name)) LIKE ('%'||unaccent(upper(?))||'%')", term) }
  scope :anonymous, -> { where(anonymous: true) }
  scope :credits, -> { where(credits: true) }
  scope :not_anonymous, -> { where(anonymous: false) }

  scope :can_cancel, -> {
    where(%Q{
      backers.state = 'waiting_confirmation' and
        (
          ((
            select count(1) as total_of_days
            from generate_series(created_at::date, current_date, '1 day') day
            WHERE extract(dow from day) not in (0,1)
          )  > 4)
          OR
          (
            payment_choice = 'DebitoBancario'
            AND
              (
                select count(1) as total_of_days
                from generate_series(created_at::date, current_date, '1 day') day
                WHERE extract(dow from day) not in (0,1)
              )  > 1)
        )
    })
  }

  # Backers already refunded or with requested_refund should appear so that the user can see their status on the refunds list
  scope :can_refund, ->{
    where(%Q{
      backers.state IN('confirmed', 'requested_refund', 'refunded') AND
      NOT backers.credits AND
      EXISTS(
        SELECT true
          FROM projects p
          WHERE p.id = backers.project_id and p.state = 'failed'
      )
    })
  }

  # TODO:
  #attr_protected :confirmed, :state

  def self.between_values(start_at, ends_at)
    return scoped unless start_at.present? && ends_at.present?
    where("value between ? and ?", start_at, ends_at)
  end

  def self.state_names
    self.state_machine.states.map do |state|
      state.name if state.name != :deleted
    end.compact!
  end

  def decorator
    @decorator ||= BackerDecorator.new(self)
  end

  def recommended_projects
    user.recommended_projects.where("projects.id <> ?", project.id)
  end

  def refund_deadline
    created_at + 180.days
  end

  def change_reward! reward
    self.reward_id = reward
    self.save
  end

  def can_refund?
    confirmed? && project.finished? && !project.successful?
  end

  def reward_must_be_from_project
    return unless reward
    errors.add(:reward, I18n.t('backer.reward_must_be_from_project')) unless reward.project == project
  end

  def value_must_be_at_least_rewards_value
    return unless reward
    errors.add(:value, I18n.t('backer.value_must_be_at_least_rewards_value', minimum_value: reward.display_minimum)) unless value.to_f >= reward.minimum_value
  end

  def should_not_back_if_maximum_backers_been_reached
    return unless reward && reward.maximum_backers && reward.maximum_backers > 0<commit_msg>fix recommendations after back was created, we need the explicit order in this SQL.<commit_after>require 'state_machine'
# coding: utf-8
class Backer < ActiveRecord::Base
  schema_associations

  delegate :display_value, :display_confirmed_at, to: :decorator

  validates_presence_of :project, :user, :value
  validates_numericality_of :value, greater_than_or_equal_to: 10.00
  validate :reward_must_be_from_project
  validate :value_must_be_at_least_rewards_value
  validate :should_not_back_if_maximum_backers_been_reached, on: :create
  validate :project_should_be_online, on: :create

  scope :available_to_count, ->{ with_states(['confirmed', 'requested_refund', 'refunded']) }
  scope :available_to_display, ->{ with_states(['confirmed', 'requested_refund', 'refunded', 'waiting_confirmation']) }
  scope :by_id, ->(id) { where(id: id) }
  scope :by_key, ->(key) { where(key: key) }
  scope :by_user_id, ->(user_id) { where(user_id: user_id) }
  scope :user_name_contains, ->(term) { joins(:user).where("unaccent(upper(users.name)) LIKE ('%'||unaccent(upper(?))||'%')", term) }
  scope :user_email_contains, ->(term) { joins(:user).where("unaccent(upper(users.email)) LIKE ('%'||unaccent(upper(?))||'%')", term) }
  scope :payer_email_contains, ->(term) { where("unaccent(upper(payer_email)) LIKE ('%'||unaccent(upper(?))||'%')", term) }
  scope :project_name_contains, ->(term) { joins(:project).where("unaccent(upper(projects.name)) LIKE ('%'||unaccent(upper(?))||'%')", term) }
  scope :anonymous, -> { where(anonymous: true) }
  scope :credits, -> { where(credits: true) }
  scope :not_anonymous, -> { where(anonymous: false) }

  scope :can_cancel, -> {
    where(%Q{
      backers.state = 'waiting_confirmation' and
        (
          ((
            select count(1) as total_of_days
            from generate_series(created_at::date, current_date, '1 day') day
            WHERE extract(dow from day) not in (0,1)
          )  > 4)
          OR
          (
            payment_choice = 'DebitoBancario'
            AND
              (
                select count(1) as total_of_days
                from generate_series(created_at::date, current_date, '1 day') day
                WHERE extract(dow from day) not in (0,1)
              )  > 1)
        )
    })
  }

  # Backers already refunded or with requested_refund should appear so that the user can see their status on the refunds list
  scope :can_refund, ->{
    where(%Q{
      backers.state IN('confirmed', 'requested_refund', 'refunded') AND
      NOT backers.credits AND
      EXISTS(
        SELECT true
          FROM projects p
          WHERE p.id = backers.project_id and p.state = 'failed'
      )
    })
  }

  # TODO:
  #attr_protected :confirmed, :state

  def self.between_values(start_at, ends_at)
    return scoped unless start_at.present? && ends_at.present?
    where("value between ? and ?", start_at, ends_at)
  end

  def self.state_names
    self.state_machine.states.map do |state|
      state.name if state.name != :deleted
    end.compact!
  end

  def decorator
    @decorator ||= BackerDecorator.new(self)
  end

  def recommended_projects
    user.recommended_projects.where("projects.id <> ?", project.id).order("count DESC")
  end

  def refund_deadline
    created_at + 180.days
  end

  def change_reward! reward
    self.reward_id = reward
    self.save
  end

  def can_refund?
    confirmed? && project.finished? && !project.successful?
  end

  def reward_must_be_from_project
    return unless reward
    errors.add(:reward, I18n.t('backer.reward_must_be_from_project')) unless reward.project == project
  end

  def value_must_be_at_least_rewards_value
    return unless reward
    errors.add(:value, I18n.t('backer.value_must_be_at_least_rewards_value', minimum_value: reward.display_minimum)) unless value.to_f >= reward.minimum_value
  end

  def should_not_back_if_maximum_backers_been_reached
    return unless reward && reward.maximum_backers && reward.maximum_backers > 0
{'fraction_non_alphanumeric': 0.12541212274917576, 'fraction_numerical': 0.005833121988333756, 'mean_word_length': 3.614979520187244, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 41, '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': '2088616', 'n_tokens_mistral': 2737, 'n_tokens_neox': 2614, 'n_words': 567}