Fix dispatch_timeout in leaderboard job

This commit is contained in:
Max Wofford 2025-02-23 03:07:35 -05:00
parent 27c88ab70b
commit 5cc7ae265b
11 changed files with 159 additions and 25 deletions

View file

@ -0,0 +1,13 @@
class Avo::Resources::SailorsLogLeaderboard < Avo::BaseResource
# self.includes = []
# self.attachments = []
# self.search = {
# query: -> { query.ransack(id_eq: params[:q], m: "or").result(distinct: false) }
# }
def fields
field :id, as: :id
end
end

View file

@ -0,0 +1,4 @@
# This controller has been generated to enable Rails' resource routes.
# More information on https://docs.avohq.io/3.0/controllers.html
class Avo::SailorsLogLeaderboardsController < Avo::ResourcesController
end

View file

@ -2,6 +2,10 @@ class SailorsLogController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :verify_slack_request
# allow usage of short_time_simple
include ApplicationHelper
helper_method :short_time_simple
# Handle slack commands
def create
case params[:text].downcase.strip
@ -19,26 +23,23 @@ class SailorsLogController < ApplicationController
text: ":white_check_mark: Coding notifications have been turned off in this channel."
}
when "leaderboard"
leaderboard = SailorsLog.generate_leaderboard(params[:channel_id])
message = "*:boat: Sailor's Log - Today*"
medals = [ "first_place_medal", "second_place_medal", "third_place_medal" ]
# ex.
# :first_place_medal: @Irtaza: 2h 6m → Farmworks [C#]: 125m
# :second_place_medal: @Cigan: 1h 33m → Gitracker-1 [JAVA]: 49m + Dupe [JAVA]: 41m + Lovac-Integration [JAVA]: 2m
leaderboard.each_with_index do |entry, index|
medal = medals[index] || "white_small_square"
message += "\n:#{medal}: `<@#{entry[:user_id]}>`: #{entry[:duration]}"
message += entry[:projects].map do |project|
language = project[:language_emoji] ? "#{project[:language_emoji]} #{project[:language]}" : project[:language]
"#{project[:name]} [#{language}]"
end.join(" + ")
end
# Acknowledge receipt
head :ok
puts message
render json: {
response_type: "in_channel",
text: message
}
# Send loading message first
response = HTTP.post(params[:response_url], json: {
response_type: "ephemeral",
text: ":beachball: #{FlavorText.loading_messages.sample}",
trigger_id: params[:trigger_id]
})
puts "Response: #{response.body}"
# Process in background
SailorsLogLeaderboardJob.perform_later(
params[:channel_id],
params[:response_url],
)
else
render json: {
response_type: "ephemeral",

View file

@ -0,0 +1,34 @@
class SailorsLogLeaderboardJob < ApplicationJob
queue_as :default
include ApplicationHelper
def perform(channel_id, response_url)
# Generate leaderboard
leaderboard = SailorsLog.generate_leaderboard(channel_id)
message = "*:boat: Sailor's Log - Today*"
medals = [ "first_place_medal", "second_place_medal", "third_place_medal" ]
leaderboard.each_with_index do |entry, index|
medal = medals[index] || "white_small_square"
message += "\n:#{medal}: `<@#{entry[:user_id]}>`: #{short_time_simple entry[:duration]}"
message += entry[:projects].map do |project|
language = project[:language_emoji] ? "#{project[:language_emoji]} #{project[:language]}" : project[:language]
project_entry = []
project_entry << "#{project[:name]}"
project_entry << "[#{language}]" unless language.nil?
project_entry << "#{short_time_simple project[:duration]}"
project_entry.join(" ")
end.join(" + ")
end
# Update with final message
response = HTTP.post(response_url, json: {
response_type: "in_channel",
replace_original: true,
text: message
})
puts "Response: #{response.body}"
end
end

View file

@ -46,6 +46,7 @@ class SailorsLog < ApplicationRecord
.duration_seconds
projects = project_durations.map do |project, duration|
print "project: #{project}, duration: #{duration}, language: #{most_common_languages[project]}"
{
name: project,
duration: duration,
@ -54,6 +55,8 @@ class SailorsLog < ApplicationRecord
}
end
projects = projects.filter { |project| project[:duration] > 1.minute }.sort_by { |project| -project[:duration] }
{
user_id: user_id,
duration: user_durations[user_id],
@ -63,21 +66,22 @@ class SailorsLog < ApplicationRecord
end
def self.language_emoji(language)
case language.downcase
language = language.downcase
case language
when "ruby"
":ruby:"
":#{language}:"
when "javascript"
":js:"
when "typescript"
":ts:"
when "html"
":html:"
":#{language}:"
when "java"
[ ":java:", ":java_duke:" ].sample
when "unity"
[ ":unity:", ":unity_new:" ].sample
when "c++"
":c++:"
":#{language}:"
when "c"
[ ":c:", ":c_1:" ].sample
when "rust"
@ -89,7 +93,9 @@ class SailorsLog < ApplicationRecord
when "go"
[ ":golang:", ":gopher:", ":gothonk:" ].sample
when "kotlin"
":kotlin:"
":#{language}:"
when "astro"
":#{language}:"
else
nil
end

View file

@ -0,0 +1,2 @@
class SailorsLogLeaderboard < ApplicationRecord
end

View file

@ -0,0 +1,7 @@
class CreateSailorsLogLeaderboards < ActiveRecord::Migration[8.0]
def change
create_table :sailors_log_leaderboards do |t|
t.timestamps
end
end
end

7
db/schema.rb generated
View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_02_22_082500) do
ActiveRecord::Schema[8.0].define(version: 2025_02_23_072034) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
@ -122,6 +122,11 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_22_082500) do
t.datetime "deleted_at"
end
create_table "sailors_log_leaderboards", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "sailors_log_notification_preferences", force: :cascade do |t|
t.string "slack_uid", null: false
t.string "slack_channel_id", null: false

