hackatime/app/models/heartbeat_import_run.rb
Mahad Kalam 667d3a7c93
WakaTime/Hackatime v1 imports + Settings v2 (#1062)
* Imports are back!!

* Settings UI v3

* Use Inertia forms for heartbeat imports

* Update app/javascript/pages/Users/Settings/Data.svelte

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update Bundle

* Fix broken Form/Button markup in Data.svelte settings page

* Update JS deps

* Greptile fixes

* Remove dead code

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-12 21:27:10 +00:00

88 lines
2.1 KiB
Ruby

class HeartbeatImportRun < ApplicationRecord
COOLDOWN = 4.hours
TERMINAL_STATES = %w[completed failed].freeze
ACTIVE_STATES = %w[queued requesting_dump waiting_for_dump downloading_dump importing].freeze
REMOTE_SOURCE_KINDS = %w[wakatime_dump hackatime_v1_dump].freeze
belongs_to :user
encrypts :encrypted_api_key, deterministic: false
enum :source_kind, {
dev_upload: 0,
wakatime_dump: 1,
hackatime_v1_dump: 2
}
enum :state, {
queued: 0,
requesting_dump: 1,
waiting_for_dump: 2,
downloading_dump: 3,
importing: 4,
completed: 5,
failed: 6
}
validates :encrypted_api_key, presence: true, on: :create, unless: :dev_upload?
scope :latest_first, -> { order(created_at: :desc) }
scope :active_imports, -> { where(state: states.values_at(*ACTIVE_STATES)) }
scope :remote_imports, -> { where(source_kind: source_kinds.values_at(*REMOTE_SOURCE_KINDS)) }
def remote?
!dev_upload?
end
def terminal?
TERMINAL_STATES.include?(state)
end
def active_import?
ACTIVE_STATES.include?(state)
end
def cooldown_until
return nil if remote_requested_at.blank?
remote_requested_at + COOLDOWN
end
def progress_percent
return 100 if completed?
if waiting_for_dump? || downloading_dump? || requesting_dump?
return remote_percent_complete.to_f.clamp(0, 100).round
end
return 0 unless total_count.to_i.positive?
((processed_count.to_f / total_count.to_f) * 100).clamp(0, 100).round
end
def clear_sensitive_fields!
update_columns(encrypted_api_key: nil, updated_at: Time.current)
end
def self.active_for(user)
where(user: user).active_imports.latest_first.first
end
def self.latest_for(user)
where(user: user).latest_first.first
end
def self.remote_cooldown_until_for(user)
latest_remote_request = where(user: user)
.remote_imports
.where.not(remote_requested_at: nil)
.order(remote_requested_at: :desc)
.pick(:remote_requested_at)
return nil if latest_remote_request.blank?
retry_at = latest_remote_request + COOLDOWN
retry_at.future? ? retry_at : nil
end
end