dawarich/app/controllers/stats_controller.rb
2025-09-12 08:33:51 +02:00

163 lines
4.6 KiB
Ruby

# frozen_string_literal: true
class StatsController < ApplicationController
before_action :authenticate_user!, except: [:public_show]
before_action :authenticate_active_user!, only: %i[update update_all update_sharing]
def index
@stats = build_stats
assign_points_statistics
@year_distances = precompute_year_distances
end
def show
@year = params[:year].to_i
@stats = current_user.stats.where(year: @year).order(:month)
@year_distances = { @year => Stat.year_distance(@year, current_user) }
end
def month
@year = params[:year].to_i
@month = params[:month].to_i
@stat = current_user.stats.find_by(year: @year, month: @month)
@previous_stat = current_user.stats.find_by(year: @year, month: @month - 1) if @month > 1
@average_distance_this_year = current_user.stats.where(year: @year).average(:distance) / 1000
end
def update
if params[:month] == 'all'
(1..12).each do |month|
Stats::CalculatingJob.perform_later(current_user.id, params[:year], month)
end
target = "the whole #{params[:year]}"
else
Stats::CalculatingJob.perform_later(current_user.id, params[:year], params[:month])
target = "#{Date::MONTHNAMES[params[:month].to_i]} of #{params[:year]}"
end
redirect_to stats_path, notice: "Stats for #{target} are being updated", status: :see_other
end
def update_all
current_user.years_tracked.each do |year|
year[:months].each do |month|
Stats::CalculatingJob.perform_later(
current_user.id, year[:year], Date::ABBR_MONTHNAMES.index(month)
)
end
end
redirect_to stats_path, notice: 'Stats are being updated', status: :see_other
end
def update_sharing
@year = params[:year].to_i
@month = params[:month].to_i
@stat = current_user.stats.find_by(year: @year, month: @month)
return head :not_found unless @stat
if params[:enabled] == '1'
@stat.enable_sharing!(expiration: params[:expiration] || 'permanent')
sharing_url = public_stat_url(@stat.sharing_uuid)
render json: {
success: true,
sharing_url: sharing_url,
message: 'Sharing enabled successfully'
}
else
@stat.disable_sharing!
render json: {
success: true,
message: 'Sharing disabled successfully'
}
end
rescue StandardError
render json: {
success: false,
message: 'Failed to update sharing settings'
}, status: :unprocessable_entity
end
def public_show
@stat = Stat.find_by(sharing_uuid: params[:uuid])
unless @stat&.public_accessible?
return redirect_to root_path,
alert: 'Shared stats not found or no longer available'
end
@year = @stat.year
@month = @stat.month
@user = @stat.user
@is_public_view = true
@data_bounds = calculate_data_bounds(@stat)
render 'public_month'
end
private
def assign_points_statistics
points_stats = ::StatsQuery.new(current_user).points_stats
@points_total = points_stats[:total]
@points_reverse_geocoded = points_stats[:geocoded]
@points_reverse_geocoded_without_data = points_stats[:without_data]
end
def precompute_year_distances
year_distances = {}
@stats.each do |year, stats|
stats_by_month = stats.index_by(&:month)
year_distances[year] = (1..12).map do |month|
month_name = Date::MONTHNAMES[month]
distance = stats_by_month[month]&.distance || 0
[month_name, distance]
end
end
year_distances
end
def build_stats
current_user.stats.group_by(&:year).transform_values do |stats|
stats.sort_by(&:updated_at).reverse
end.sort.reverse
end
def calculate_data_bounds(stat)
start_date = Date.new(stat.year, stat.month, 1).beginning_of_day
end_date = start_date.end_of_month.end_of_day
points_relation = stat.user.points.where(timestamp: start_date.to_i..end_date.to_i)
point_count = points_relation.count
return nil if point_count.zero?
bounds_result = ActiveRecord::Base.connection.exec_query(
"SELECT MIN(latitude) as min_lat, MAX(latitude) as max_lat,
MIN(longitude) as min_lng, MAX(longitude) as max_lng
FROM points
WHERE user_id = $1
AND timestamp BETWEEN $2 AND $3",
'data_bounds_query',
[stat.user.id, start_date.to_i, end_date.to_i]
).first
{
min_lat: bounds_result['min_lat'].to_f,
max_lat: bounds_result['max_lat'].to_f,
min_lng: bounds_result['min_lng'].to_f,
max_lng: bounds_result['max_lng'].to_f,
point_count: point_count
}
end
end