leaderboards rework (#623)

* rolling leaderboards

* use actual deletions over soft deletes
This commit is contained in:
Echo 2025-11-15 17:34:13 -05:00 committed by GitHub
parent 7d4ca90e18
commit 4466d8d820
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 25 additions and 183 deletions

View file

@ -1,7 +1,6 @@
class LeaderboardsController < ApplicationController
def index
set_params
validate_timezone_requirements
@leaderboard = find_or_generate_leaderboard
@ -15,90 +14,16 @@ class LeaderboardsController < ApplicationController
private
def set_params
@use_timezone_leaderboard = current_user&.default_timezone_leaderboard
@period_type = validated_period_type
@scope = params[:scope] || (@use_timezone_leaderboard ? "regional" : "global")
@scope_description = scope_description
end
def validated_period_type
period = (params[:period_type] || "daily").to_sym
valid_periods = [ :daily, :weekly, :last_7_days ]
valid_periods = [ :daily, :last_7_days ]
valid_periods.include?(period) ? period : :daily
end
def scope_description
case @scope
when "regional" then current_user&.timezone_offset_name
when "timezone" then current_user&.timezone
end
end
def validate_timezone_requirements
return unless regional_or_timezone_scope?
unless current_user
flash[:error] = "Please log in to view regional leaderboards!"
redirect_to leaderboards_path(scope: "global")
return
end
unless current_user.timezone
flash[:error] = "Please set your timezone in settings to view regional leaderboards"
redirect_to my_settings_path
return
end
if @scope == "regional" && current_user.timezone_utc_offset.nil?
flash[:error] = "Unable to determine UTC offset for your timezone: #{current_user.timezone}"
redirect_to leaderboards_path
end
end
def regional_or_timezone_scope?
%w[regional timezone].include?(@scope)
end
def find_or_generate_leaderboard
leaderboard = case @scope
when "regional" then generate_regional_leaderboard
when "timezone" then generate_timezone_leaderboard
else find_or_generate_global_leaderboard
end
if leaderboard.nil? && %w[regional timezone].include?(@scope)
@scope = "global"
@scope_description = nil
leaderboard = find_or_generate_global_leaderboard
end
leaderboard
end
def generate_regional_leaderboard
return nil unless current_user&.timezone_utc_offset
LeaderboardService.get(
period: @period_type,
date: start_date,
offset: current_user.timezone_utc_offset
)
end
def generate_timezone_leaderboard
return nil unless current_user&.timezone
offset = current_user.timezone_utc_offset
return nil unless offset
LeaderboardService.get(
period: @period_type,
date: start_date,
offset: offset
)
end
def find_or_generate_global_leaderboard
LeaderboardService.get(
period: @period_type,
date: start_date
@ -107,8 +32,7 @@ class LeaderboardsController < ApplicationController
def start_date
@start_date ||= case @period_type
when :weekly then Date.current.beginning_of_week
when :last_7_days then Date.current - 6.days
when :last_7_days then Date.current
else Date.current
end
end
@ -128,19 +52,17 @@ class LeaderboardsController < ApplicationController
tracked_user_ids = @leaderboard.entries.distinct.pluck(:user_id)
@user_on_leaderboard = current_user && tracked_user_ids.include?(current_user.id)
unless @user_on_leaderboard || regional_or_timezone_scope?
unless @user_on_leaderboard
@untracked_entries = calculate_untracked_entries(tracked_user_ids)
end
end
def calculate_untracked_entries(tracked_user_ids)
time_range = case @period_type
when :weekly
(start_date.beginning_of_day...(start_date + 7.days).beginning_of_day)
when :last_7_days
((start_date - 6.days).beginning_of_day...start_date.end_of_day)
((Date.current - 6.days).beginning_of_day...Date.current.end_of_day)
else
start_date.all_day
Date.current.all_day
end
Hackatime::Heartbeat.where(time: time_range)

View file

@ -95,28 +95,10 @@ class StaticPagesController < ApplicationController
end
def mini_leaderboard
use_timezone_leaderboard = current_user&.default_timezone_leaderboard
if use_timezone_leaderboard && current_user&.timezone_utc_offset
# we now doing it by default wooo
@leaderboard = LeaderboardService.get(
period: :daily,
date: Date.current,
offset: current_user.timezone_utc_offset
)
if @leaderboard&.entries&.empty?
Rails.logger.warn "[MiniLeaderboard] Regional leaderboard empty for offset #{current_user.timezone_utc_offset}"
@leaderboard = nil
end
end
if @leaderboard.nil?
@leaderboard = LeaderboardService.get(
period: :daily,
date: Date.current
)
end
@leaderboard = LeaderboardService.get(
period: :daily,
date: Date.current
)
@active_projects = Cache::ActiveProjectsJob.perform_now

View file

@ -23,14 +23,6 @@ class UsersController < ApplicationController
prepare_settings_page
render :edit, status: :unprocessable_entity
end
elsif params[:default_timezone_leaderboard].present?
if @user.update(default_timezone_leaderboard: params[:default_timezone_leaderboard] == "1")
redirect_to is_own_settings? ? my_settings_path : settings_user_path(@user),
notice: "Settings updated successfully!"
else
flash[:error] = "Failed to update settings :("
redirect_to is_own_settings? ? my_settings_path : settings_user_path(@user)
end
else
redirect_to is_own_settings? ? my_settings_path : settings_user_path(@user),
notice: "Settings updated successfully!"
@ -152,7 +144,6 @@ class UsersController < ApplicationController
:hackatime_extension_text_type,
:timezone,
:allow_public_stats_lookup,
:default_timezone_leaderboard,
:username,
)
end

View file

@ -5,13 +5,11 @@ class CleanupOldLeaderboardsJob < ApplicationJob
cutoff = 2.days.ago.beginning_of_day
old_leaderboards = Leaderboard.where("created_at < ?", cutoff)
.where(deleted_at: nil)
count = old_leaderboards.count
return if count.zero?
return if old_leaderboards.empty?
old_leaderboards.destroy_all # kerblam!
old_leaderboards.update_all(deleted_at: Time.current)
Rails.logger.info "CleanupOldLeaderboardsJob: Marked #{old_leaderboards.count} old leaderboards as deleted"
Leaderboard.where("created_at < ?", cutoff).where.not(deleted_at: nil).destroy_all
Rails.logger.info "CleanupOldLeaderboardsJob: Deleted #{count} old leaderboards"
end
end

View file

@ -9,7 +9,6 @@ class Leaderboard < ApplicationRecord
enum :period_type, {
daily: 0,
weekly: 1,
last_7_days: 2
}
@ -18,19 +17,12 @@ class Leaderboard < ApplicationRecord
end
def period_end_date
case period_type
when "weekly"
start_date + 6.days
when "last_7_days"
start_date
else
start_date
end
start_date
end
def date_range_text
if weekly?
"#{start_date.strftime('%b %d')} - #{period_end_date.strftime('%b %d, %Y')}"
if last_7_days?
"#{(start_date - 6.days).strftime('%b %d')} - #{start_date.strftime('%b %d, %Y')}"
else
start_date.strftime("%B %d, %Y")
end

View file

@ -22,14 +22,7 @@
<% if mini_leaderboard_entries&.any? %>
<div class="bg-elevated rounded-xl border border-primary p-4 shadow-lg">
<p class="text-xs italic text-muted mb-4">
<% if leaderboard.respond_to?(:scope_name) %>
<strong>Showing others in <%= link_to "your timezone", my_settings_path(anchor: "user_timezone"), data: { turbo: false }, class: "text-accent hover:text-cyan-400 transition-colors" %></strong>
<% else %>
This leaderboard is in <%= Leaderboard::GLOBAL_TIMEZONE %>.
<% if current_user && timezone_difference_in_seconds(Leaderboard::GLOBAL_TIMEZONE, current_user.timezone) != 0 %>
<%= timezone_difference_in_words(Leaderboard::GLOBAL_TIMEZONE, current_user.timezone) %>
<% end %>
<% end %>
This leaderboard shows time logged in the last 24 hours (UTC time).
</p>
<div class="space-y-2">
<% mini_leaderboard_entries.each_with_index do |entry, idx| %>

View file

@ -1,9 +1,6 @@
<div class="bg-elevated rounded-xl border border-primary p-4 shadow-lg">
<p class="text-xs italic text-muted mb-4 opacity-70">
This leaderboard is in <%= Leaderboard::GLOBAL_TIMEZONE %>.
<% if current_user && timezone_difference_in_seconds(Leaderboard::GLOBAL_TIMEZONE, current_user.timezone) != 0 %>
<%= timezone_difference_in_words(Leaderboard::GLOBAL_TIMEZONE, current_user.timezone) %>
<% end %>
This leaderboard shows time logged in the last 24 hours (UTC time).
</p>
<div class="space-y-2">
<% (current_user ? 5 : 3).times do %>

View file

@ -2,32 +2,12 @@
<div class="mb-8">
<h1 class="text-3xl font-bold text-white mb-4">Leaderboard</h1>
<% if @scope == 'regional' %>
<em class="text-muted block mb-4"><strong>Regional Leaderboard:</strong> Showing users in <%= @scope_description %></em>
<% elsif @scope == 'timezone' %>
<em class="text-muted block mb-4"><strong>Timezone Leaderboard:</strong> Showing users in <%= @scope_description %></em>
<% elsif @scope == 'global' %>
<em class="text-muted block mb-4" title="Current UTC time: <%= Time.now.utc.strftime('%H:%M:%S UTC') %>">This leaderboard runs in UTC time!</em>
<% else %>
<em class="text-muted block mb-4"><strong>Regional Leaderboard:</strong> Showing users in <%= @scope_description %></em>
<% end %>
<div class="inline-flex rounded-full p-1 mb-4">
<%= link_to "Daily", leaderboards_path(period_type: 'daily', scope: @scope),
class: "px-4 py-2 rounded-full text-sm font-medium transition-all duration-200 #{(@period_type == :daily || @period_type == :daily_timezone_normalized) ? 'bg-primary text-white' : 'text-muted hover:text-white'}", style: "background:none; border:none;" %>
<%= link_to "Weekly", leaderboards_path(period_type: 'weekly', scope: @scope),
class: "px-4 py-2 rounded-full text-sm font-medium transition-all duration-200 #{@period_type == :weekly ? 'bg-primary text-white' : 'text-muted hover:text-white'}", style: "background:none; border:none;" %>
<%= link_to "Last 7 Days", leaderboards_path(period_type: 'last_7_days', scope: @scope),
class: "px-4 py-2 rounded-full text-sm font-medium transition-all duration-200 #{@period_type == :last_7_days ? 'bg-primary text-white' : 'text-muted hover:text-white'}", style: "background:none; border:none;" %>
</div>
<div class="inline-flex rounded-full p-1 mb-4 ml-4">
<%= link_to "Timezone", leaderboards_path(period_type: @period_type, scope: 'timezone'),
class: "px-4 py-2 rounded-full text-sm font-medium transition-all duration-200 #{@scope == 'timezone' ? ' bg-primary text-white' : 'text-muted hover:text-white'}", style: "background:none; border:none;" %>
<%= link_to "Regional", leaderboards_path(period_type: @period_type, scope: 'regional'),
class: "px-4 py-2 rounded-full text-sm font-medium transition-all duration-200 #{@scope == 'regional' ? ' bg-primary text-white' : 'text-muted hover:text-white'}", style: "background:none; border:none;" %>
<%= link_to "Global", leaderboards_path(period_type: @period_type, scope: 'global'),
class: "px-4 py-2 rounded-full text-sm font-medium transition-all duration-200 #{@scope == 'global' ? ' bg-primary text-white' : 'text-muted hover:text-white'}", style: "background:none; border:none;" %>
<%= link_to "Last 24 Hours", leaderboards_path(period_type: 'daily'),
class: "px-4 py-2 rounded-full text-sm font-medium transition-all duration-200 #{@period_type == :daily ? 'bg-primary text-white' : 'text-muted bg-darkless hover:text-white'}", style: "background:none; border:none;" %>
<%= link_to "Last 7 Days", leaderboards_path(period_type: 'last_7_days'),
class: "px-4 py-2 rounded-full text-sm font-medium transition-all duration-200 #{@period_type == :last_7_days ? 'bg-primary text-white' : 'text-muted bg-darkless hover:text-white'}", style: "background:none; border:none;" %>
</div>
<% if current_user && current_user.github_uid.blank? %>
@ -41,15 +21,13 @@
<%= @leaderboard.date_range_text %>
<% if @leaderboard.finished_generating? && @leaderboard.persisted? %>
<span class="block text-xs mt-1 italic">
Updated <%= time_ago_in_words(@leaderboard.updated_at) %> ago.
<span class="italic">
- Updated <%= time_ago_in_words(@leaderboard.updated_at) %> ago.
</span>
<% end %>
<% else %>
<%= case @period_type
when :weekly
"#{Date.current.beginning_of_week.strftime('%B %d')} - #{(Date.current.beginning_of_week + 6.days).strftime('%B %d, %Y')}"
when :last_7_days
"#{(Date.current - 6.days).strftime('%B %d')} - #{Date.current.strftime('%B %d, %Y')}"
else
@ -120,7 +98,7 @@
<% else %>
<div class="py-16 text-center">
<h3 class="text-xl font-medium text-white mb-2">No data available</h3>
<p class="text-muted">Check back later for <%= @period_type == :weekly ? "this week's" : @period_type == :last_7_days ? "the last 7 days" : "today's" %> results!</p>
<p class="text-muted">Check back later for <%= @period_type == :last_7_days ? "the last 7 days" : "the last 24 hours" %> results!</p>
</div>
<% end %>
</div>

View file

@ -168,17 +168,6 @@
<% end %>
</div>
<div class="border border-primary rounded-xl p-6 bg-dark transition-all duration-200">
<div class="flex items-center gap-3 mb-4">
<div class="p-2 bg-red-600/10 rounded">
<span class="text-2xl">🏆</span>
</div>
<h2 class="text-xl font-semibold text-white" id="user_beta_features">Leaderboard Settings</h2>
</div>
<p class="text-gray-300 text-sm mb-4">Customize how you see the leaderboard</p>
<%= render "timezone_leaderboard_toggle", user: @user %>
</div>
<div class="border border-primary rounded-xl p-6 bg-dark transition-all duration-200 md:col-span-2">
<div class="flex items-center gap-3 mb-4">
<div class="p-2 bg-red-600/10 rounded">