44
lib/flavor_text.rb Normal file
View file

@ -0,0 +1,44 @@
class FlavorText
def self.loading_messages
[
"Generating leaderboard...",
"Crunching the numbers...",
"Hold tight, I'm working on it...",
"chugging the data juice",
"chugging *Stat-Cola©*, for those who want to know things™",
"that's numberwang!",
"crunching the numbers",
"munching the numbers",
"gurgling the bits",
"juggling the electrons",
"chomping the bytes",
"playing the photons on bass",
"reticulating the splines",
"rolling down data hills",
"frolicking through fields of numbers",
"skiing the data slopes",
"zooming through the cyber-pipes",
"grabbing the stats",
"switching the dependent and independent variables",
"flipping a coin to choose which axis to use",
"warming up the powerhouse of the cell",
"calculating significant figures...",
"p-hacking the n value",
"computing P = NP",
"realizing P ≠ NP",
"so, uh... come here often?",
"*powertool noises*",
"spinning violently around the y-axis",
"serializing first-time coders experiences",
"tokenizing personal experiences",
"waking up the bits",
"petting the bits",
"testing patience",
"npm installing 3 of 27594 packages",
"spinning the rgbs",
"Installing dependencies",
"shoveling the overflowed pixels",
".split() === :large_blue_circle::large_green_circle::large_yellow_circle::large_orange_circle::red_circle::large_purple_circle: "
]
end
end

View file

@ -0,0 +1,11 @@
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
# This model initially had no columns defined. If you add columns to the
# model remove the "{}" from the fixture names and add the columns immediately
# below each fixture, per the syntax in the comments below
#
one: {}
# column: value
#
two: {}
# column: value

View file

@ -0,0 +1,7 @@
require "test_helper"
class SailorsLogLeaderboardTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end