Fix logic for grouping consecutive points in CountriesAndCities

This update corrects the logic for grouping consecutive points in the group_points_with_consecutive_cities method. It ensures sessions are properly split when transitioning between cities or encountering significant time gaps, leading to accurate grouping and filtering of points based on session duration.
This commit is contained in:
Arne Schwarck 2025-01-01 13:06:07 +01:00 committed by GitHub
parent a43f2c6a1d
commit c1b767d791
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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