2021-06-01 12:20:20 +00:00
|
|
|
# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
|
2014-12-25 23:41:00 +00:00
|
|
|
|
|
|
|
class Job < ApplicationModel
|
2017-05-02 15:21:13 +00:00
|
|
|
include ChecksClientNotification
|
|
|
|
include ChecksConditionValidation
|
2021-04-12 09:49:26 +00:00
|
|
|
include ChecksHtmlSanitized
|
2019-01-29 14:04:47 +00:00
|
|
|
include ChecksPerformValidation
|
2017-01-31 17:13:45 +00:00
|
|
|
|
2016-03-20 19:09:52 +00:00
|
|
|
include Job::Assets
|
|
|
|
|
2014-12-25 23:41:00 +00:00
|
|
|
store :timeplan
|
|
|
|
store :condition
|
2016-03-18 02:04:49 +00:00
|
|
|
store :perform
|
2015-04-27 13:42:53 +00:00
|
|
|
validates :name, presence: true
|
2014-12-25 23:41:00 +00:00
|
|
|
|
2021-08-19 12:29:28 +00:00
|
|
|
before_save :updated_matching, :update_next_run_at
|
2014-12-25 23:41:00 +00:00
|
|
|
|
2021-04-12 09:49:26 +00:00
|
|
|
sanitized_html :note
|
|
|
|
|
2017-11-17 10:39:57 +00:00
|
|
|
=begin
|
|
|
|
|
|
|
|
verify each job if needed to run (e. g. if true and times are matching) and execute it
|
|
|
|
|
|
|
|
Job.run
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
2014-12-25 23:41:00 +00:00
|
|
|
def self.run
|
2017-11-17 10:39:57 +00:00
|
|
|
start_at = Time.zone.now
|
2018-08-16 13:12:55 +00:00
|
|
|
jobs = Job.where(active: true)
|
2014-12-25 23:41:00 +00:00
|
|
|
jobs.each do |job|
|
2018-08-16 13:12:55 +00:00
|
|
|
next if !job.executable?
|
2018-10-09 06:17:41 +00:00
|
|
|
|
2017-11-17 10:39:57 +00:00
|
|
|
job.run(false, start_at)
|
|
|
|
end
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
=begin
|
|
|
|
|
|
|
|
execute a single job if needed (e. g. if true and times are matching)
|
|
|
|
|
|
|
|
job = Job.find(123)
|
|
|
|
|
|
|
|
job.run
|
2016-03-18 02:04:49 +00:00
|
|
|
|
2017-11-17 10:39:57 +00:00
|
|
|
force to run job (ignore times are matching)
|
2014-12-25 23:41:00 +00:00
|
|
|
|
2017-11-17 10:39:57 +00:00
|
|
|
job.run(true)
|
|
|
|
|
|
|
|
=end
|
|
|
|
|
|
|
|
def run(force = false, start_at = Time.zone.now)
|
2018-03-20 17:47:49 +00:00
|
|
|
logger.debug { "Execute job #{inspect}" }
|
2017-11-17 10:39:57 +00:00
|
|
|
|
2019-07-26 14:31:24 +00:00
|
|
|
tickets = nil
|
|
|
|
Transaction.execute(reset_user_id: true) do
|
|
|
|
if !executable?(start_at) && force == false
|
|
|
|
if next_run_at && next_run_at <= Time.zone.now
|
|
|
|
save!
|
|
|
|
end
|
|
|
|
return
|
2016-03-18 02:04:49 +00:00
|
|
|
end
|
2014-12-25 23:41:00 +00:00
|
|
|
|
2019-07-26 14:31:24 +00:00
|
|
|
# find tickets to change
|
|
|
|
matching = matching_count
|
|
|
|
if self.matching != matching
|
|
|
|
self.matching = matching
|
2017-11-17 10:39:57 +00:00
|
|
|
save!
|
|
|
|
end
|
|
|
|
|
2019-07-26 14:31:24 +00:00
|
|
|
if !in_timeplan?(start_at) && force == false
|
|
|
|
if next_run_at && next_run_at <= Time.zone.now
|
|
|
|
save!
|
|
|
|
end
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2020-02-20 12:34:16 +00:00
|
|
|
ticket_count, tickets = Ticket.selectors(condition, limit: 2_000, execution_time: true)
|
2014-12-25 23:41:00 +00:00
|
|
|
|
2019-07-26 14:31:24 +00:00
|
|
|
logger.debug { "Job #{name} with #{ticket_count} tickets" }
|
2014-12-25 23:41:00 +00:00
|
|
|
|
2019-07-26 14:31:24 +00:00
|
|
|
self.processed = ticket_count || 0
|
|
|
|
self.running = true
|
|
|
|
self.last_run_at = Time.zone.now
|
|
|
|
save!
|
|
|
|
end
|
2014-12-25 23:41:00 +00:00
|
|
|
|
2017-11-23 08:09:44 +00:00
|
|
|
tickets&.each do |ticket|
|
|
|
|
Transaction.execute(disable_notification: disable_notification, reset_user_id: true) do
|
2020-09-30 12:28:07 +00:00
|
|
|
ticket.perform_changes(self, 'job')
|
2014-12-25 23:41:00 +00:00
|
|
|
end
|
|
|
|
end
|
2017-11-17 10:39:57 +00:00
|
|
|
|
2019-07-26 14:31:24 +00:00
|
|
|
Transaction.execute(reset_user_id: true) do
|
|
|
|
self.running = false
|
|
|
|
self.last_run_at = Time.zone.now
|
|
|
|
save!
|
|
|
|
end
|
2014-12-25 23:41:00 +00:00
|
|
|
end
|
|
|
|
|
2017-11-17 10:39:57 +00:00
|
|
|
def executable?(start_at = Time.zone.now)
|
2016-03-18 14:29:03 +00:00
|
|
|
return false if !active
|
2016-03-18 02:04:49 +00:00
|
|
|
|
2019-07-31 08:23:48 +00:00
|
|
|
# only execute jobs older than 1 min to give admin time to make last-minute changes
|
2016-03-18 02:04:49 +00:00
|
|
|
return false if updated_at > Time.zone.now - 1.minute
|
|
|
|
|
2018-08-16 13:12:55 +00:00
|
|
|
# check if job got stuck
|
|
|
|
return false if running == true && last_run_at && Time.zone.now - 1.day < last_run_at
|
|
|
|
|
2016-03-18 02:04:49 +00:00
|
|
|
# check if jobs need to be executed
|
|
|
|
# ignore if job was running within last 10 min.
|
2017-11-17 10:39:57 +00:00
|
|
|
return false if last_run_at && last_run_at > start_at - 10.minutes
|
2016-03-18 02:04:49 +00:00
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2016-03-18 14:29:03 +00:00
|
|
|
def in_timeplan?(time = Time.zone.now)
|
2021-08-19 12:29:28 +00:00
|
|
|
Job::TimeplanCalculation.new(timeplan).contains?(time)
|
2016-03-18 02:04:49 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def matching_count
|
2020-02-20 12:34:16 +00:00
|
|
|
ticket_count, _tickets = Ticket.selectors(condition, limit: 1, execution_time: true)
|
2016-03-18 02:04:49 +00:00
|
|
|
ticket_count || 0
|
|
|
|
end
|
|
|
|
|
2016-03-18 14:29:03 +00:00
|
|
|
def next_run_at_calculate(time = Time.zone.now)
|
|
|
|
return nil if !active
|
|
|
|
|
2021-08-19 12:29:28 +00:00
|
|
|
if last_run_at && (time - last_run_at).positive?
|
|
|
|
time += 10.minutes
|
2016-03-18 14:29:03 +00:00
|
|
|
end
|
2021-08-19 12:29:28 +00:00
|
|
|
|
|
|
|
Job::TimeplanCalculation.new(timeplan).next_at(time)
|
2016-03-18 14:29:03 +00:00
|
|
|
end
|
|
|
|
|
2014-12-25 23:41:00 +00:00
|
|
|
private
|
|
|
|
|
|
|
|
def updated_matching
|
2016-03-18 02:04:49 +00:00
|
|
|
self.matching = matching_count
|
2014-12-25 23:41:00 +00:00
|
|
|
end
|
|
|
|
|
2016-03-18 14:29:03 +00:00
|
|
|
def update_next_run_at
|
|
|
|
self.next_run_at = next_run_at_calculate
|
|
|
|
end
|
|
|
|
|
2016-03-18 02:04:49 +00:00
|
|
|
def match_minutes(minutes)
|
|
|
|
return 0 if minutes < 10
|
2018-10-09 06:17:41 +00:00
|
|
|
|
2021-05-12 11:37:44 +00:00
|
|
|
"#{minutes.to_s.gsub(%r{(\d)\d}, '\\1')}0".to_i
|
2014-12-25 23:41:00 +00:00
|
|
|
end
|
2016-03-18 02:04:49 +00:00
|
|
|
|
2015-04-27 14:15:29 +00:00
|
|
|
end
|