dawarich/app/services/users/import_data/places.rb

116 lines
4 KiB
Ruby
Raw Normal View History

2025-06-28 06:22:56 -04:00
# frozen_string_literal: true
class Users::ImportData::Places
def initialize(user, places_data)
@user = user
@places_data = places_data
end
def call
return 0 unless places_data.is_a?(Array)
Rails.logger.info "Importing #{places_data.size} places for user: #{user.email}"
places_created = 0
# Preload all existing places to avoid N+1 queries
@existing_places_cache = load_existing_places
2025-06-28 06:22:56 -04:00
places_data.each do |place_data|
next unless place_data.is_a?(Hash)
2025-07-02 14:22:40 -04:00
place = find_or_create_place_for_import(place_data)
2025-06-28 06:22:56 -04:00
places_created += 1 if place&.respond_to?(:previously_new_record?) && place.previously_new_record?
end
Rails.logger.info "Places import completed. Created: #{places_created}"
places_created
end
private
attr_reader :user, :places_data
def load_existing_places
# Extract all coordinates from places_data to preload relevant places
coordinates = places_data.select do |pd|
pd.is_a?(Hash) && pd['name'].present? && pd['latitude'].present? && pd['longitude'].present?
end.map { |pd| { name: pd['name'], lat: pd['latitude'].to_f, lon: pd['longitude'].to_f } }
return {} if coordinates.empty?
# Build a hash for quick lookup: "name_lat_lon" => place
cache = {}
# Build OR conditions using Arel to fetch all matching places in a single query
arel_table = Place.arel_table
conditions = coordinates.map do |coord|
arel_table[:name].eq(coord[:name])
.and(arel_table[:latitude].eq(coord[:lat]))
.and(arel_table[:longitude].eq(coord[:lon]))
end.reduce { |result, condition| result.or(condition) }
# Fetch all matching places in a single query
Place.where(conditions).find_each do |place|
cache_key = place_cache_key(place.name, place.latitude, place.longitude)
cache[cache_key] = place
end
cache
end
def place_cache_key(name, latitude, longitude)
"#{name}_#{latitude}_#{longitude}"
end
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?
Rails.logger.debug "Skipping place with missing required data: #{place_data.inspect}"
return nil
end
2025-07-02 14:22:40 -04:00
Rails.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
# During import, we prioritize data integrity for the importing user
# First try exact match (name + coordinates) from cache
cache_key = place_cache_key(name, latitude, longitude)
existing_place = @existing_places_cache[cache_key]
2025-06-28 06:22:56 -04:00
if existing_place
2025-07-02 14:22:40 -04:00
Rails.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-07-02 14:22:40 -04:00
Rails.logger.debug "No exact match found for #{name} at (#{latitude}, #{longitude}). Creating new place."
# If no exact match, create a new place to ensure data integrity
# This prevents data loss during import even if similar places exist
2025-06-28 06:22:56 -04:00
place_attributes = place_data.except('created_at', 'updated_at', 'latitude', 'longitude')
place_attributes['lonlat'] = "POINT(#{longitude} #{latitude})"
place_attributes['latitude'] = latitude
place_attributes['longitude'] = longitude
place_attributes.delete('user')
2025-07-02 14:22:40 -04:00
Rails.logger.debug "Creating place with attributes: #{place_attributes.inspect}"
2025-06-28 06:22:56 -04:00
begin
place = Place.create!(place_attributes)
place.define_singleton_method(:previously_new_record?) { true }
2025-07-02 14:22:40 -04:00
Rails.logger.debug "Created place during import: #{place.name} (ID: #{place.id})"
2025-06-28 06:22:56 -04:00
# Add to cache for subsequent lookups
@existing_places_cache[cache_key] = place
2025-06-28 06:22:56 -04:00
place
rescue ActiveRecord::RecordInvalid => e
2025-07-02 14:22:40 -04:00
Rails.logger.error "Failed to create place: #{place_data.inspect}, error: #{e.message}"
2025-06-28 06:22:56 -04:00
nil
end
end
end