Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Fix Missing Restaurant Image

Purpose

This script repairs missing restaurant logos and gallery images by fetching production API data. If image data is missing, it uses fallback dummy assets.

Prerequisites

  • Rails environment with production access
  • Gems: retriable, faraday
  • Run in controlled environment (helper pod or maintenance shell)

How to run

rails c --production
# paste and run script

Verification

  • Check updated restaurants in admin UI.
  • Confirm no broken image placeholders.
  • Verify cache refresh workers completed successfully.

Rollback guidance

  • Re-run using known-good source URLs.
  • Restore from backup/snapshot if a bulk update is incorrect.

Script

# Fetch production data and fix missing logo/pictures.
host = 'https://hungryhub.com'

def fix_image_url(url)
  return url if url.include?('http')

  URI.join('https://images.hungryhub.com', URI.escape(url)).to_s
end

errors = []
dummy_logo_url = 'https://images.hungryhub.com/uploads/restaurant/logo/997/12764618_993228954095365_7698085734652638054_o.jpg'
dummy_picture_urls = [
  'uploads/restaurants/997/photos/32963/RackMultipart20200220-229-13mf4s0.jpg',
  'uploads/restaurants/997/photos/32964/RackMultipart20200220-229-fsfiie.jpg'
]

Restaurant.active.not_expired.find_each do |restaurant|
  data = nil

  if restaurant.logo.blank? || (restaurant.logo.present? && !restaurant.logo.file.present?)
    data = Retriable.retriable do
      response = Faraday.get("#{host}/api/v5/restaurants/#{restaurant.id}.json?include_packages=false&include_pictures=true&minor_version=3&preview_mode=false")
      JSON.parse(response.body)
    end

    attributes = data['data']['attributes']
    logo_url = attributes['logo_url']['medium']

    restaurant.remote_logo_url = fix_image_url(logo_url.presence || dummy_logo_url)
    restaurant.save!
  end

  data ||= Retriable.retriable do
    response = Faraday.get("#{host}/api/v5/restaurants/#{restaurant.id}.json?include_packages=false&include_pictures=true&minor_version=3&preview_mode=false")
    JSON.parse(response.body)
  end

  pictures = data['included'].select { |r| r['type'] == 'restaurants-pictures' }

  restaurant.pictures.each_with_index do |picture, index|
    if picture.item.blank? || (picture.item.present? && !picture.item.file.present?)
      prod_picture = pictures[index]
      picture.remote_item_url = fix_image_url(prod_picture&.dig('attributes', 'item', 'url') || dummy_picture_urls.sample)
      picture.save!
    end
  end

  if restaurant.pictures.cover.blank?
    picture = restaurant.pictures.first
    if picture
      picture.tag_list << 'cover'
      picture.save!
    end
  end

  # Trigger compact data rebuild and cache refresh.
  GenerateCompactRestaurantsWorker.perform_async(restaurant.id)
  restaurant.refresh_view_cache_key
  restaurant.touch

  puts "done restaurant id -> #{restaurant.id}"
rescue StandardError => e
  errors << { restaurant_id: restaurant.id, error: e.message }
end

puts "Failed restaurants: #{errors.map { |x| x[:restaurant_id] }.join(', ')}"