mirror of
https://github.com/SrIzan10/hc-harbor.git
synced 2026-05-01 10:45:21 +00:00
Rewrite sailors log to work with user_id index (#156)
This commit is contained in:
@@ -11,7 +11,7 @@ class UsersController < ApplicationController
|
||||
@enabled_sailors_logs = SailorsLogNotificationPreference.where(
|
||||
slack_uid: @user.slack_uid,
|
||||
enabled: true,
|
||||
).where.not(slack_channel_id: "C0835AZP9GB")
|
||||
).where.not(slack_channel_id: SailorsLog::DEFAULT_CHANNELS)
|
||||
|
||||
@heartbeats_migration_jobs = @user.data_migration_jobs
|
||||
end
|
||||
|
||||
32
app/jobs/one_time/log_to_sailors_log_channel_job.rb
Normal file
32
app/jobs/one_time/log_to_sailors_log_channel_job.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
class OneTime::LogToSailorsLogChannelJob < ApplicationJob
|
||||
queue_as :default
|
||||
|
||||
def perform
|
||||
# every user should have a sailors_log &
|
||||
# every sailors_log should atleast have a notification_preference for the debug sailors_log channel
|
||||
User.where.missing(:sailors_log)
|
||||
.where.not(slack_uid: nil)
|
||||
.pluck(:slack_uid).each do |slack_uid|
|
||||
puts "creating sailors_log for #{slack_uid}"
|
||||
SailorsLog.create!(slack_uid: slack_uid)
|
||||
end
|
||||
|
||||
# Find SailorsLogs that don't have a notification preference for the debug channel
|
||||
debug_channel_id = "C0835AZP9GB"
|
||||
|
||||
# Get all SailorsLogs
|
||||
SailorsLog.find_each do |sailors_log|
|
||||
# Check if this SailorsLog already has a notification preference for the debug channel
|
||||
has_preference = sailors_log.notification_preferences.exists?(slack_channel_id: debug_channel_id)
|
||||
|
||||
# If not, create one
|
||||
unless has_preference
|
||||
puts "Creating notification preference for #{sailors_log.slack_uid}"
|
||||
sailors_log.notification_preferences.create!(
|
||||
slack_channel_id: debug_channel_id,
|
||||
enabled: true
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
12
app/jobs/one_time/reset_sailors_log_job.rb
Normal file
12
app/jobs/one_time/reset_sailors_log_job.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
class OneTime::ResetSailorsLogJob < ApplicationJob
|
||||
queue_as :default
|
||||
|
||||
def perform
|
||||
SailorsLog.find_each do |sl|
|
||||
next if sl.user.blank?
|
||||
sl.projects_summary = nil
|
||||
sl.send(:initialize_projects_summary)
|
||||
sl.save!
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -23,7 +23,10 @@ class SailorsLogNotifyJob < ApplicationJob
|
||||
|
||||
hours = project_duration / 3600
|
||||
|
||||
message = ":boat: `@#{SlackUsername.find_by_uid(slack_uid)}` just coded 1 more hour on *#{project_name}* (total: #{hours}hrs). _#{kudos_message}_"
|
||||
username = SlackUsername.find_by_uid(slack_uid)
|
||||
handle = username.blank? ? "<@#{slack_uid}>" : "@#{username}"
|
||||
|
||||
message = ":boat: `#{handle}` just coded 1 more hour on *#{project_name}* (total: #{hours}hrs). _#{kudos_message}_"
|
||||
|
||||
response = HTTP.auth("Bearer #{ENV['SAILORS_LOG_SLACK_BOT_OAUTH_TOKEN']}")
|
||||
.post("https://slack.com/api/chat.postMessage",
|
||||
|
||||
@@ -3,56 +3,63 @@ class SailorsLogPollForChangesJob < ApplicationJob
|
||||
|
||||
def perform
|
||||
puts "performing SailorsLogPollForChangesJob"
|
||||
# get all users who've coded in the last minute
|
||||
users_who_coded = Heartbeat.where("created_at > ?", 1.minutes.ago)
|
||||
.where(time: 1.minutes.ago..)
|
||||
users_who_coded = Heartbeat.with_valid_timestamps
|
||||
.where(time: 10.minutes.ago..)
|
||||
.distinct.pluck(:user_id)
|
||||
|
||||
puts "users_who_coded: #{users_who_coded}"
|
||||
slack_uids = User.where(id: users_who_coded)
|
||||
.where.not(slack_uid: nil)
|
||||
.distinct.pluck(:slack_uid)
|
||||
|
||||
# Get all of those with enabled preferences
|
||||
enabled_users = SailorsLogNotificationPreference.where(enabled: true, slack_uid: slack_uids).distinct.pluck(:slack_uid)
|
||||
slack_uids = User.where(id: users_who_coded).pluck(:slack_uid)
|
||||
puts "slack_uids: #{slack_uids}"
|
||||
|
||||
puts "enabled_users: #{enabled_users}"
|
||||
new_notifs = SailorsLog.includes(:user, :notification_preferences)
|
||||
.where(notification_preferences: { enabled: true })
|
||||
.where(slack_uid: slack_uids)
|
||||
.map { |sl| update_sailors_log(sl) }.flatten
|
||||
|
||||
logs = SailorsLog.includes(:user).where(slack_uid: enabled_users)
|
||||
notifs_to_send = SailorsLogSlackNotification.insert_all(new_notifs)
|
||||
notif_ids = notifs_to_send.result.to_a.map { |r| r["id"] }
|
||||
|
||||
user_ids = logs.map(&:user).pluck(:id)
|
||||
SailorsLogSlackNotification.where(id: notif_ids).map(&:notify_user_later!)
|
||||
end
|
||||
|
||||
puts "logs: #{logs}"
|
||||
private
|
||||
|
||||
logs.each do |log|
|
||||
# get all projects for the user with duration
|
||||
new_project_times = Heartbeat.where(user_id: user_ids)
|
||||
.group(:project)
|
||||
.duration_seconds
|
||||
|
||||
new_project_times.each do |project, new_project_duration|
|
||||
next if project.blank?
|
||||
if new_project_duration > (log.projects_summary[project] || 0) + 1.hour
|
||||
log.notification_preferences.each do |preference|
|
||||
log.notifications << SailorsLogSlackNotification.new(
|
||||
slack_uid: log.slack_uid,
|
||||
slack_channel_id: preference.slack_channel_id,
|
||||
project_name: project,
|
||||
project_duration: new_project_duration
|
||||
)
|
||||
end
|
||||
log.projects_summary[project] = new_project_duration
|
||||
end
|
||||
end
|
||||
log.save! if log.changed?
|
||||
|
||||
# if multiple notifications for the same project, only the most recent one should be sent
|
||||
log.notifications.group_by(&:project_name).each do |project_name, notifications|
|
||||
if notifications.size > 1
|
||||
# Keep the most recent notification, destroy the older ones
|
||||
notifications.sort_by(&:created_at)[0..-2].each(&:destroy)
|
||||
end
|
||||
def update_sailors_log(sailors_log)
|
||||
project_updates = []
|
||||
project_durations = Heartbeat.where(user_id: sailors_log.user.id)
|
||||
.group(:project).duration_seconds
|
||||
project_durations.each do |k, v|
|
||||
old_duration = sailors_log.projects_summary[k] || 0
|
||||
new_duration = v
|
||||
puts "#{k}| old_duration: #{old_duration}, new_duration: #{new_duration}"
|
||||
if old_duration / 3600 < new_duration / 3600
|
||||
puts "updating #{k} to #{new_duration}"
|
||||
sailors_log.projects_summary[k] = new_duration
|
||||
project_updates << { project: k, duration: new_duration }
|
||||
end
|
||||
end
|
||||
|
||||
notifications_to_create = []
|
||||
if sailors_log.changed?
|
||||
sailors_log.notification_preferences.each do |np|
|
||||
project_updates.map do |pu|
|
||||
puts "np: #{np.inspect}, pu: #{pu.inspect}"
|
||||
notifications_to_create << {
|
||||
slack_uid: sailors_log.user.slack_uid,
|
||||
slack_channel_id: np.slack_channel_id,
|
||||
project_name: pu[:project],
|
||||
project_duration: pu[:duration]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
sailors_log.save!
|
||||
end
|
||||
|
||||
notifications_to_create
|
||||
end
|
||||
end
|
||||
|
||||
# optimizations?
|
||||
# - index heartbeats on user_id + project so we can call duration_seconds grouping by both
|
||||
# - investigate lookup by slack_uid, maybe index or computed field?
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
class SailorsLog < ApplicationRecord
|
||||
validates :slack_uid, presence: true, uniqueness: true
|
||||
validates :projects_summary, presence: true
|
||||
|
||||
before_validation :initialize_projects_summary
|
||||
after_create :ensure_notification_preference_for_debug_channel
|
||||
|
||||
belongs_to :user, foreign_key: :slack_uid, primary_key: :slack_uid
|
||||
|
||||
@@ -16,10 +16,21 @@ class SailorsLog < ApplicationRecord
|
||||
foreign_key: :slack_uid,
|
||||
primary_key: :slack_uid
|
||||
|
||||
DEFAULT_CHANNELS = %w[C0835AZP9GB]
|
||||
|
||||
private
|
||||
|
||||
def initialize_projects_summary
|
||||
return unless projects_summary.blank?
|
||||
self.projects_summary = Heartbeat.where(user_id: slack_uid).group(:project).duration_seconds
|
||||
return if projects_summary.present?
|
||||
self.projects_summary = Heartbeat.where(user_id: user.id)
|
||||
.group(:project).duration_seconds
|
||||
self.projects_summary ||= {}
|
||||
end
|
||||
|
||||
def ensure_notification_preference_for_debug_channel
|
||||
return if notification_preferences.any? { |np| SailorsLog::DEFAULT_CHANNELS.include?(np.slack_channel_id) && np.enabled }
|
||||
SailorsLog::DEFAULT_CHANNELS.each do |channel_id|
|
||||
notification_preferences.create!(slack_channel_id: channel_id, enabled: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,4 +4,8 @@ class SailorsLogSlackNotification < ApplicationRecord
|
||||
|
||||
SailorsLogNotifyJob.perform_now(self.id)
|
||||
end
|
||||
|
||||
def notify_user_later!
|
||||
SailorsLogNotifyJob.perform_later(self.id)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -23,6 +23,11 @@ class User < ApplicationRecord
|
||||
|
||||
has_many :api_keys
|
||||
|
||||
has_one :sailors_log,
|
||||
foreign_key: :slack_uid,
|
||||
primary_key: :slack_uid,
|
||||
class_name: "SailorsLog"
|
||||
|
||||
delegate :streak_days, :streak_days_formatted, to: :heartbeats
|
||||
|
||||
enum :hackatime_extension_text_type, {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
class SlackUsername
|
||||
def self.find_by_uid(uid)
|
||||
cached_name = Rails.cache.fetch("slack_username_#{uid}", expires_in: 1.day) do
|
||||
key = "slack_username_#{uid}"
|
||||
|
||||
cached_name = Rails.cache.fetch(key, expires_in: 1.day) do
|
||||
response = HTTP.headers(Authorization: "Bearer #{ENV.fetch("SAILORS_LOG_SLACK_BOT_OAUTH_TOKEN")}").get("https://slack.com/api/users.info?user=#{uid}")
|
||||
data = JSON.parse(response.body)
|
||||
|
||||
@@ -10,6 +12,8 @@ class SlackUsername
|
||||
name
|
||||
end
|
||||
|
||||
Rails.cache.delete(key) unless cached_name.present?
|
||||
|
||||
cached_name
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user