mirror of
https://github.com/we-promise/sure.git
synced 2026-04-19 03:54:08 +00:00
* Add AI merchant enhancement and dedup * Enhancements Add error if job is already running add note that we also merge merchants * Allow updating provider website * Review fixes * Update provider_merchant.rb * Linter and fixes * FIX transaction quick menu modal
99 lines
3.6 KiB
Ruby
99 lines
3.6 KiB
Ruby
class ProviderMerchant::Enhancer
|
|
BATCH_SIZE = 25
|
|
|
|
def initialize(family)
|
|
@family = family
|
|
end
|
|
|
|
def enhance
|
|
return { enhanced: 0, deduplicated: 0 } unless llm_provider
|
|
return { enhanced: 0, deduplicated: 0 } if unenhanced_merchants.none?
|
|
|
|
Rails.logger.info("Enhancing #{unenhanced_merchants.count} provider merchants for family #{@family.id}")
|
|
|
|
enhanced_count = 0
|
|
deduplicated_count = 0
|
|
|
|
unenhanced_merchants.each_slice(BATCH_SIZE) do |batch|
|
|
result = llm_provider.enhance_provider_merchants(
|
|
merchants: batch.map { |m| { id: m.id, name: m.name } },
|
|
family: @family
|
|
)
|
|
|
|
next unless result.success?
|
|
|
|
result.data.each do |enhancement|
|
|
next unless enhancement.business_url.present?
|
|
|
|
merchant = batch.find { |m| m.id == enhancement.merchant_id }
|
|
next unless merchant
|
|
next if merchant.website_url.present? # Skip if already enhanced (race condition guard)
|
|
|
|
# Step 1: Update the provider merchant with website + logo
|
|
updates = { website_url: enhancement.business_url }
|
|
updates[:logo_url] = build_logo_url(enhancement.business_url) if Setting.brand_fetch_client_id.present?
|
|
merchant.update!(updates)
|
|
enhanced_count += 1
|
|
|
|
# Step 2: Deduplicate — find other merchants with the same website_url
|
|
# and merge them INTO this provider merchant (prefer provider over AI)
|
|
deduplicated_count += deduplicate_by_website(merchant, enhancement.business_url)
|
|
rescue ActiveRecord::RecordInvalid => e
|
|
Rails.logger.error("Failed to enhance merchant #{merchant.id}: #{e.message}")
|
|
end
|
|
end
|
|
|
|
Rails.logger.info("Enhanced #{enhanced_count} merchants, deduplicated #{deduplicated_count} for family #{@family.id}")
|
|
|
|
{ enhanced: enhanced_count, deduplicated: deduplicated_count }
|
|
end
|
|
|
|
private
|
|
|
|
def deduplicate_by_website(target_merchant, website_url)
|
|
# Find duplicate provider merchants assigned to this family with the same website_url.
|
|
# Excludes FamilyMerchants — user-curated merchants should never be touched by dedup.
|
|
duplicates = @family.assigned_merchants
|
|
.where(type: "ProviderMerchant")
|
|
.where(website_url: website_url)
|
|
.where.not(id: target_merchant.id)
|
|
|
|
return 0 if duplicates.none?
|
|
|
|
count = 0
|
|
duplicates.each do |duplicate|
|
|
# Reassign family's transactions from duplicate to target
|
|
@family.transactions.where(merchant_id: duplicate.id)
|
|
.update_all(merchant_id: target_merchant.id)
|
|
count += 1
|
|
end
|
|
count
|
|
end
|
|
|
|
def llm_provider
|
|
@llm_provider ||= Provider::Registry.get_provider(:openai)
|
|
end
|
|
|
|
def unenhanced_merchants
|
|
@unenhanced_merchants ||= @family.assigned_merchants
|
|
.where(type: "ProviderMerchant")
|
|
.where(website_url: [ nil, "" ])
|
|
.to_a
|
|
end
|
|
|
|
def build_logo_url(business_url)
|
|
return nil unless Setting.brand_fetch_client_id.present? && business_url.present?
|
|
domain = extract_domain(business_url)
|
|
return nil unless domain.present?
|
|
size = Setting.brand_fetch_logo_size
|
|
"https://cdn.brandfetch.io/#{domain}/icon/fallback/lettermark/w/#{size}/h/#{size}?c=#{Setting.brand_fetch_client_id}"
|
|
end
|
|
|
|
def extract_domain(url)
|
|
normalized_url = url.start_with?("http://", "https://") ? url : "https://#{url}"
|
|
URI.parse(normalized_url).host&.sub(/\Awww\./, "")
|
|
rescue URI::InvalidURIError
|
|
url.sub(/\Awww\./, "")
|
|
end
|
|
end
|