dawarich/app/services/countries_and_cities.rb

65 lines
1.8 KiB
Ruby

# frozen_string_literal: true
class CountriesAndCities
CountryData = Struct.new(:country, :cities, keyword_init: true)
CityData = Struct.new(:city, :points, :timestamp, :stayed_for, keyword_init: true)
def initialize(points)
@points = points
end
def call
points
.reject { |point| point[:country_name].nil? || point[:city].nil? }
.group_by { |point| point[:country_name] }
.transform_values { |country_points| process_country_points(country_points) }
.map { |country, cities| CountryData.new(country: country, cities: cities) }
end
private
attr_reader :points
def process_country_points(country_points)
country_points
.group_by { |point| point[:city] }
.transform_values { |city_points| create_city_data_if_valid(city_points) }
.values
.compact
end
def create_city_data_if_valid(city_points)
timestamps = city_points.pluck(:timestamp)
duration = calculate_duration_in_minutes(timestamps)
city = city_points.first[:city]
points_count = city_points.size
build_city_data(city, points_count, timestamps, duration)
end
def build_city_data(city, points_count, timestamps, duration)
return nil if duration < ::MIN_MINUTES_SPENT_IN_CITY
CityData.new(
city: city,
points: points_count,
timestamp: timestamps.max,
stayed_for: duration
)
end
def calculate_duration_in_minutes(timestamps)
return 0 if timestamps.size < 2
sorted = timestamps.sort
total_minutes = 0
gap_threshold_seconds = ::MIN_MINUTES_SPENT_IN_CITY * 60
sorted.each_cons(2) do |prev_ts, curr_ts|
interval_seconds = curr_ts - prev_ts
total_minutes += (interval_seconds / 60) if interval_seconds < gap_threshold_seconds
end
total_minutes
end
end