class GeolocationService::Services::ImportBulkDataService

Constants

Contract
DEFAULT_CONTRACTS
Structs

Public Instance Methods

call() click to toggle source
# File lib/geolocation_service/services/import_bulk_data_service.rb, line 18
def call
  start_time = Time.zone.now
  load_existing_records
  load_new_records

  CSV.foreach(file_path, headers: :first_row) do |row|
    ip = build_or_find_ip(row)
    if ip.nil?
      @invalid_records += 1
      next
    end

    city = find_record(:city, row['city']&.downcase)
    if city.nil?
      country = find_or_build_country(row)
      city = build_city(row, country)
    end

    build_location(row, ip, city)
  end

  bulk_save(@new_records[:ip].values, GeolocationService::Structs::IpStruct)
  bulk_save(@new_records[:country].values, GeolocationService::Structs::CountryStruct)
  bulk_save(@new_records[:city].values, GeolocationService::Structs::CityStruct)
  bulk_save(@new_records[:location], GeolocationService::Structs::LocationStruct)

  GeolocationService::ImportResult.new(
    imported_records: {
      ip: @new_records[:ip].values.size,
      city: @new_records[:city].values.size,
      country: @new_records[:country].values.size,
      location: @new_records[:location].size
    },
    invalid_records: @invalid_records,
    time_consumed: (Time.zone.now - start_time)
  )
end

Private Instance Methods

build_city(row, country) click to toggle source
# File lib/geolocation_service/services/import_bulk_data_service.rb, line 74
def build_city(row, country)
  return if row['city'].blank?

  if validate(:city, name: row['city'], country_id: country&.id).success?
    new_city = Structs::CityStruct.new(@city_count, row['city'], country&.id)
    @new_records[:city][row['city'].downcase] = new_city
    @city_count += 1
    return new_city
  end

  nil
end
build_location(row, ip, city) click to toggle source
# File lib/geolocation_service/services/import_bulk_data_service.rb, line 103
def build_location(row, ip, city)
  if validate(:location, latitude: row['latitude']&.to_d, longitude: row['longitude']&.to_d, ip_id: ip&.id,
    city_id: city&.id).success?
    @new_records[:location] << Structs::LocationStruct.new(ip.id, row['latitude']&.to_d, row['longitude']&.to_d,
      city&.id)
  end
end
build_or_find_ip(row) click to toggle source
# File lib/geolocation_service/services/import_bulk_data_service.rb, line 58
def build_or_find_ip(row)
  return if row['ip_address'].blank?

  existing_ip = find_record(:ip, row['ip_address'])
  return existing_ip if existing_ip.present?

  if validate(:ip, address: row['ip_address'], mystery_value: row['mystery_value']).success?
    new_ip = Structs::IpStruct.new(@ip_count, row['ip_address'], row['mystery_value'])
    @new_records[:ip][row['ip_address']] = new_ip
    @ip_count += 1
    return new_ip
  end

  nil
end
bulk_save(all_records, struct, chunk: 1000) click to toggle source
# File lib/geolocation_service/services/import_bulk_data_service.rb, line 137
def bulk_save(all_records, struct, chunk: 1000)
  all_records.each_slice(chunk) do |records|
    ActiveRecord::Base.connection.execute(struct.insert_sql(records))
  end
end
find_or_build_country(row) click to toggle source
# File lib/geolocation_service/services/import_bulk_data_service.rb, line 87
def find_or_build_country(row)
  return if row['country_code'].blank?

  existing_country = find_record(:country, row['country_code'])
  return existing_country if existing_country.present?

  if validate(:country, name: row['country'], code: row['country_code']).success?
    new_country = Structs::CountryStruct.new(@country_count, row['country_code'], row['country'])
    @new_records[:country][row['country_code']] = new_country
    @country_count += 1
    return new_country
  end

  nil
end
find_record(type, key) click to toggle source
# File lib/geolocation_service/services/import_bulk_data_service.rb, line 127
def find_record(type, key)
  return if key.nil?

  @existing_records[type][key] || @new_records[type][key]
end
load_existing_records() click to toggle source
# File lib/geolocation_service/services/import_bulk_data_service.rb, line 111
def load_existing_records
  @existing_records = {
    ip: Hash[Ip.all.collect { |ip| [ip.address, ip] }],
    city: Hash[City.all.collect { |city| [city.name.downcase, city] }],
    country: Hash[Country.all.collect { |country| [country.code.downcase, country] }]
  }
end
load_new_records() click to toggle source
# File lib/geolocation_service/services/import_bulk_data_service.rb, line 119
def load_new_records
  @ip_count = Ip.count == 0 ? 0 : Ip.last.id + 1
  @city_count = City.count == 0 ? 0 : City.last.id + 1
  @country_count = Country.count == 0 ? 0 : Country.last.id + 1
  @invalid_records = 0
  @new_records = {ip: {}, location: [], city: {}, country: {}}
end
validate(type, **args) click to toggle source
# File lib/geolocation_service/services/import_bulk_data_service.rb, line 133
def validate(type, **args)
  contracts[type].call(args)
end