dawarich/app/services/countries_and_cities.rb

66 lines
1.8 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
class CountriesAndCities
2024-12-11 11:14:26 -05:00
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
2024-12-11 11:14:26 -05:00
points
.reject { |point| point[:country_name].nil? || point[:city].nil? }
.group_by { |point| point[:country_name] }
2025-06-09 06:09:26 -04:00
.transform_values { |country_points| process_country_points(country_points) }
.map { |country, cities| CountryData.new(country: country, cities: cities) }
end
private
attr_reader :points
2025-06-09 06:09:26 -04:00
def process_country_points(country_points)
country_points
.group_by { |point| point[:city] }
2025-06-09 06:09:26 -04:00
.transform_values { |city_points| create_city_data_if_valid(city_points) }
.values
.compact
end
2025-06-09 06:09:26 -04:00
def create_city_data_if_valid(city_points)
timestamps = city_points.pluck(:timestamp)
duration = calculate_duration_in_minutes(timestamps)
city = city_points.first[:city]
2025-06-09 06:09:26 -04:00
points_count = city_points.size
2025-06-09 06:09:26 -04:00
build_city_data(city, points_count, timestamps, duration)
2024-12-16 09:10:46 -05:00
end
2025-06-09 06:09:26 -04:00
def build_city_data(city, points_count, timestamps, duration)
return nil if duration < ::MIN_MINUTES_SPENT_IN_CITY
2024-12-16 09:10:46 -05:00
2025-06-09 06:09:26 -04:00
CityData.new(
city: city,
points: points_count,
timestamp: timestamps.max,
stayed_for: duration
)
end
2025-06-09 06:09:26 -04:00
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