Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add daily buddies #395

Merged
merged 17 commits into from
Dec 3, 2023
1 change: 1 addition & 0 deletions app/controllers/pages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def calendar
}
end

@daily_buddy = Buddy.of_the_day(current_user)
@next_puzzle_time = Aoc.next_puzzle_time
@now = Aoc.event_timezone.now
end
Expand Down
67 changes: 67 additions & 0 deletions app/jobs/buddies/generate_daily_pairs_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true

module Buddies
class GenerateDailyPairsJob < ApplicationJob
queue_as :default

def perform(day)
@day = day

if Buddy.exists?(day:)
Rails.logger.info "Daily pairs already exist for day #{day}"
return
end

retrieve_confirmed_users
handle_odd_number_of_users
generate_possible_pairs_of_buddies

@possible_pairs.shuffle!

pick_valid_pairs_of_buddies
handle_unpaired_users

insert_generated_buddies
end

private

def retrieve_confirmed_users
@user_ids = User.confirmed.order(:id).pluck(:id)
end

def handle_odd_number_of_users
@user_ids.pop if @user_ids.size.odd?
end

def generate_possible_pairs_of_buddies
all_pairs = @user_ids.combination(2).to_set
past_buddies = Buddy.pluck(:id_1, :id_2).to_set

@possible_pairs = (all_pairs - past_buddies).to_a
end

def pick_valid_pairs_of_buddies
@users_to_match = Set.new(@user_ids)
@buddies = []

# Iterate once over possible pairs to find matches
@possible_pairs.each do |pair|
# If a pair contains two available IDs, it's a match!
if pair.all? { |id| @users_to_match.include?(id) }
@buddies << pair
pair.each { |id| @users_to_match.delete(id) }
end
end
end

def handle_unpaired_users
@buddies += @users_to_match.to_a.shuffle.each_slice(2).to_a
end

def insert_generated_buddies
Buddy.insert_all!(@buddies.map { |a, b| { day: @day, id_1: a, id_2: b } })
Rails.logger.info "Buddies successfully generated for day #{@day}"
end
end
end
13 changes: 13 additions & 0 deletions app/models/buddy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

class Buddy < ApplicationRecord
scope :of_today, -> { where(day: Aoc.latest_day) }
scope :of_user, ->(user_id) { where("id_1 = ? OR id_2 = ?", user_id, user_id) }

def self.of_the_day(user)
buddy_pair = of_today.of_user(user.id).first
daily_buddy_id = user.id == buddy_pair.id_1 ? buddy_pair.id_2 : buddy_pair.id_1 if buddy_pair

User.find_by(id: daily_buddy_id)
end
end
14 changes: 14 additions & 0 deletions app/views/pages/calendar.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@
Welcome, <%= link_to current_user.username, profile_path(current_user.uid), class: "strong hover:text-gold" %>
</p>

<% if @daily_buddy %>

<p class="text-center">
Your daily buddy is

<% if @daily_buddy.slack_id %>
<%= link_to "@#{@daily_buddy.slack_username}", @daily_buddy.slack_link, target: :_blank, rel: :noopener, class: "link-explicit link-slack" %>
<% else %>
<%= link_to @daily_buddy.username, profile_path(@daily_buddy.uid), class: "link-explicit" %>
<% end %>
</p>

<% end %>

<div class="mx-auto my-4 w-max grid grid-cols-5 gap-2 lg:gap-3">
<%= render Calendar::AdventDayComponent.with_collection(@advent_days, now: @now) %>
</div>
Expand Down
19 changes: 13 additions & 6 deletions config/initializers/good_job.rb
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
# frozen_string_literal: true

require "aoc"

Rails.application.configure do
config.good_job = {
execution_mode: :external,
max_threads: 5,
shutdown_timeout: 30,
enable_cron: true,
cron: {
refresh_completions: {
cron: "*/10 * 1-30 11-12 *", # every 10th minute between November 1st and December 30th
refresh_completions: { # every 10 minutes between November 1st and December 30th
cron: "*/10 * 1-30 11-12 *",
class: "InsertNewCompletionsJob"
},
auto_cleanup: {
cron: "55 5 * * *", # every day at 5:55
auto_cleanup: { # every puzzle day, just before a new puzzle
cron: "55 23 1-25 12 * America/New_York",
class: "Cache::CleanupJob"
},
lock_time_achievements: {
cron: "30 18 9 12 *", # 9th December at 18:30
generate_buddies: { # every puzzle day, just after a new puzzle
cron: "5 0 1-25 12 * America/New_York",
class: "Buddies::GenerateDailyPairsJob",
args: [Aoc.latest_day]
},
unlock_lock_time_achievements: { # once at lock time
cron: "30 17 8 12 * Europe/Paris",
class: "Achievements::LockTimeJob"
}
}
Expand Down
13 changes: 13 additions & 0 deletions db/migrate/20231201004728_create_buddies.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

class CreateBuddies < ActiveRecord::Migration[7.1]
def change
create_table :buddies do |t|
t.integer :id_1
t.integer :id_2
t.integer :day

t.timestamps
end
end
end
10 changes: 9 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.1].define(version: 2023_11_30_162723) do
ActiveRecord::Schema[7.1].define(version: 2023_12_01_004728) do
# These are extensions that must be enabled in order to support this database
enable_extension "citext"
enable_extension "pgcrypto"
Expand Down Expand Up @@ -89,6 +89,14 @@
t.index ["creator_id"], name: "index_blazer_queries_on_creator_id"
end

create_table "buddies", force: :cascade do |t|
t.datetime "created_at", null: false
t.integer "day"
t.integer "id_1"
t.integer "id_2"
t.datetime "updated_at", null: false
end

create_table "cities", force: :cascade do |t|
t.datetime "created_at", null: false
t.string "name"
Expand Down