Skip to content

Commit

Permalink
Add daily buddies (#395)
Browse files Browse the repository at this point in the history
* Create a table/model to store daily pairs of buddies

* Fix RuboCop issues

* WIP: job

* Finish job

* Add Buddy model

* Wording

* Update schema from main

* Adjust cron timezones (with fugit)

* Wording

* Fix job

* Adjust crons

* Add @daily_buddy in page controller with sugar in Buddy model

* Add the front for buddies: on top of calendar, link to Slack, fallback to profile

* Never happy with crons

* Fix RuboCop issues

* Add explicit .present?
  • Loading branch information
pil0u authored Dec 3, 2023
1 parent 8ade727 commit a8e326c
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 7 deletions.
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.present? %>

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

<% if @daily_buddy.slack_id.present? %>
<%= 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

0 comments on commit a8e326c

Please sign in to comment.