2014-12-25 23:41:00 +00:00
|
|
|
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
|
|
|
|
|
|
|
|
class Job < ApplicationModel
|
2016-03-20 19:09:52 +00:00
|
|
|
load 'job/assets.rb'
|
|
|
|
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
|
|
|
|
2016-03-18 14:29:03 +00:00
|
|
|
before_create :updated_matching, :update_next_run_at
|
|
|
|
before_update :updated_matching, :update_next_run_at
|
2014-12-25 23:41:00 +00:00
|
|
|
|
2015-02-01 12:08:11 +00:00
|
|
|
notify_clients_support
|
2014-12-25 23:41:00 +00:00
|
|
|
|
|
|
|
def self.run
|
2016-03-18 02:04:49 +00:00
|
|
|
jobs = Job.where(active: true, running: false)
|
2014-12-25 23:41:00 +00:00
|
|
|
jobs.each do |job|
|
2016-03-18 02:04:49 +00:00
|
|
|
logger.debug "Execute job #{job.inspect}"
|
|
|
|
|
|
|
|
next if !job.executable?
|
2014-12-25 23:41:00 +00:00
|
|
|
|
2016-03-18 02:04:49 +00:00
|
|
|
matching = job.matching_count
|
|
|
|
if job.matching != matching
|
|
|
|
job.matching = matching
|
|
|
|
job.save
|
|
|
|
end
|
2014-12-25 23:41:00 +00:00
|
|
|
|
2016-03-18 02:04:49 +00:00
|
|
|
next if !job.in_timeplan?
|
2014-12-25 23:41:00 +00:00
|
|
|
|
2016-03-18 02:04:49 +00:00
|
|
|
# find tickets to change
|
|
|
|
ticket_count, tickets = Ticket.selectors(job.condition, 2_000)
|
2014-12-25 23:41:00 +00:00
|
|
|
|
2016-03-18 02:04:49 +00:00
|
|
|
logger.debug "Job #{job.name} with #{ticket_count} tickets"
|
2014-12-25 23:41:00 +00:00
|
|
|
|
2016-03-18 02:04:49 +00:00
|
|
|
job.processed = ticket_count || 0
|
|
|
|
job.running = true
|
|
|
|
job.save
|
2014-12-25 23:41:00 +00:00
|
|
|
|
2016-03-18 02:04:49 +00:00
|
|
|
if tickets
|
|
|
|
tickets.each do |ticket|
|
|
|
|
|
2016-03-18 14:29:03 +00:00
|
|
|
# use transaction
|
|
|
|
ActiveRecord::Base.transaction do
|
|
|
|
UserInfo.current_user_id = 1
|
|
|
|
|
|
|
|
logger.debug "Perform job #{job.perform.inspect} in Ticket.find(#{ticket.id})"
|
|
|
|
changed = false
|
|
|
|
job.perform.each do |key, value|
|
|
|
|
(object_name, attribute) = key.split('.', 2)
|
|
|
|
raise "Unable to update object #{object_name}.#{attribute}, only can update tickets!" if object_name != 'ticket'
|
|
|
|
|
|
|
|
next if ticket[attribute].to_s == value['value'].to_s
|
|
|
|
changed = true
|
|
|
|
|
|
|
|
ticket[attribute] = value['value']
|
|
|
|
logger.debug "set #{object_name}.#{attribute} = #{value['value'].inspect}"
|
|
|
|
end
|
|
|
|
next if !changed
|
|
|
|
ticket.save
|
|
|
|
|
|
|
|
# execute ticket notification events
|
|
|
|
if !job.disable_notification
|
|
|
|
Observer::Ticket::Notification.transaction
|
|
|
|
end
|
2016-03-18 02:04:49 +00:00
|
|
|
end
|
2014-12-25 23:41:00 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-03-18 02:04:49 +00:00
|
|
|
job.running = false
|
2015-05-08 10:20:33 +00:00
|
|
|
job.last_run_at = Time.zone.now
|
2014-12-25 23:41:00 +00:00
|
|
|
job.save
|
|
|
|
end
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2016-03-18 02:04:49 +00:00
|
|
|
def executable?
|
2016-03-18 14:29:03 +00:00
|
|
|
return false if !active
|
2016-03-18 02:04:49 +00:00
|
|
|
|
|
|
|
# only execute jobs, older then 1 min, to give admin posibility to change
|
|
|
|
return false if updated_at > Time.zone.now - 1.minute
|
|
|
|
|
|
|
|
# check if jobs need to be executed
|
|
|
|
# ignore if job was running within last 10 min.
|
|
|
|
return false if last_run_at && last_run_at > Time.zone.now - 10.minutes
|
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2016-03-18 14:29:03 +00:00
|
|
|
def in_timeplan?(time = Time.zone.now)
|
2016-03-18 02:04:49 +00:00
|
|
|
day_map = {
|
|
|
|
0 => 'Sun',
|
|
|
|
1 => 'Mon',
|
|
|
|
2 => 'Tue',
|
|
|
|
3 => 'Wed',
|
|
|
|
4 => 'Thu',
|
|
|
|
5 => 'Fri',
|
|
|
|
6 => 'Sat',
|
|
|
|
}
|
|
|
|
|
|
|
|
# check day
|
|
|
|
return false if !timeplan['days']
|
|
|
|
return false if !timeplan['days'][day_map[time.wday]]
|
|
|
|
|
|
|
|
# check hour
|
|
|
|
return false if !timeplan['hours']
|
|
|
|
return false if !timeplan['hours'][time.hour.to_s] && !timeplan['hours'][time.hour]
|
|
|
|
|
|
|
|
# check min
|
|
|
|
return false if !timeplan['minutes']
|
|
|
|
return false if !timeplan['minutes'][match_minutes(time.min).to_s] && !timeplan['minutes'][match_minutes(time.min)]
|
|
|
|
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
def matching_count
|
|
|
|
ticket_count, tickets = Ticket.selectors(condition, 1)
|
|
|
|
ticket_count || 0
|
|
|
|
end
|
|
|
|
|
2016-03-18 14:29:03 +00:00
|
|
|
def next_run_at_calculate(time = Time.zone.now)
|
|
|
|
if last_run_at
|
|
|
|
diff = time - last_run_at
|
|
|
|
if diff > 0
|
|
|
|
time = time + 10.minutes
|
|
|
|
end
|
|
|
|
end
|
|
|
|
day_map = {
|
|
|
|
0 => 'Sun',
|
|
|
|
1 => 'Mon',
|
|
|
|
2 => 'Tue',
|
|
|
|
3 => 'Wed',
|
|
|
|
4 => 'Thu',
|
|
|
|
5 => 'Fri',
|
|
|
|
6 => 'Sat',
|
|
|
|
}
|
|
|
|
return nil if !active
|
|
|
|
return nil if !timeplan['days']
|
|
|
|
return nil if !timeplan['hours']
|
|
|
|
return nil if !timeplan['minutes']
|
|
|
|
|
|
|
|
# loop week days
|
|
|
|
(0..7).each do |day_counter|
|
|
|
|
time_to_check = nil
|
|
|
|
day_to_check = if day_counter == 0
|
|
|
|
time
|
|
|
|
else
|
|
|
|
time + 1.day
|
|
|
|
end
|
|
|
|
if !timeplan['days'][day_map[day_to_check.wday]]
|
|
|
|
|
|
|
|
# start on next day at 00:00:00
|
|
|
|
time = day_to_check - day_to_check.sec.seconds
|
|
|
|
time = time - day_to_check.min.minutes
|
|
|
|
time = time - day_to_check.hour.hours
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
|
|
|
min = day_to_check.min
|
|
|
|
if min < 9
|
|
|
|
min = 0
|
|
|
|
elsif min < 20
|
|
|
|
min = 10
|
|
|
|
elsif min < 30
|
|
|
|
min = 20
|
|
|
|
elsif min < 40
|
|
|
|
min = 30
|
|
|
|
elsif min < 50
|
|
|
|
min = 40
|
|
|
|
elsif min < 60
|
|
|
|
min = 50
|
|
|
|
end
|
|
|
|
|
|
|
|
# move to [0-5]0:00 time stamps
|
|
|
|
day_to_check = day_to_check - day_to_check.min.minutes + min.minutes
|
|
|
|
day_to_check = day_to_check - day_to_check.sec.seconds
|
|
|
|
|
|
|
|
# loop minutes till next full hour
|
|
|
|
if day_to_check.min != 0
|
|
|
|
(0..5).each do |minute_counter|
|
|
|
|
if minute_counter != 0
|
|
|
|
break if day_to_check.min == 0
|
|
|
|
day_to_check = day_to_check + 10.minutes
|
|
|
|
end
|
|
|
|
next if !timeplan['hours'][day_to_check.hour] && !timeplan['hours'][day_to_check.hour.to_s]
|
|
|
|
next if !timeplan['minutes'][match_minutes(day_to_check.min)] && !timeplan['minutes'][match_minutes(day_to_check.min).to_s]
|
|
|
|
return day_to_check
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# loop hours
|
|
|
|
hour_to_check = nil
|
|
|
|
(0..23).each do |hour_counter|
|
|
|
|
hour_to_check = day_to_check + hour_counter.hours
|
|
|
|
|
|
|
|
# start on next day
|
|
|
|
if hour_to_check.day != day_to_check.day
|
|
|
|
time = day_to_check - day_to_check.hour.hours
|
|
|
|
break
|
|
|
|
end
|
|
|
|
|
|
|
|
# ignore not configured hours
|
|
|
|
next if !timeplan['hours'][hour_to_check.hour] && !timeplan['hours'][hour_to_check.hour.to_s]
|
|
|
|
return nil if !hour_to_check
|
|
|
|
|
|
|
|
# loop minutes
|
|
|
|
minute_to_check = nil
|
|
|
|
(0..5).each do |minute_counter|
|
|
|
|
minute_to_check = hour_to_check + minute_counter.minutes * 10
|
|
|
|
next if !timeplan['minutes'][match_minutes(minute_to_check.min)] && !timeplan['minutes'][match_minutes(minute_to_check.min).to_s]
|
|
|
|
time_to_check = minute_to_check
|
|
|
|
break
|
|
|
|
end
|
|
|
|
next if !minute_to_check
|
|
|
|
return time_to_check
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
nil
|
|
|
|
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
|
|
|
|
"#{minutes.to_s.gsub(/(\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
|