diff --git a/app/avo/resources/sailors_log_leaderboard.rb b/app/avo/resources/sailors_log_leaderboard.rb new file mode 100644 index 0000000..c2e0867 --- /dev/null +++ b/app/avo/resources/sailors_log_leaderboard.rb @@ -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 + + diff --git a/app/controllers/avo/sailors_log_leaderboards_controller.rb b/app/controllers/avo/sailors_log_leaderboards_controller.rb new file mode 100644 index 0000000..d425e0d --- /dev/null +++ b/app/controllers/avo/sailors_log_leaderboards_controller.rb @@ -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 diff --git a/app/controllers/sailors_log_controller.rb b/app/controllers/sailors_log_controller.rb index e7c6586..577c8c8 100644 --- a/app/controllers/sailors_log_controller.rb +++ b/app/controllers/sailors_log_controller.rb @@ -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", diff --git a/app/jobs/sailors_log_leaderboard_job.rb b/app/jobs/sailors_log_leaderboard_job.rb new file mode 100644 index 0000000..683e7bd --- /dev/null +++ b/app/jobs/sailors_log_leaderboard_job.rb @@ -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 diff --git a/app/models/sailors_log.rb b/app/models/sailors_log.rb index 3ca4cad..1bd2207 100644 --- a/app/models/sailors_log.rb +++ b/app/models/sailors_log.rb @@ -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 diff --git a/app/models/sailors_log_leaderboard.rb b/app/models/sailors_log_leaderboard.rb new file mode 100644 index 0000000..69ec7f2 --- /dev/null +++ b/app/models/sailors_log_leaderboard.rb @@ -0,0 +1,2 @@ +class SailorsLogLeaderboard < ApplicationRecord +end diff --git a/db/migrate/20250223072034_create_sailors_log_leaderboards.rb b/db/migrate/20250223072034_create_sailors_log_leaderboards.rb new file mode 100644 index 0000000..00e0ebe --- /dev/null +++ b/db/migrate/20250223072034_create_sailors_log_leaderboards.rb @@ -0,0 +1,7 @@ +class CreateSailorsLogLeaderboards < ActiveRecord::Migration[8.0] + def change + create_table :sailors_log_leaderboards do |t| + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index c26cf91..e434861 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -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 diff --git a/lib/flavor_text.rb b/lib/flavor_text.rb new file mode 100644 index 0000000..3992ba9 --- /dev/null +++ b/lib/flavor_text.rb @@ -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 diff --git a/test/fixtures/sailors_log_leaderboards.yml b/test/fixtures/sailors_log_leaderboards.yml new file mode 100644 index 0000000..d7a3329 --- /dev/null +++ b/test/fixtures/sailors_log_leaderboards.yml @@ -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 diff --git a/test/models/sailors_log_leaderboard_test.rb b/test/models/sailors_log_leaderboard_test.rb new file mode 100644 index 0000000..7030615 --- /dev/null +++ b/test/models/sailors_log_leaderboard_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class SailorsLogLeaderboardTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end