dawarich/app/services/stats/calculate_month.rb

89 lines
2.1 KiB
Ruby
Raw Normal View History

2024-12-06 10:52:36 -05:00
# frozen_string_literal: true
class Stats::CalculateMonth
def initialize(user_id, year, month)
@user = User.find(user_id)
@year = year.to_i
@month = month.to_i
2024-12-06 10:52:36 -05:00
end
def call
if points.empty?
destroy_month_stats(year, month)
return
end
2024-12-06 10:52:36 -05:00
update_month_stats(year, month)
rescue StandardError => e
create_stats_update_failed_notification(user, e)
end
private
attr_reader :user, :year, :month
def start_timestamp = DateTime.new(year, month, 1).to_i
def end_timestamp
DateTime.new(year, month, -1).to_i
2024-12-06 10:52:36 -05:00
end
def update_month_stats(year, month)
Stat.transaction do
stat = Stat.find_or_initialize_by(year:, month:, user:)
distance_by_day = stat.distance_by_day
stat.assign_attributes(
daily_distance: distance_by_day,
distance: distance(distance_by_day),
2025-09-13 17:11:42 -04:00
toponyms: toponyms,
h3_hex_ids: calculate_h3_hex_ids
2024-12-06 10:52:36 -05:00
)
2025-09-29 16:27:07 -04:00
stat.save!
Cache::InvalidateUserCaches.new(user.id).call
2024-12-06 10:52:36 -05:00
end
end
def points
return @points if defined?(@points)
# Select all needed columns to avoid duplicate queries
# Used for both distance calculation and toponyms extraction
2024-12-06 10:52:36 -05:00
@points = user
.points
2024-12-06 10:52:36 -05:00
.without_raw_data
.where(timestamp: start_timestamp..end_timestamp)
.select(:lonlat, :timestamp, :city, :country_name)
2024-12-06 10:52:36 -05:00
.order(timestamp: :asc)
end
def distance(distance_by_day)
distance_by_day.sum { |day| day[1] }
end
def toponyms
# Reuse already-loaded points instead of making a duplicate query
CountriesAndCities.new(points).call
2024-12-06 10:52:36 -05:00
end
def create_stats_update_failed_notification(user, error)
Notifications::Create.new(
user:,
kind: :error,
title: 'Stats update failed',
content: "#{error.message}, stacktrace: #{error.backtrace.join("\n")}"
).call
end
def destroy_month_stats(year, month)
Stat.where(year:, month:, user:).destroy_all
end
2025-09-13 17:11:42 -04:00
def calculate_h3_hex_ids
Stats::HexagonCalculator.new(user.id, year, month).call
end
2024-12-06 10:52:36 -05:00
end