slight updates for weekly leaderboards

This commit is contained in:
Karthik Sankar 2025-03-12 11:19:23 +00:00 committed by Max Wofford
parent fd6b4ea720
commit fa416b3842
5 changed files with 102 additions and 14 deletions

View file

@ -52,4 +52,31 @@
margin: -4px 0;
font-weight: bold;
letter-spacing: 2px;
}
}
.period-toggle {
display: inline-flex;
background-color: #f1f1f1;
border-radius: 999px;
padding: 4px;
margin-bottom: 16px;
}
.period-toggle-btn {
padding: 8px 16px;
border-radius: 999px;
text-decoration: none;
color: #444;
font-weight: 500;
transition: all 0.2s ease;
}
.period-toggle-btn.active {
background-color: var(--primary-color);
color: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.period-toggle-btn:hover:not(.active) {
background-color: #e0e0e0;
}

View file

@ -1,9 +1,22 @@
class LeaderboardsController < ApplicationController
def index
@leaderboard = Leaderboard.find_by(start_date: Date.current, deleted_at: nil)
@period_type = (params[:period_type] || 'daily').to_sym
@period_type = :daily unless [:daily, :weekly].include?(@period_type)
start_date = if @period_type == :weekly
Date.current.beginning_of_week
else
Date.current
end
@leaderboard = Leaderboard.find_by(
start_date: start_date,
period_type: @period_type,
deleted_at: nil
)
if @leaderboard.nil?
LeaderboardUpdateJob.perform_later
LeaderboardUpdateJob.perform_later(start_date, @period_type)
flash.now[:notice] = "Leaderboard is being updated..."
else
@entries = @leaderboard.entries
@ -14,9 +27,14 @@ class LeaderboardsController < ApplicationController
@user_on_leaderboard = current_user && tracked_user_ids.include?(current_user.id)
unless @user_on_leaderboard
today = Time.current
time_range = if @period_type == :weekly
(start_date.beginning_of_day...(start_date + 7.days).beginning_of_day)
else
Time.current
end
@untracked_entries = Hackatime::Heartbeat
.where(time: today.beginning_of_day..today.end_of_day)
.where(time: time_range)
.distinct
.pluck(:user_id)
.count { |user_id| !tracked_user_ids.include?(user_id) }

View file

@ -6,25 +6,40 @@ class LeaderboardUpdateJob < ApplicationJob
# Limits concurrency to 1 job per date
good_job_control_concurrency_with(
key: -> { arguments.first || Date.current.to_s },
key: -> { "#{arguments[0] || Date.current.to_s}_#{arguments[1] || 'daily'}" },
total: 1,
drop: true
)
def perform(date = Date.current)
def perform(date = Date.current, period_type = :daily)
parsed_date = date.is_a?(Date) ? date : Date.parse(date.to_s)
leaderboard = Leaderboard.create!(start_date: parsed_date)
period_type = period_type.to_sym
if period_type == :weekly
parsed_date = parsed_date.beginning_of_week
end
leaderboard = Leaderboard.create!(
start_date: parsed_date,
period_type: period_type
)
# Get list of valid user IDs from our database
valid_user_ids = User.pluck(:id)
return if valid_user_ids.empty?
date_range = if period_type == :weekly
(parsed_date.beginning_of_day...(parsed_date + 7.days).beginning_of_day)
else
parsed_date.all_day
end
ActiveRecord::Base.transaction do
valid_user_ids.each_slice(BATCH_SIZE) do |batch_user_ids|
entries_data = Heartbeat.where(user_id: batch_user_ids)
.where(time: parsed_date.all_day)
.group(:user_id)
.duration_seconds
.where(time: date_range)
.group(:user_id)
.duration_seconds
entries_data = entries_data.filter { |_, total_seconds| total_seconds > 60 }
@ -45,8 +60,10 @@ class LeaderboardUpdateJob < ApplicationJob
leaderboard.finished_generating_at = Time.current
leaderboard.save!
# Delete previous leaderboard entries from today
Leaderboard.where.not(id: leaderboard.id).where(start_date: parsed_date).where(deleted_at: nil).update_all(deleted_at: Time.current)
Leaderboard.where.not(id: leaderboard.id)
.where(start_date: parsed_date, period_type: period_type)
.where(deleted_at: nil)
.update_all(deleted_at: Time.current)
rescue => e
Rails.logger.error "Failed to update current leaderboard: #{e.message}"
raise

View file

@ -4,8 +4,29 @@ class Leaderboard < ApplicationRecord
dependent: :destroy
validates :start_date, presence: true
enum :period_type, {
daily: 0,
weekly: 1
}
def finished_generating?
finished_generating_at.present?
end
end
def period_end_date
if weekly?
start_date + 6.days
else
start_date
end
end
def date_range_text
if weekly?
"#{start_date.strftime('%b %d')} - #{period_end_date.strftime('%b %d, %Y')}"
else
start_date.strftime("%B %d, %Y")
end
end
end

View file

@ -0,0 +1,5 @@
class AddPeriodTypeToLeaderboards < ActiveRecord::Migration[8.0]
def change
add_column :leaderboards, :period_type, :integer, default: 0, null: false
end
end