HornsAndHooves-slackiq 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/HornsAndHooves-slackiq.gemspec +1 -2
- data/lib/slackiq.rb +173 -134
- data/lib/slackiq/version.rb +2 -2
- metadata +5 -20
- data/lib/slackiq/time_helper.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5cf82d00d5319230c303822b593bc4350c99f18cc5bd7539247c2622cc35b021
|
4
|
+
data.tar.gz: 40f830f297f3469d0b32bd104c6a3eeaa1cb71c0da5e8b31a8813af7bcfd5294
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a07bc1b7e428c2ceac87a093118307b549dd2cae23ce12872a80f4347459951dabef1af998853f8ac54a7abdafe109805dc20a0a7703b4b4883029dc030a554d
|
7
|
+
data.tar.gz: 5281494bd6f24caf781621d41960f9db72655a676684b9212ed51dc721695bd8f704236f8e6c8f090febda3a7a78795198af537fa869a3daf159d6bff3d2798d
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
|
3
|
-
lib = File.expand_path("
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
5
|
require "slackiq/version"
|
6
6
|
|
@@ -20,7 +20,6 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
21
|
s.require_paths = ["lib"]
|
22
22
|
|
23
|
-
s.add_dependency "httparty"
|
24
23
|
s.add_development_dependency "bundler"
|
25
24
|
s.add_development_dependency "rake"
|
26
25
|
s.add_development_dependency "rspec"
|
data/lib/slackiq.rb
CHANGED
@@ -1,156 +1,195 @@
|
|
1
1
|
require "slackiq/version"
|
2
2
|
|
3
3
|
require "net/http"
|
4
|
+
require "uri"
|
4
5
|
require "json"
|
5
|
-
require "
|
6
|
+
require "date"
|
6
7
|
|
7
|
-
|
8
|
+
class Slackiq
|
8
9
|
|
9
|
-
|
10
|
+
attr_reader :options
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
# @author Jason Lew
|
17
|
-
def configure(webhook_urls={})
|
18
|
-
raise "Argument must be a Hash" unless webhook_urls.class == Hash
|
19
|
-
@@webhook_urls = webhook_urls
|
20
|
-
end
|
21
|
-
|
22
|
-
# Send a notification to Slack with Sidekiq info about the batch
|
23
|
-
# @author Jason Lew
|
24
|
-
def notify(options={})
|
25
|
-
url = @@webhook_urls[options[:webhook_name]]
|
26
|
-
title = options[:title]
|
27
|
-
# description = options[:description]
|
28
|
-
status = options[:status]
|
29
|
-
|
30
|
-
if (bid = options[:bid]) && status.nil?
|
31
|
-
raise <<~EOT.chomp unless defined?(Sidekiq::Batch::Status)
|
32
|
-
Sidekiq::Batch::Status is not defined. \
|
33
|
-
Are you sure Sidekiq Pro is set up correctly?
|
34
|
-
EOT
|
35
|
-
status = Sidekiq::Batch::Status.new(bid)
|
36
|
-
end
|
37
|
-
|
38
|
-
color = options[:color] || color_for(status)
|
39
|
-
|
40
|
-
extra_fields = options.except(:webhook_name, :title, :description, :status)
|
12
|
+
# @param options [Hash]
|
13
|
+
def self.notify(options)
|
14
|
+
raise "Need to run Slackiq.configure first" if @webhook_urls.nil?
|
15
|
+
new(options.merge(webhook_urls: @webhook_urls)).execute
|
16
|
+
end
|
41
17
|
|
42
|
-
|
18
|
+
# @param options [Hash]
|
19
|
+
def self.configure(webhook_urls={})
|
20
|
+
@webhook_urls = webhook_urls
|
21
|
+
end
|
43
22
|
|
44
|
-
|
45
|
-
|
23
|
+
# @param options [Hash]
|
24
|
+
def initialize(options)
|
25
|
+
@options = options
|
26
|
+
end
|
46
27
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
time_now_title = (status.complete? ? "Completed" : "Now")
|
51
|
-
end
|
28
|
+
# Send a notification to Slack with Sidekiq info about the batch
|
29
|
+
def execute
|
30
|
+
time_now = Time.now
|
52
31
|
|
53
|
-
|
54
|
-
|
55
|
-
jobs_run = total_jobs - status.pending
|
56
|
-
|
57
|
-
completion_percentage = (jobs_run/total_jobs.to_f)*100
|
58
|
-
failure_percentage = (failures/total_jobs.to_f)*100 if total_jobs && failures
|
59
|
-
|
60
|
-
# Round to two decimal places
|
61
|
-
decimal_places = 2
|
62
|
-
completion_percentage = completion_percentage.round(decimal_places)
|
63
|
-
failure_percentage = failure_percentage.round(decimal_places)
|
64
|
-
|
65
|
-
description = status.description
|
66
|
-
|
67
|
-
fields += [
|
68
|
-
{
|
69
|
-
title: "Created",
|
70
|
-
value: Slackiq::TimeHelper.format(created_at),
|
71
|
-
short: true
|
72
|
-
},
|
73
|
-
{
|
74
|
-
title: time_now_title,
|
75
|
-
value: Slackiq::TimeHelper.format(time_now),
|
76
|
-
short: true
|
77
|
-
},
|
78
|
-
{
|
79
|
-
title: "Duration",
|
80
|
-
value: duration,
|
81
|
-
short: true
|
82
|
-
},
|
83
|
-
{
|
84
|
-
title: "Total Jobs",
|
85
|
-
value: total_jobs,
|
86
|
-
short: true
|
87
|
-
},
|
88
|
-
{
|
89
|
-
title: "Jobs Run",
|
90
|
-
value: jobs_run,
|
91
|
-
short: true
|
92
|
-
},
|
93
|
-
{
|
94
|
-
title: "Completion %",
|
95
|
-
value: "#{completion_percentage}%",
|
96
|
-
short: true
|
97
|
-
},
|
98
|
-
{
|
99
|
-
title: "Failures",
|
100
|
-
value: status.failures,
|
101
|
-
short: true
|
102
|
-
},
|
103
|
-
{
|
104
|
-
title: "Failure %",
|
105
|
-
value: "#{failure_percentage}%",
|
106
|
-
short: true
|
107
|
-
},
|
108
|
-
]
|
109
|
-
end
|
32
|
+
title = options[:title]
|
33
|
+
status = options[:status]
|
110
34
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
end
|
35
|
+
if (bid = options[:bid]) && status.nil?
|
36
|
+
raise <<~EOT.chomp unless defined?(Sidekiq::Batch::Status)
|
37
|
+
Sidekiq::Batch::Status is not defined. \
|
38
|
+
Are you sure Sidekiq Pro is set up correctly?
|
39
|
+
EOT
|
40
|
+
status = Sidekiq::Batch::Status.new(bid)
|
41
|
+
end
|
119
42
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
43
|
+
return if status.nil?
|
44
|
+
|
45
|
+
color = options[:color] || color_for(status)
|
46
|
+
|
47
|
+
duration = elapsed_time_humanized(status.created_at, time_now)
|
48
|
+
time_title = status.complete? ? "Completed" : "Now"
|
49
|
+
jobs_run = status.total - status.pending
|
50
|
+
|
51
|
+
completion_percentage = percentage(jobs_run / status.total.to_f)
|
52
|
+
failure_percentage = percentage(status.failures / status.total.to_f)
|
53
|
+
|
54
|
+
fields = [
|
55
|
+
{
|
56
|
+
title: title,
|
57
|
+
value: status.description,
|
58
|
+
short: false
|
59
|
+
},
|
60
|
+
{
|
61
|
+
title: "Batch ID",
|
62
|
+
value: status.bid,
|
63
|
+
short: false
|
64
|
+
},
|
65
|
+
{
|
66
|
+
title: "Created",
|
67
|
+
value: time_format(status.created_at),
|
68
|
+
short: true
|
69
|
+
},
|
70
|
+
{
|
71
|
+
title: time_title,
|
72
|
+
value: time_format(time_now),
|
73
|
+
short: true
|
74
|
+
},
|
75
|
+
{
|
76
|
+
title: "Duration",
|
77
|
+
value: duration,
|
78
|
+
short: true
|
79
|
+
},
|
80
|
+
{
|
81
|
+
title: "Total Jobs",
|
82
|
+
value: status.total,
|
83
|
+
short: true
|
84
|
+
},
|
85
|
+
{
|
86
|
+
title: "Jobs Run",
|
87
|
+
value: jobs_run,
|
88
|
+
short: true
|
89
|
+
},
|
90
|
+
{
|
91
|
+
title: "Completion %",
|
92
|
+
value: completion_percentage,
|
93
|
+
short: true
|
94
|
+
},
|
95
|
+
{
|
96
|
+
title: "Failures",
|
97
|
+
value: status.failures,
|
98
|
+
short: true
|
99
|
+
},
|
100
|
+
{
|
101
|
+
title: "Failure %",
|
102
|
+
value: failure_percentage,
|
103
|
+
short: true
|
104
|
+
}
|
105
|
+
]
|
106
|
+
|
107
|
+
body = {
|
108
|
+
attachments: [
|
109
|
+
fields: fields,
|
110
|
+
color: color
|
128
111
|
]
|
112
|
+
}
|
113
|
+
http_post(body)
|
114
|
+
end
|
129
115
|
|
130
|
-
|
116
|
+
# @param data [Hash]
|
117
|
+
private def http_post(data)
|
118
|
+
url = options[:webhook_urls].fetch(options[:webhook_name])
|
119
|
+
uri = URI.parse(url)
|
120
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
121
|
+
http.use_ssl = true if uri.port == 443
|
122
|
+
|
123
|
+
header = {"Content-Type": "application/json"}
|
124
|
+
request = Net::HTTP::Post.new(uri.request_uri, header)
|
125
|
+
request.body = data.to_json
|
126
|
+
http.request(request)
|
127
|
+
end
|
131
128
|
|
132
|
-
|
133
|
-
|
129
|
+
# @param number [Numeric]
|
130
|
+
# @param precision [Integer]
|
131
|
+
# @param multiply100 [Boolean]
|
132
|
+
private def percentage(number, precision: 2, multiply100: true)
|
133
|
+
number = number * 100 if multiply100
|
134
|
+
rounded = number.to_f.round(precision)
|
135
|
+
format = number == rounded.to_i ? "%.f" : "%.#{precision}f"
|
136
|
+
(format % rounded) + "%"
|
137
|
+
end
|
134
138
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
139
|
+
# @param status [Sidekiq::Batch::Status]
|
140
|
+
private def color_for(status)
|
141
|
+
colors = {
|
142
|
+
red: "f00000",
|
143
|
+
yellow: "ffc000",
|
144
|
+
green: "009800"
|
145
|
+
}
|
146
|
+
|
147
|
+
if status.total == 0
|
148
|
+
colors[:yellow]
|
149
|
+
elsif status.failures > 0
|
150
|
+
colors[:red]
|
151
|
+
elsif status.failures == 0
|
152
|
+
colors[:green]
|
153
|
+
else
|
154
|
+
colors[:yellow]
|
141
155
|
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# @param t0 [DateTime]
|
159
|
+
# @param t1 [DateTime]
|
160
|
+
private def elapsed_time_humanized(t0, t1, precision: 2)
|
161
|
+
time_humanize(
|
162
|
+
elapsed_seconds(t0, t1, precision: precision),
|
163
|
+
precision: precision
|
164
|
+
)
|
165
|
+
end
|
142
166
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
167
|
+
# @param t0 [DateTime]
|
168
|
+
# @param t1 [DateTime]
|
169
|
+
private def elapsed_seconds(t0, t1, precision: 2)
|
170
|
+
dt0 = t0.to_datetime
|
171
|
+
dt1 = t1.to_datetime
|
172
|
+
((dt1 - dt0) * 24 * 60 * 60).to_f.round(precision)
|
173
|
+
end
|
174
|
+
|
175
|
+
# http://stackoverflow.com/questions/4136248/how-to-generate-a-human-readable-time-range-using-ruby-on-rails
|
176
|
+
# @param secs [Integer]
|
177
|
+
private def time_humanize(secs, precision: 2)
|
178
|
+
[[60, :s], [60, :m], [24, :h], [1000, :d]].map do |count, name|
|
179
|
+
if secs > 0
|
180
|
+
secs, n = secs.divmod(count)
|
181
|
+
if name == :s
|
182
|
+
num = n.to_f == n.to_i ? n.to_i : n.to_f
|
183
|
+
"%.#{precision}f#{name}" % num
|
184
|
+
else
|
185
|
+
"#{n.to_i}#{name}"
|
186
|
+
end
|
153
187
|
end
|
154
|
-
end
|
188
|
+
end.compact.reverse.join(" ")
|
189
|
+
end
|
190
|
+
|
191
|
+
# @param time [DateTime]
|
192
|
+
private def time_format(time)
|
193
|
+
time.strftime("%D @ %I:%M:%S %P")
|
155
194
|
end
|
156
195
|
end
|
data/lib/slackiq/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = "1.
|
1
|
+
class Slackiq
|
2
|
+
VERSION = "1.3.0".freeze
|
3
3
|
end
|
metadata
CHANGED
@@ -1,30 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: HornsAndHooves-slackiq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- HornsAndHooves
|
8
8
|
- Peter Maneykowski
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2020-
|
12
|
+
date: 2020-07-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: httparty
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
requirements:
|
18
|
-
- - ">="
|
19
|
-
- !ruby/object:Gem::Version
|
20
|
-
version: '0'
|
21
|
-
type: :runtime
|
22
|
-
prerelease: false
|
23
|
-
version_requirements: !ruby/object:Gem::Requirement
|
24
|
-
requirements:
|
25
|
-
- - ">="
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
version: '0'
|
28
14
|
- !ruby/object:Gem::Dependency
|
29
15
|
name: bundler
|
30
16
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,13 +72,12 @@ files:
|
|
86
72
|
- bin/console
|
87
73
|
- bin/setup
|
88
74
|
- lib/slackiq.rb
|
89
|
-
- lib/slackiq/time_helper.rb
|
90
75
|
- lib/slackiq/version.rb
|
91
76
|
homepage: https://github.com/HornsAndHooves/slackiq
|
92
77
|
licenses:
|
93
78
|
- MIT
|
94
79
|
metadata: {}
|
95
|
-
post_install_message:
|
80
|
+
post_install_message:
|
96
81
|
rdoc_options: []
|
97
82
|
require_paths:
|
98
83
|
- lib
|
@@ -108,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
93
|
version: '0'
|
109
94
|
requirements: []
|
110
95
|
rubygems_version: 3.0.6
|
111
|
-
signing_key:
|
96
|
+
signing_key:
|
112
97
|
specification_version: 4
|
113
98
|
summary: 'HornsAndHooves: Slack and Sidekiq Pro integration'
|
114
99
|
test_files: []
|
data/lib/slackiq/time_helper.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
require "date"
|
2
|
-
|
3
|
-
module Slackiq
|
4
|
-
module TimeHelper
|
5
|
-
|
6
|
-
class << self
|
7
|
-
|
8
|
-
def elapsed_time_humanized(t0, t1)
|
9
|
-
humanize(elapsed_seconds(t0, t1))
|
10
|
-
end
|
11
|
-
|
12
|
-
def elapsed_seconds(t0, t1)
|
13
|
-
dt0 = t0.to_datetime
|
14
|
-
dt1 = t1.to_datetime
|
15
|
-
((dt1-dt0)*24*60*60).to_f.round(2)
|
16
|
-
end
|
17
|
-
|
18
|
-
# http://stackoverflow.com/questions/4136248/how-to-generate-a-human-readable-time-range-using-ruby-on-rails
|
19
|
-
def humanize(secs)
|
20
|
-
[[60, :s], [60, :m], [24, :h], [1000, :d]].map{ |count, name|
|
21
|
-
if secs > 0
|
22
|
-
secs, n = secs.divmod(count)
|
23
|
-
if name == :s
|
24
|
-
"%.2f#{name}" % [n.to_f]
|
25
|
-
else
|
26
|
-
"#{n.to_i}#{name}"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
}.compact.reverse.join(" ")
|
30
|
-
end
|
31
|
-
|
32
|
-
def format(time)
|
33
|
-
time.strftime("%D @ %r").gsub("PM", "pm").gsub("AM", "am")
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|