mirror of
https://github.com/System-End/hackatime.git
synced 2026-04-19 23:32:53 +00:00
Initial code for replicating hackclub/sailors-log
This commit is contained in:
parent
7aa2068b1e
commit
cfd38a87eb
21 changed files with 287 additions and 1 deletions
14
app/avo/resources/sailors_log.rb
Normal file
14
app/avo/resources/sailors_log.rb
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
class Avo::Resources::SailorsLog < 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
|
||||
field :slack_uid, as: :text
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
16
app/avo/resources/sailors_log_notification_preference.rb
Normal file
16
app/avo/resources/sailors_log_notification_preference.rb
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
class Avo::Resources::SailorsLogNotificationPreference < 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
|
||||
field :slack_uid, as: :text
|
||||
field :enabled, as: :boolean
|
||||
field :slack_channel_id, as: :text
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
16
app/avo/resources/sailors_log_slack_notification.rb
Normal file
16
app/avo/resources/sailors_log_slack_notification.rb
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
class Avo::Resources::SailorsLogSlackNotification < 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
|
||||
field :slack_uid, as: :text
|
||||
field :slack_channel_id, as: :text
|
||||
field :project_name, as: :text
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
|
@ -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::SailorsLogNotificationPreferencesController < Avo::ResourcesController
|
||||
end
|
||||
|
|
@ -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::SailorsLogSlackNotificationsController < Avo::ResourcesController
|
||||
end
|
||||
4
app/controllers/avo/sailors_logs_controller.rb
Normal file
4
app/controllers/avo/sailors_logs_controller.rb
Normal 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::SailorsLogsController < Avo::ResourcesController
|
||||
end
|
||||
37
app/jobs/sailors_log_poll_for_changes_job.rb
Normal file
37
app/jobs/sailors_log_poll_for_changes_job.rb
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
class SailorsLogPollForChangesJob < ApplicationJob
|
||||
queue_as :default
|
||||
|
||||
def perform
|
||||
# Get all users with enabled preferences
|
||||
slack_ids = SailorsLogNotificationPreference.where(enabled: true).distinct.pluck(:slack_uid)
|
||||
|
||||
# for each user, check if their logs have changed
|
||||
logs = SailorsLog.where(slack_uid: slack_ids).includes(:notification_preferences)
|
||||
logs.each do |log|
|
||||
# get all projects for the user
|
||||
projects = Heartbeat.today.where(user_id: log.slack_uid).distinct.pluck(:project)
|
||||
|
||||
new_notification = []
|
||||
|
||||
projects.each do |project|
|
||||
new_project_time = Heartbeat.where(user_id: log.slack_uid, project: project).duration_seconds
|
||||
if new_project_time > (log.projects_summary[project] || 0) + 1.hour
|
||||
# create a new SailorsLogSlackNotification
|
||||
log.notification_preferences.each do |preference|
|
||||
new_notification << {
|
||||
slack_uid: log.slack_uid,
|
||||
slack_channel_id: preference.slack_channel_id,
|
||||
project_name: project,
|
||||
project_duration: new_project_time
|
||||
}
|
||||
end
|
||||
log.projects_summary[project] = new_project_time
|
||||
end
|
||||
end
|
||||
ActiveRecord::Base.transaction do
|
||||
SailorsLogSlackNotification.insert_all(new_notification)
|
||||
log.save! if log.changed?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
40
app/jobs/sailors_log_slack_notification_job.rb
Normal file
40
app/jobs/sailors_log_slack_notification_job.rb
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
class SailorsLogSlackNotificationJob < ApplicationJob
|
||||
queue_as :default
|
||||
|
||||
def perform(sailors_log_slack_notification)
|
||||
slack_uid = sailors_log_slack_notification.slack_uid
|
||||
slack_channel_id = sailors_log_slack_notification.slack_channel_id
|
||||
project_name = sailors_log_slack_notification.project_name
|
||||
project_duration = sailors_log_slack_notification.project_duration
|
||||
|
||||
kudos_message = [
|
||||
"Great work!",
|
||||
"Nice job!",
|
||||
"Amazing!",
|
||||
"Fantastic!",
|
||||
"Excellent!",
|
||||
"Awesome!",
|
||||
"Well done!",
|
||||
"Wahoo!",
|
||||
"Way to go!"
|
||||
].sample
|
||||
|
||||
hours = project_duration / 3600
|
||||
|
||||
message = ":boat: <@#{slack_uid}> just coded 1 more hour on #{project_name} (total: #{hours}hrs). #{kudos_message}"
|
||||
|
||||
response = HTTP.auth("Bearer #{ENV['SLACK_BOT_TOKEN']}")
|
||||
.post("https://slack.com/api/chat.postMessage",
|
||||
json: {
|
||||
channel: slack_channel_id,
|
||||
text: message
|
||||
})
|
||||
|
||||
response_data = JSON.parse(response.body)
|
||||
if response_data["ok"]
|
||||
sailors_log_slack_notification.update(sent: true)
|
||||
else
|
||||
Rails.logger.error("Failed to send Slack notification: #{response_data["error"]}")
|
||||
end
|
||||
end
|
||||
end
|
||||
24
app/models/sailors_log.rb
Normal file
24
app/models/sailors_log.rb
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
class SailorsLog < ApplicationRecord
|
||||
validates :slack_uid, presence: true, uniqueness: true
|
||||
after_create :initialize_projects_summary
|
||||
|
||||
has_many :notification_preferences,
|
||||
class_name: "SailorsLogNotificationPreference",
|
||||
foreign_key: :slack_uid,
|
||||
primary_key: :slack_uid
|
||||
|
||||
has_many :notifications,
|
||||
class_name: "SailorsLogSlackNotification",
|
||||
foreign_key: :slack_uid,
|
||||
primary_key: :slack_uid
|
||||
|
||||
private
|
||||
|
||||
def initialize_projects_summary
|
||||
self.projects_summary = {}
|
||||
Heartbeat.where(user_id: slack_uid).distinct.pluck(:project).each do |project|
|
||||
self.projects_summary[project] = Heartbeat.where(user_id: slack_uid, project: project).duration_seconds
|
||||
end
|
||||
save! if changed?
|
||||
end
|
||||
end
|
||||
6
app/models/sailors_log_notification_preference.rb
Normal file
6
app/models/sailors_log_notification_preference.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
class SailorsLogNotificationPreference < ApplicationRecord
|
||||
belongs_to :sailors_log,
|
||||
class_name: "SailorsLog",
|
||||
foreign_key: :slack_uid,
|
||||
primary_key: :slack_uid
|
||||
end
|
||||
11
app/models/sailors_log_slack_notification.rb
Normal file
11
app/models/sailors_log_slack_notification.rb
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
class SailorsLogSlackNotification < ApplicationRecord
|
||||
after_create :notify_user
|
||||
|
||||
private
|
||||
|
||||
def notify_user
|
||||
return if sent?
|
||||
|
||||
SailorsLogSlackNotificationJob.perform_later(self)
|
||||
end
|
||||
end
|
||||
10
db/migrate/20250222031955_create_sailors_logs.rb
Normal file
10
db/migrate/20250222031955_create_sailors_logs.rb
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
class CreateSailorsLogs < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
create_table :sailors_logs do |t|
|
||||
t.string :slack_uid, null: false
|
||||
t.jsonb :projects_summary, null: false, default: {}
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
class CreateSailorsLogNotificationPreferences < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
create_table :sailors_log_notification_preferences do |t|
|
||||
t.string :slack_uid, null: false
|
||||
t.string :slack_channel_id, null: false
|
||||
t.boolean :enabled, null: false, default: true
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
class CreateSailorsLogSlackNotifications < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
create_table :sailors_log_slack_notifications do |t|
|
||||
t.string :slack_uid, null: false
|
||||
t.string :slack_channel_id, null: false
|
||||
t.string :project_name, null: false
|
||||
t.integer :project_duration, null: false
|
||||
|
||||
t.boolean :sent, null: false, default: false
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
||||
27
db/schema.rb
generated
27
db/schema.rb
generated
|
|
@ -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_005401) do
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_02_22_032930) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_catalog.plpgsql"
|
||||
|
||||
|
|
@ -122,6 +122,31 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_22_005401) do
|
|||
t.datetime "deleted_at"
|
||||
end
|
||||
|
||||
create_table "sailors_log_notification_preferences", force: :cascade do |t|
|
||||
t.string "slack_uid", null: false
|
||||
t.string "slack_channel_id", null: false
|
||||
t.boolean "enabled", default: true, null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table "sailors_log_slack_notifications", force: :cascade do |t|
|
||||
t.string "slack_uid", null: false
|
||||
t.string "slack_channel_id", null: false
|
||||
t.string "project_name", null: false
|
||||
t.integer "project_duration", null: false
|
||||
t.boolean "sent", default: false, null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table "sailors_logs", force: :cascade do |t|
|
||||
t.string "slack_uid", null: false
|
||||
t.jsonb "projects_summary", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table "users", force: :cascade do |t|
|
||||
t.string "slack_uid", null: false
|
||||
t.string "email", null: false
|
||||
|
|
|
|||
11
test/fixtures/sailors_log_notification_preferences.yml
vendored
Normal file
11
test/fixtures/sailors_log_notification_preferences.yml
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
|
||||
|
||||
one:
|
||||
slack_uid: MyString
|
||||
enabled: false
|
||||
slack_channel_id: MyString
|
||||
|
||||
two:
|
||||
slack_uid: MyString
|
||||
enabled: false
|
||||
slack_channel_id: MyString
|
||||
11
test/fixtures/sailors_log_slack_notifications.yml
vendored
Normal file
11
test/fixtures/sailors_log_slack_notifications.yml
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
|
||||
|
||||
one:
|
||||
slack_uid: MyString
|
||||
slack_channel_id: MyString
|
||||
project_name: MyString
|
||||
|
||||
two:
|
||||
slack_uid: MyString
|
||||
slack_channel_id: MyString
|
||||
project_name: MyString
|
||||
7
test/fixtures/sailors_logs.yml
vendored
Normal file
7
test/fixtures/sailors_logs.yml
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
|
||||
|
||||
one:
|
||||
slack_uid: MyString
|
||||
|
||||
two:
|
||||
slack_uid: MyString
|
||||
7
test/models/sailors_log_notification_preference_test.rb
Normal file
7
test/models/sailors_log_notification_preference_test.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
require "test_helper"
|
||||
|
||||
class SailorsLogNotificationPreferenceTest < ActiveSupport::TestCase
|
||||
# test "the truth" do
|
||||
# assert true
|
||||
# end
|
||||
end
|
||||
7
test/models/sailors_log_slack_notification_test.rb
Normal file
7
test/models/sailors_log_slack_notification_test.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
require "test_helper"
|
||||
|
||||
class SailorsLogSlackNotificationTest < ActiveSupport::TestCase
|
||||
# test "the truth" do
|
||||
# assert true
|
||||
# end
|
||||
end
|
||||
7
test/models/sailors_log_test.rb
Normal file
7
test/models/sailors_log_test.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
require "test_helper"
|
||||
|
||||
class SailorsLogTest < ActiveSupport::TestCase
|
||||
# test "the truth" do
|
||||
# assert true
|
||||
# end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue