dawarich/app/queries/stats/daily_distance_query.rb
2025-07-22 22:41:12 +02:00

64 lines
1.7 KiB
Ruby

# frozen_string_literal: true
class Stats::DailyDistanceQuery
def initialize(monthly_points, timespan)
@monthly_points = monthly_points
@timespan = timespan
end
def call
daily_distances = daily_distances(monthly_points)
distance_by_day_map = distance_by_day_map(daily_distances)
convert_to_daily_distances(distance_by_day_map)
end
private
attr_reader :monthly_points, :timespan
def daily_distances(monthly_points)
Stat.connection.select_all(<<-SQL.squish)
WITH points_with_distances AS (
SELECT
EXTRACT(day FROM to_timestamp(timestamp)) as day_of_month,
CASE
WHEN LAG(lonlat) OVER (
PARTITION BY EXTRACT(day FROM to_timestamp(timestamp))
ORDER BY timestamp
) IS NOT NULL THEN
ST_Distance(
lonlat::geography,
LAG(lonlat) OVER (
PARTITION BY EXTRACT(day FROM to_timestamp(timestamp))
ORDER BY timestamp
)::geography
)
ELSE 0
END as segment_distance
FROM (#{monthly_points.to_sql}) as points
)
SELECT
day_of_month,
ROUND(COALESCE(SUM(segment_distance), 0)) as distance_meters
FROM points_with_distances
GROUP BY day_of_month
ORDER BY day_of_month
SQL
end
def distance_by_day_map(daily_distances)
daily_distances.index_by do |row|
row['day_of_month'].to_i
end
end
def convert_to_daily_distances(distance_by_day_map)
timespan.to_a.map.with_index(1) do |day, index|
distance_meters =
distance_by_day_map[day.day]&.fetch('distance_meters', 0) || 0
[index, distance_meters.to_i]
end
end
end