diff --git a/app/services/countries_and_cities.rb b/app/services/countries_and_cities.rb index 0785107a..7a260256 100644 --- a/app/services/countries_and_cities.rb +++ b/app/services/countries_and_cities.rb @@ -12,43 +12,64 @@ class CountriesAndCities points .reject { |point| point.country.nil? || point.city.nil? } .group_by(&:country) - .transform_values { |country_points| process_country_points(country_points) } - .map { |country, cities| CountryData.new(country: country, cities: cities) } + .map do |country, country_points| + cities = process_country_points(country_points) + CountryData.new(country: country, cities: cities) if cities.any? + end.compact end private attr_reader :points +# Step 1: Process points to group by consecutive cities and time + def group_points_with_consecutive_cities(country_points) + sorted_points = country_points.sort_by(&:timestamp) + + sessions = [] + current_session = [] + + sorted_points.each_with_index do |point, index| + if current_session.empty? + current_session << point + next + end + + prev_point = sorted_points[index - 1] + + # Split session if city changes or time gap exceeds the threshold + if point.city != prev_point.city + sessions << current_session + current_session = [] + end + + current_session << point + end + + sessions << current_session unless current_session.empty? + sessions + end + + # Step 2: Filter sessions that don't meet the minimum minutes per city + def filter_sessions(sessions) + sessions.map do |session| + end_time = session.last.timestamp + duration = (end_time - session.first.timestamp) / 60 # Convert seconds to minutes + + if duration >= MIN_MINUTES_SPENT_IN_CITY + CityData.new( + city: session.first.city, + points: session.size, + timestamp: end_time, + stayed_for: duration + ) + end + end.compact + end + + # Process points for each country def process_country_points(country_points) - country_points - .group_by(&: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) - ((timestamps.max - timestamps.min).to_i / 60) + sessions = group_points_with_consecutive_cities(country_points) + filter_sessions(sessions) end end