Class: CatalogItemRetailerProbe

Inherits:
ApplicationRecord show all
Includes:
Turbo::Broadcastable
Defined in:
app/models/catalog_item_retailer_probe.rb

Overview

== Schema Information

Table name: catalog_item_retailer_probes
Database name: primary

id :bigint not null, primary key
content_size_bytes :integer
currency :string(3)
error_message :string
geo_location :string(10)
page_accessible :boolean default(FALSE)
price :decimal(10, 2)
product_available :boolean
response_time_ms :integer
status :string default("pending"), not null
store_json :jsonb
url :string
created_at :datetime not null
updated_at :datetime not null
catalog_item_id :bigint not null

Indexes

idx_retailer_probes_item_created (catalog_item_id,created_at)
index_catalog_item_retailer_probes_on_created_at (created_at)
index_catalog_item_retailer_probes_on_status (status)

Foreign Keys

fk_rails_... (catalog_item_id => catalog_items.id)

Constant Summary collapse

STATUSES =

Status values

{
  pending: 'pending',                 # Probe queued but not yet performed
  success: 'success',                 # Page loaded and data extracted
  failed: 'failed',                   # Request failed (timeout, error)
  not_found: 'not_found',             # Page returned 404 or product not on page
  product_mismatch: 'product_mismatch' # Page loaded but product identifiers not found
}.freeze

Instance Attribute Summary collapse

Belongs to collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#currencyObject (readonly)



58
# File 'app/models/catalog_item_retailer_probe.rb', line 58

validates :currency, length: { maximum: 3 }

#geo_locationObject (readonly)



57
# File 'app/models/catalog_item_retailer_probe.rb', line 57

validates :geo_location, length: { maximum: 10 }

#statusObject (readonly)



56
# File 'app/models/catalog_item_retailer_probe.rb', line 56

validates :status, presence: true, inclusion: { in: STATUSES.values }

Class Method Details

.failedActiveRecord::Relation<CatalogItemRetailerProbe>

A relation of CatalogItemRetailerProbes that are failed. Active Record Scope

Returns:

See Also:



61
# File 'app/models/catalog_item_retailer_probe.rb', line 61

scope :failed, -> { where(status: %w[failed not_found product_mismatch]) }

.in_stockActiveRecord::Relation<CatalogItemRetailerProbe>

A relation of CatalogItemRetailerProbes that are in stock. Active Record Scope

Returns:

See Also:



65
# File 'app/models/catalog_item_retailer_probe.rb', line 65

scope :in_stock, -> { where(product_available: true) }

.latestActiveRecord::Relation<CatalogItemRetailerProbe>

A relation of CatalogItemRetailerProbes that are latest. Active Record Scope

Returns:

See Also:



63
# File 'app/models/catalog_item_retailer_probe.rb', line 63

scope :latest, -> { order(created_at: :desc).first }

.out_of_stockActiveRecord::Relation<CatalogItemRetailerProbe>

A relation of CatalogItemRetailerProbes that are out of stock. Active Record Scope

Returns:

See Also:



66
# File 'app/models/catalog_item_retailer_probe.rb', line 66

scope :out_of_stock, -> { where(product_available: false) }

.recentActiveRecord::Relation<CatalogItemRetailerProbe>

A relation of CatalogItemRetailerProbes that are recent. Active Record Scope

Returns:

See Also:



62
# File 'app/models/catalog_item_retailer_probe.rb', line 62

scope :recent, -> { where(created_at: 7.days.ago..) }

.successfulActiveRecord::Relation<CatalogItemRetailerProbe>

A relation of CatalogItemRetailerProbes that are successful. Active Record Scope

Returns:

See Also:



60
# File 'app/models/catalog_item_retailer_probe.rb', line 60

scope :successful, -> { where(status: 'success') }

.todayActiveRecord::Relation<CatalogItemRetailerProbe>

A relation of CatalogItemRetailerProbes that are today. Active Record Scope

Returns:

See Also:



64
# File 'app/models/catalog_item_retailer_probe.rb', line 64

scope :today, -> { where(created_at: Time.current.beginning_of_day..) }

Instance Method Details

#amazon_probe?Boolean

Check if this is an Amazon probe (has buy box tracking)

Returns:

  • (Boolean)


115
116
117
# File 'app/models/catalog_item_retailer_probe.rb', line 115

def amazon_probe?
  has_buy_box != nil
end

#availability_textObject

Availability as text



142
143
144
145
146
147
148
149
150
# File 'app/models/catalog_item_retailer_probe.rb', line 142

def availability_text
  if product_available.nil?
    'Unknown'
  elsif product_available
    'In Stock'
  else
    'Out of Stock'
  end
end

#buy_box?Boolean

Check if this probe detected a buy box (Amazon-specific)
Returns true only if we explicitly detected a buy box

Returns:

  • (Boolean)


104
105
106
# File 'app/models/catalog_item_retailer_probe.rb', line 104

def buy_box?
  has_buy_box == true
end

#catalog_itemCatalogItem



38
# File 'app/models/catalog_item_retailer_probe.rb', line 38

belongs_to :catalog_item

#in_stock?Boolean

Check if product appears to be in stock

Returns:

  • (Boolean)


93
94
95
# File 'app/models/catalog_item_retailer_probe.rb', line 93

def in_stock?
  product_available == true
end

#no_buy_box?Boolean

Check if this probe explicitly detected NO buy box (Amazon-specific)
Returns true only if we explicitly detected no buy box (not just nil/unknown)

Returns:

  • (Boolean)


110
111
112
# File 'app/models/catalog_item_retailer_probe.rb', line 110

def no_buy_box?
  has_buy_box == false
end

#out_of_stock?Boolean

Check if product is definitely out of stock

Returns:

  • (Boolean)


98
99
100
# File 'app/models/catalog_item_retailer_probe.rb', line 98

def out_of_stock?
  product_available == false
end

#price_captured?Boolean

Check if this represents a successful price capture

Returns:

  • (Boolean)


88
89
90
# File 'app/models/catalog_item_retailer_probe.rb', line 88

def price_captured?
  status == 'success' && price.present?
end

#summaryObject

Human-readable summary



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'app/models/catalog_item_retailer_probe.rb', line 120

def summary
  case status
  when 'success'
    parts = []
    if no_buy_box?
      parts << 'No Buy Box'
    elsif price.present?
      parts << format_price
      parts << "Seller: #{buy_box_seller}" if buy_box_seller.present?
    end
    parts << availability_text if product_available.present?
    parts.join(' | ')
  when 'failed'
    "Failed: #{error_message&.truncate(50)}"
  when 'not_found'
    'Product not found'
  else
    'Pending'
  end
end