2025-06-28 06:22:56 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
|
|
class Users::ImportData::Places
|
2025-10-05 15:18:16 -04:00
|
|
|
BATCH_SIZE = 5000
|
2025-10-05 14:59:03 -04:00
|
|
|
|
|
|
|
|
def initialize(user, places_data = nil, batch_size: BATCH_SIZE, logger: Rails.logger)
|
2025-06-28 06:22:56 -04:00
|
|
|
@user = user
|
|
|
|
|
@places_data = places_data
|
2025-10-05 14:59:03 -04:00
|
|
|
@batch_size = batch_size
|
|
|
|
|
@logger = logger
|
|
|
|
|
@buffer = []
|
|
|
|
|
@created = 0
|
2025-06-28 06:22:56 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def call
|
2025-10-05 14:59:03 -04:00
|
|
|
return 0 unless places_data.respond_to?(:each)
|
2025-06-28 06:22:56 -04:00
|
|
|
|
2025-10-05 14:59:03 -04:00
|
|
|
logger.info "Importing #{collection_description(places_data)} places for user: #{user.email}"
|
2025-06-28 06:22:56 -04:00
|
|
|
|
2025-10-05 14:59:03 -04:00
|
|
|
enumerate(places_data) do |place_data|
|
|
|
|
|
add(place_data)
|
|
|
|
|
end
|
2025-06-28 06:22:56 -04:00
|
|
|
|
2025-10-05 14:59:03 -04:00
|
|
|
finalize
|
|
|
|
|
end
|
2025-06-28 06:22:56 -04:00
|
|
|
|
2025-10-05 14:59:03 -04:00
|
|
|
def add(place_data)
|
|
|
|
|
return unless place_data.is_a?(Hash)
|
2025-06-28 06:22:56 -04:00
|
|
|
|
2025-10-05 14:59:03 -04:00
|
|
|
@buffer << place_data
|
|
|
|
|
flush_batch if @buffer.size >= batch_size
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def finalize
|
|
|
|
|
flush_batch
|
|
|
|
|
logger.info "Places import completed. Created: #{@created}"
|
|
|
|
|
@created
|
2025-06-28 06:22:56 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
2025-10-05 14:59:03 -04:00
|
|
|
attr_reader :user, :places_data, :batch_size, :logger
|
|
|
|
|
|
|
|
|
|
def enumerate(collection, &block)
|
2025-10-05 15:18:16 -04:00
|
|
|
collection.each(&block)
|
2025-10-05 14:59:03 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def collection_description(collection)
|
|
|
|
|
return collection.size if collection.respond_to?(:size)
|
|
|
|
|
|
|
|
|
|
'streamed'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def flush_batch
|
|
|
|
|
return if @buffer.empty?
|
|
|
|
|
|
|
|
|
|
logger.debug "Processing places batch of #{@buffer.size}"
|
|
|
|
|
@buffer.each do |place_data|
|
|
|
|
|
place = find_or_create_place_for_import(place_data)
|
|
|
|
|
@created += 1 if place&.respond_to?(:previously_new_record?) && place.previously_new_record?
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@buffer.clear
|
|
|
|
|
end
|
2025-06-28 06:22:56 -04:00
|
|
|
|
2025-07-02 14:22:40 -04:00
|
|
|
def find_or_create_place_for_import(place_data)
|
2025-06-28 06:22:56 -04:00
|
|
|
name = place_data['name']
|
|
|
|
|
latitude = place_data['latitude']&.to_f
|
|
|
|
|
longitude = place_data['longitude']&.to_f
|
|
|
|
|
|
|
|
|
|
unless name.present? && latitude.present? && longitude.present?
|
2025-10-05 14:59:03 -04:00
|
|
|
logger.debug "Skipping place with missing required data: #{place_data.inspect}"
|
2025-06-28 06:22:56 -04:00
|
|
|
return nil
|
|
|
|
|
end
|
|
|
|
|
|
2025-10-05 14:59:03 -04:00
|
|
|
logger.debug "Processing place for import: #{name} at (#{latitude}, #{longitude})"
|
2025-06-28 06:22:56 -04:00
|
|
|
|
2025-07-02 14:22:40 -04:00
|
|
|
existing_place = Place.where(
|
2025-11-17 13:05:58 -05:00
|
|
|
user_id: user.id,
|
2025-07-02 14:22:40 -04:00
|
|
|
name: name,
|
|
|
|
|
latitude: latitude,
|
|
|
|
|
longitude: longitude
|
|
|
|
|
).first
|
2025-06-28 06:22:56 -04:00
|
|
|
|
|
|
|
|
if existing_place
|
2025-10-05 14:59:03 -04:00
|
|
|
logger.debug "Found exact place match: #{name} at (#{latitude}, #{longitude}) -> existing place ID #{existing_place.id}"
|
2025-06-28 06:22:56 -04:00
|
|
|
existing_place.define_singleton_method(:previously_new_record?) { false }
|
|
|
|
|
return existing_place
|
|
|
|
|
end
|
|
|
|
|
|
2025-10-05 14:59:03 -04:00
|
|
|
logger.debug "No exact match found for #{name} at (#{latitude}, #{longitude}). Creating new place."
|
2025-07-02 14:22:40 -04:00
|
|
|
|
2025-06-28 06:22:56 -04:00
|
|
|
place_attributes = place_data.except('created_at', 'updated_at', 'latitude', 'longitude')
|
2025-11-19 15:40:43 -05:00
|
|
|
place_attributes['lonlat'] = "POINT(#{longitude} #{latitude})"
|
2025-06-28 06:22:56 -04:00
|
|
|
place_attributes['latitude'] = latitude
|
|
|
|
|
place_attributes['longitude'] = longitude
|
2025-11-17 13:05:58 -05:00
|
|
|
place_attributes['user_id'] = user.id
|
2025-06-28 06:22:56 -04:00
|
|
|
place_attributes.delete('user')
|
|
|
|
|
|
2025-10-05 14:59:03 -04:00
|
|
|
logger.debug "Creating place with attributes: #{place_attributes.inspect}"
|
2025-07-02 14:22:40 -04:00
|
|
|
|
2025-06-28 06:22:56 -04:00
|
|
|
begin
|
|
|
|
|
place = Place.create!(place_attributes)
|
|
|
|
|
place.define_singleton_method(:previously_new_record?) { true }
|
2025-10-05 14:59:03 -04:00
|
|
|
logger.debug "Created place during import: #{place.name} (ID: #{place.id})"
|
2025-06-28 06:22:56 -04:00
|
|
|
|
|
|
|
|
place
|
|
|
|
|
rescue ActiveRecord::RecordInvalid => e
|
2025-10-05 14:59:03 -04:00
|
|
|
logger.error "Failed to create place: #{place_data.inspect}, error: #{e.message}"
|
2025-06-28 06:22:56 -04:00
|
|
|
nil
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|