checkoff 0.40.0 → 0.41.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/checkoff/internal/config_loader.rb +1 -0
- data/lib/checkoff/internal/task_selector_evaluator.rb +137 -9
- data/lib/checkoff/task_searches.rb +0 -8
- data/lib/checkoff/task_selectors.rb +10 -2
- data/lib/checkoff/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca8318553efc488d88a70b394e290ec8030f532ac9b3d4fa4fc67eb60849270a
|
4
|
+
data.tar.gz: cd86cc76ca2f74bed8e35212da234f5cbcad358db6a08d181f702fda149e785a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ecde71759c34fe1cee589084e7c06577726af223cc207f3cb4236f02d3d4fba75662bf622d69de7cabc7f22acb6f041cd8ddfeb142f60aba07051b35a3ac9e00
|
7
|
+
data.tar.gz: bb87fb6a83333fc0efffcd4ce5a37da55689bd617642a15dbbb4b4a1c94cb8511aa9a2e9d11e4fc16fc6ec6631d8191976e125156a1241b8f27367fb0afd4d27
|
data/Gemfile.lock
CHANGED
@@ -3,28 +3,47 @@
|
|
3
3
|
module Checkoff
|
4
4
|
# Base class to evaluate a task selector function given fully evaluated arguments
|
5
5
|
class FunctionEvaluator
|
6
|
+
# @param task_selector [Array<(Symbol, Array)>]
|
7
|
+
# @param tasks [Checkoff::Tasks]
|
6
8
|
def initialize(task_selector:,
|
7
9
|
tasks:)
|
8
10
|
@task_selector = task_selector
|
9
11
|
@tasks = tasks
|
10
12
|
end
|
11
13
|
|
14
|
+
# @sg-ignore
|
15
|
+
# @param _index [Integer]
|
12
16
|
def evaluate_arg?(_index)
|
13
17
|
true
|
14
18
|
end
|
15
19
|
|
20
|
+
# @sg-ignore
|
21
|
+
# @return [Boolean]
|
22
|
+
def matches?
|
23
|
+
raise 'Override me!'
|
24
|
+
end
|
25
|
+
|
16
26
|
private
|
17
27
|
|
28
|
+
# @param object [Object]
|
29
|
+
# @param fn_name [Symbol]
|
18
30
|
def fn?(object, fn_name)
|
19
31
|
object.is_a?(Array) && !object.empty? && [fn_name, fn_name.to_s].include?(object[0])
|
20
32
|
end
|
21
33
|
|
34
|
+
# @sg-ignore
|
35
|
+
# @param task [Asana::Resources::Task]
|
36
|
+
# @param custom_field_gid [String]
|
37
|
+
# @return [Hash]
|
22
38
|
def pull_custom_field_or_raise(task, custom_field_gid)
|
39
|
+
# @type [Array<Hash>]
|
23
40
|
custom_fields = task.custom_fields
|
24
41
|
if custom_fields.nil?
|
25
42
|
raise "Could not find custom_fields under task (was 'custom_fields' included in 'extra_fields'?)"
|
26
43
|
end
|
27
44
|
|
45
|
+
# @sg-ignore
|
46
|
+
# @type [Hash, nil]
|
28
47
|
matched_custom_field = custom_fields.find { |data| data.fetch('gid') == custom_field_gid }
|
29
48
|
if matched_custom_field.nil?
|
30
49
|
raise "Could not find custom field with gid #{custom_field_gid} " \
|
@@ -34,8 +53,12 @@ module Checkoff
|
|
34
53
|
matched_custom_field
|
35
54
|
end
|
36
55
|
|
56
|
+
# @return [Array<(Symbol, Array)>]
|
37
57
|
attr_reader :task_selector
|
38
58
|
|
59
|
+
# @sg-ignore
|
60
|
+
# @param custom_field [Hash]
|
61
|
+
# @return [Array<String>]
|
39
62
|
def pull_enum_values(custom_field)
|
40
63
|
resource_subtype = custom_field.fetch('resource_subtype')
|
41
64
|
case resource_subtype
|
@@ -48,6 +71,9 @@ module Checkoff
|
|
48
71
|
end
|
49
72
|
end
|
50
73
|
|
74
|
+
# @param custom_field [Hash]
|
75
|
+
# @param enum_value [Object, nil]
|
76
|
+
# @return [Array<String>]
|
51
77
|
def find_gids(custom_field, enum_value)
|
52
78
|
if enum_value.nil?
|
53
79
|
[]
|
@@ -58,12 +84,42 @@ module Checkoff
|
|
58
84
|
end
|
59
85
|
end
|
60
86
|
|
87
|
+
# @param task [Asana::Resources::Task]
|
88
|
+
# @param custom_field_gid [String]
|
89
|
+
# @return [Array<String>]
|
61
90
|
def pull_custom_field_values_gids(task, custom_field_gid)
|
62
91
|
custom_field = pull_custom_field_or_raise(task, custom_field_gid)
|
63
92
|
pull_enum_values(custom_field).flat_map do |enum_value|
|
64
93
|
find_gids(custom_field, enum_value)
|
65
94
|
end
|
66
95
|
end
|
96
|
+
|
97
|
+
# @sg-ignore
|
98
|
+
# @param task [Asana::Resources::Task]
|
99
|
+
# @param custom_field_name [String]
|
100
|
+
# @return [Hash, nil]
|
101
|
+
def pull_custom_field_by_name(task, custom_field_name)
|
102
|
+
custom_fields = task.custom_fields
|
103
|
+
if custom_fields.nil?
|
104
|
+
raise "custom fields not found on task - did you add 'custom_field' in your extra_fields argument?"
|
105
|
+
end
|
106
|
+
|
107
|
+
# @sg-ignore
|
108
|
+
# @type [Hash, nil]
|
109
|
+
custom_fields.find { |field| field.fetch('name') == custom_field_name }
|
110
|
+
end
|
111
|
+
|
112
|
+
# @param task [Asana::Resources::Task]
|
113
|
+
# @param custom_field_name [String]
|
114
|
+
# @return [Hash]
|
115
|
+
def pull_custom_field_by_name_or_raise(task, custom_field_name)
|
116
|
+
custom_field = pull_custom_field_by_name(task, custom_field_name)
|
117
|
+
if custom_field.nil?
|
118
|
+
raise "Could not find custom field with name #{custom_field_name} " \
|
119
|
+
"in task #{task.gid} with custom fields #{task.custom_fields}"
|
120
|
+
end
|
121
|
+
custom_field
|
122
|
+
end
|
67
123
|
end
|
68
124
|
|
69
125
|
# :and function
|
@@ -72,6 +128,10 @@ module Checkoff
|
|
72
128
|
fn?(task_selector, :and)
|
73
129
|
end
|
74
130
|
|
131
|
+
# @param _task [Asana::Resources::Task]
|
132
|
+
# @param lhs [Object]
|
133
|
+
# @param rhs [Object]
|
134
|
+
# @return [Object]
|
75
135
|
def evaluate(_task, lhs, rhs)
|
76
136
|
lhs && rhs
|
77
137
|
end
|
@@ -83,6 +143,9 @@ module Checkoff
|
|
83
143
|
fn?(task_selector, :not)
|
84
144
|
end
|
85
145
|
|
146
|
+
# @param _task [Asana::Resources::Task]
|
147
|
+
# @param subvalue [Object]
|
148
|
+
# @return [Boolean]
|
86
149
|
def evaluate(_task, subvalue)
|
87
150
|
!subvalue
|
88
151
|
end
|
@@ -94,6 +157,9 @@ module Checkoff
|
|
94
157
|
fn?(task_selector, :nil?)
|
95
158
|
end
|
96
159
|
|
160
|
+
# @param _task [Asana::Resources::Task]
|
161
|
+
# @param subvalue [Object]
|
162
|
+
# @return [Boolean]
|
97
163
|
def evaluate(_task, subvalue)
|
98
164
|
subvalue.nil?
|
99
165
|
end
|
@@ -105,10 +171,15 @@ module Checkoff
|
|
105
171
|
fn?(task_selector, :tag)
|
106
172
|
end
|
107
173
|
|
174
|
+
# @param _index [Integer]
|
108
175
|
def evaluate_arg?(_index)
|
109
176
|
false
|
110
177
|
end
|
111
178
|
|
179
|
+
# @sg-ignore
|
180
|
+
# @param task [Asana::Resources::Task]
|
181
|
+
# @param tag_name [String]
|
182
|
+
# @return [Boolean]
|
112
183
|
def evaluate(task, tag_name)
|
113
184
|
task.tags.map(&:name).include? tag_name
|
114
185
|
end
|
@@ -120,6 +191,8 @@ module Checkoff
|
|
120
191
|
fn?(task_selector, :due)
|
121
192
|
end
|
122
193
|
|
194
|
+
# @param task [Asana::Resources::Task]
|
195
|
+
# @return [Boolean]
|
123
196
|
def evaluate(task)
|
124
197
|
@tasks.task_ready?(task)
|
125
198
|
end
|
@@ -131,6 +204,9 @@ module Checkoff
|
|
131
204
|
fn?(task_selector, :due_date_set)
|
132
205
|
end
|
133
206
|
|
207
|
+
# @sg-ignore
|
208
|
+
# @param task [Asana::Resources::Task]
|
209
|
+
# @return [Boolean]
|
134
210
|
def evaluate(task)
|
135
211
|
!task.due_at.nil? || !task.due_on.nil?
|
136
212
|
end
|
@@ -142,17 +218,16 @@ module Checkoff
|
|
142
218
|
fn?(task_selector, :custom_field_value)
|
143
219
|
end
|
144
220
|
|
221
|
+
# @param _index [Integer]
|
145
222
|
def evaluate_arg?(_index)
|
146
223
|
false
|
147
224
|
end
|
148
225
|
|
226
|
+
# @param task [Asana::Resources::Task]
|
227
|
+
# @param custom_field_name [String]
|
228
|
+
# @return [String, nil]
|
149
229
|
def evaluate(task, custom_field_name)
|
150
|
-
|
151
|
-
if custom_fields.nil?
|
152
|
-
raise "custom fields not found on task - did you add 'custom_field' in your extra_fields argument?"
|
153
|
-
end
|
154
|
-
|
155
|
-
custom_field = custom_fields.find { |field| field.fetch('name') == custom_field_name }
|
230
|
+
custom_field = pull_custom_field_by_name(task, custom_field_name)
|
156
231
|
return nil if custom_field.nil?
|
157
232
|
|
158
233
|
custom_field['display_value']
|
@@ -169,6 +244,10 @@ module Checkoff
|
|
169
244
|
false
|
170
245
|
end
|
171
246
|
|
247
|
+
# @sg-ignore
|
248
|
+
# @param task [Asana::Resources::Task]
|
249
|
+
# @param custom_field_gid [String]
|
250
|
+
# @return [String, nil]
|
172
251
|
def evaluate(task, custom_field_gid)
|
173
252
|
custom_field = pull_custom_field_or_raise(task, custom_field_gid)
|
174
253
|
custom_field['display_value']
|
@@ -185,6 +264,10 @@ module Checkoff
|
|
185
264
|
false
|
186
265
|
end
|
187
266
|
|
267
|
+
# @param task [Asana::Resources::Task]
|
268
|
+
# @param custom_field_gid [String]
|
269
|
+
# @param custom_field_values_gids [Array<String>]
|
270
|
+
# @return [Boolean]
|
188
271
|
def evaluate(task, custom_field_gid, custom_field_values_gids)
|
189
272
|
actual_custom_field_values_gids = pull_custom_field_values_gids(task, custom_field_gid)
|
190
273
|
|
@@ -204,6 +287,10 @@ module Checkoff
|
|
204
287
|
false
|
205
288
|
end
|
206
289
|
|
290
|
+
# @param task [Asana::Resources::Task]
|
291
|
+
# @param custom_field_gid [String]
|
292
|
+
# @param custom_field_values_gids [Array<String>]
|
293
|
+
# @return [Boolean]
|
207
294
|
def evaluate(task, custom_field_gid, custom_field_values_gids)
|
208
295
|
actual_custom_field_values_gids = pull_custom_field_values_gids(task, custom_field_gid)
|
209
296
|
|
@@ -213,8 +300,34 @@ module Checkoff
|
|
213
300
|
end
|
214
301
|
end
|
215
302
|
|
303
|
+
# :custom_field_less_than_n_days_from_now function
|
304
|
+
class CustomFieldLessThanNDaysFromNowFunctionEvaluator < FunctionEvaluator
|
305
|
+
def matches?
|
306
|
+
fn?(task_selector, :custom_field_less_than_n_days_from_now)
|
307
|
+
end
|
308
|
+
|
309
|
+
def evaluate_arg?(_index)
|
310
|
+
false
|
311
|
+
end
|
312
|
+
|
313
|
+
# @param task [Asana::Resources::Task]
|
314
|
+
# @param custom_field_name [String]
|
315
|
+
# @param num_days [Integer]
|
316
|
+
# @return [Boolean]
|
317
|
+
def evaluate(task, custom_field_name, num_days)
|
318
|
+
custom_field = pull_custom_field_by_name_or_raise(task, custom_field_name)
|
319
|
+
|
320
|
+
time_str = custom_field.fetch('display_value')
|
321
|
+
time = Time.parse(time_str)
|
322
|
+
n_days_from_now = (Time.now + (num_days * 24 * 60 * 60))
|
323
|
+
time < n_days_from_now
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
216
327
|
# Evaluator task selectors against a task
|
217
328
|
class TaskSelectorEvaluator
|
329
|
+
# @param task [Asana::Resources::Task]
|
330
|
+
# @param tasks [Checkoff::Tasks]
|
218
331
|
def initialize(task:,
|
219
332
|
tasks: Checkoff::Tasks.new)
|
220
333
|
@task = task
|
@@ -232,12 +345,18 @@ module Checkoff
|
|
232
345
|
AndFunctionEvaluator,
|
233
346
|
DuePFunctionEvaluator,
|
234
347
|
DueDateSetPFunctionEvaluator,
|
348
|
+
CustomFieldLessThanNDaysFromNowFunctionEvaluator,
|
235
349
|
].freeze
|
236
350
|
|
351
|
+
# @param task_selector [Array]
|
352
|
+
# @return [Boolean, Object, nil]
|
237
353
|
def evaluate(task_selector)
|
238
|
-
return true if task_selector
|
354
|
+
return true if task_selector.empty?
|
239
355
|
|
356
|
+
# @param evaluator_class [Class<FunctionEvaluator>]
|
240
357
|
FUNCTION_EVALUTORS.each do |evaluator_class|
|
358
|
+
# @sg-ignore
|
359
|
+
# @type [FunctionEvaluator]
|
241
360
|
evaluator = evaluator_class.new(task_selector: task_selector,
|
242
361
|
tasks: tasks)
|
243
362
|
|
@@ -246,11 +365,15 @@ module Checkoff
|
|
246
365
|
return try_this_evaluator(task_selector, evaluator)
|
247
366
|
end
|
248
367
|
|
249
|
-
raise "Syntax issue trying to handle #{task_selector}"
|
368
|
+
raise "Syntax issue trying to handle #{task_selector.inspect}"
|
250
369
|
end
|
251
370
|
|
252
371
|
private
|
253
372
|
|
373
|
+
# @sg-ignore
|
374
|
+
# @param task_selector [Array]
|
375
|
+
# @param evaluator [FunctionEvaluator]
|
376
|
+
# @return [Boolean, Object, nil]
|
254
377
|
def try_this_evaluator(task_selector, evaluator)
|
255
378
|
evaluated_args = task_selector[1..].map.with_index do |item, index|
|
256
379
|
if evaluator.evaluate_arg?(index)
|
@@ -263,6 +386,11 @@ module Checkoff
|
|
263
386
|
evaluator.evaluate(task, *evaluated_args)
|
264
387
|
end
|
265
388
|
|
266
|
-
|
389
|
+
# @return [Asana::Resources::Task]
|
390
|
+
attr_reader :task
|
391
|
+
# @return [Checkoff::Tasks]
|
392
|
+
attr_reader :tasks
|
393
|
+
# @return [Array<(Symbol, Array)>]
|
394
|
+
attr_reader :task_selector
|
267
395
|
end
|
268
396
|
end
|
@@ -19,12 +19,9 @@ module Checkoff
|
|
19
19
|
# Run task searches against the Asana API
|
20
20
|
class TaskSearches
|
21
21
|
MINUTE = 60
|
22
|
-
# @sg-ignore
|
23
22
|
HOUR = MINUTE * 60
|
24
23
|
DAY = 24 * HOUR
|
25
|
-
# @sg-ignore
|
26
24
|
REALLY_LONG_CACHE_TIME = HOUR * 1
|
27
|
-
# @sg-ignore
|
28
25
|
LONG_CACHE_TIME = MINUTE * 15
|
29
26
|
SHORT_CACHE_TIME = MINUTE
|
30
27
|
|
@@ -62,7 +59,6 @@ module Checkoff
|
|
62
59
|
client: client)
|
63
60
|
tasks.select { |task| task_selectors.filter_via_task_selector(task, task_selector) }
|
64
61
|
end
|
65
|
-
# @sg-ignore
|
66
62
|
cache_method :task_search, LONG_CACHE_TIME
|
67
63
|
|
68
64
|
private
|
@@ -81,11 +77,7 @@ module Checkoff
|
|
81
77
|
# :nocov:
|
82
78
|
class << self
|
83
79
|
def run
|
84
|
-
# @sg-ignore
|
85
|
-
# @type [String]
|
86
80
|
workspace_name = ARGV[0] || raise('Please pass workspace name as first argument')
|
87
|
-
# @sg-ignore
|
88
|
-
# @type [String]
|
89
81
|
url = ARGV[1] || raise('Please pass task search URL as second argument')
|
90
82
|
task_searches = Checkoff::TaskSearches.new
|
91
83
|
task_search = task_searches.task_search(workspace_name, url)
|
@@ -21,6 +21,10 @@ module Checkoff
|
|
21
21
|
LONG_CACHE_TIME = MINUTE * 15
|
22
22
|
SHORT_CACHE_TIME = MINUTE
|
23
23
|
|
24
|
+
# @sg-ignore
|
25
|
+
# @param [Hash] config
|
26
|
+
# @param [Asana::Client] client
|
27
|
+
# @param [Checkoff::Tasks] tasks
|
24
28
|
def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
|
25
29
|
client: Checkoff::Clients.new(config: config).client,
|
26
30
|
tasks: Checkoff::Tasks.new(config: config,
|
@@ -30,8 +34,8 @@ module Checkoff
|
|
30
34
|
end
|
31
35
|
|
32
36
|
# @param [Asana::Resources::Task] task
|
33
|
-
# @param [
|
34
|
-
#
|
37
|
+
# @param [Array<(Symbol, Array)>] task_selector Filter based on
|
38
|
+
# task details. Examples: [:tag, 'foo'] [:not, [:tag, 'foo']] [:tag, 'foo']
|
35
39
|
# @return [Boolean]
|
36
40
|
def filter_via_task_selector(task, task_selector)
|
37
41
|
evaluator = TaskSelectorEvaluator.new(task: task, tasks: tasks)
|
@@ -40,16 +44,19 @@ module Checkoff
|
|
40
44
|
|
41
45
|
private
|
42
46
|
|
47
|
+
# @return [Checkoff::Tasks]
|
43
48
|
attr_reader :tasks
|
44
49
|
|
45
50
|
# bundle exec ./task_selectors.rb
|
46
51
|
# :nocov:
|
47
52
|
class << self
|
53
|
+
# @sg-ignore
|
48
54
|
# @return [String]
|
49
55
|
def project_name
|
50
56
|
ARGV[1] || raise('Please pass project name to pull tasks from as first argument')
|
51
57
|
end
|
52
58
|
|
59
|
+
# @sg-ignore
|
53
60
|
# @return [String]
|
54
61
|
def workspace_name
|
55
62
|
ARGV[0] || raise('Please pass workspace name as first argument')
|
@@ -61,6 +68,7 @@ module Checkoff
|
|
61
68
|
JSON.parse(task_selector_json)
|
62
69
|
end
|
63
70
|
|
71
|
+
# @return [void]
|
64
72
|
def run
|
65
73
|
require 'checkoff/projects'
|
66
74
|
|
data/lib/checkoff/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: checkoff
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.41.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vince Broz
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-06-
|
11
|
+
date: 2023-06-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|