mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 01:01:39 -05:00
Stats calculation is now timezone-aware.
This commit is contained in:
parent
9803ccc6a8
commit
bdcfb5eb62
4 changed files with 63 additions and 6 deletions
|
|
@ -37,6 +37,16 @@ class Stat < ApplicationRecord
|
|||
end
|
||||
|
||||
def calculate_daily_distances(monthly_points)
|
||||
Stats::DailyDistanceQuery.new(monthly_points, timespan).call
|
||||
Stats::DailyDistanceQuery.new(monthly_points, timespan, user_timezone).call
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def user_timezone
|
||||
# Future: Once user.timezone column exists, uncomment the line below
|
||||
# user.timezone.presence || Time.zone.name
|
||||
|
||||
# For now, use application timezone
|
||||
Time.zone.name
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Stats::DailyDistanceQuery
|
||||
def initialize(monthly_points, timespan)
|
||||
def initialize(monthly_points, timespan, user_timezone = nil)
|
||||
@monthly_points = monthly_points
|
||||
@timespan = timespan
|
||||
@user_timezone = user_timezone || 'UTC'
|
||||
end
|
||||
|
||||
def call
|
||||
|
|
@ -15,22 +16,22 @@ class Stats::DailyDistanceQuery
|
|||
|
||||
private
|
||||
|
||||
attr_reader :monthly_points, :timespan
|
||||
attr_reader :monthly_points, :timespan, :user_timezone
|
||||
|
||||
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,
|
||||
EXTRACT(day FROM (to_timestamp(timestamp) AT TIME ZONE 'UTC' AT TIME ZONE '#{user_timezone}')) as day_of_month,
|
||||
CASE
|
||||
WHEN LAG(lonlat) OVER (
|
||||
PARTITION BY EXTRACT(day FROM to_timestamp(timestamp))
|
||||
PARTITION BY EXTRACT(day FROM (to_timestamp(timestamp) AT TIME ZONE 'UTC' AT TIME ZONE '#{user_timezone}'))
|
||||
ORDER BY timestamp
|
||||
) IS NOT NULL THEN
|
||||
ST_Distance(
|
||||
lonlat::geography,
|
||||
LAG(lonlat) OVER (
|
||||
PARTITION BY EXTRACT(day FROM to_timestamp(timestamp))
|
||||
PARTITION BY EXTRACT(day FROM (to_timestamp(timestamp) AT TIME ZONE 'UTC' AT TIME ZONE '#{user_timezone}'))
|
||||
ORDER BY timestamp
|
||||
)::geography
|
||||
)
|
||||
|
|
|
|||
30
benchmark_stats.rb
Normal file
30
benchmark_stats.rb
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
require 'benchmark'
|
||||
|
||||
# Test the optimized stats calculation
|
||||
data = Benchmark.measure do
|
||||
user_id = 7
|
||||
|
||||
last_calculated_at = DateTime.new(1970, 1, 1)
|
||||
|
||||
time_diff = last_calculated_at.to_i..Time.current.to_i
|
||||
timestamps = Point.where(user_id:, timestamp: time_diff).pluck(:timestamp).uniq
|
||||
|
||||
months = timestamps.group_by do |timestamp|
|
||||
time = Time.zone.at(timestamp)
|
||||
[time.year, time.month]
|
||||
end.keys
|
||||
|
||||
months.each do |year, month|
|
||||
Stats::CalculateMonth.new(user_id, year, month).call
|
||||
end
|
||||
end
|
||||
|
||||
puts "Stats calculation benchmark:"
|
||||
puts "User Time: #{data.utime}s"
|
||||
puts "System Time: #{data.stime}s"
|
||||
puts "Total Time: #{data.real}s"
|
||||
|
||||
# @real=28.869485000148416,
|
||||
# @stime=2.4980050000000027,
|
||||
# @total=20.303141999999976,
|
||||
# @utime=17.805136999999974>
|
||||
16
db/migrate/future_add_timezone_to_users.rb.example
Normal file
16
db/migrate/future_add_timezone_to_users.rb.example
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Example migration for adding per-user timezone support
|
||||
# To use: rename this file to remove .example and run rails db:migrate
|
||||
|
||||
class AddTimezoneToUsers < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
add_column :users, :timezone, :string, default: 'UTC'
|
||||
add_index :users, :timezone
|
||||
|
||||
# Populate existing users with application timezone
|
||||
reversible do |dir|
|
||||
dir.up do
|
||||
User.update_all(timezone: Time.zone.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in a new issue