checkoff 0.40.0 → 0.42.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/Gemfile.lock +1 -1
- data/lib/checkoff/internal/config_loader.rb +1 -0
- data/lib/checkoff/internal/task_selector_evaluator.rb +180 -16
- 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: 83cd30ff493ada586596020db325eed63c9e4c24919e34c7e9926c0f9bedf51f
|
4
|
+
data.tar.gz: fee54feaa76dafc06dba69267f4ea6a7b2877e68df6af43738468e65cca552b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d4cb677d35d91830b2aa6145d40a74ebb1e34ddd9a1e17203ef62ac66afb30a3a864924462de263ff663d33cc561272143fd1b0ab45e9499c4f42e2dad97840
|
7
|
+
data.tar.gz: b074ac5d6a2675e801b52e96d8453847607bf0e523a5988a50456258f1a668319846c8c1f965eadb09409f83b82d23b1262935c2606bab175de0664c7af2077a
|
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)>,String]
|
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,21 +157,44 @@ 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
|
100
166
|
end
|
101
167
|
|
168
|
+
# :equals? function
|
169
|
+
class EqualsPFunctionEvaluator < FunctionEvaluator
|
170
|
+
def matches?
|
171
|
+
fn?(task_selector, :equals?)
|
172
|
+
end
|
173
|
+
|
174
|
+
# @param _task [Asana::Resources::Task]
|
175
|
+
# @param lhs [Object]
|
176
|
+
# @param rhs [Object]
|
177
|
+
# @return [Boolean]
|
178
|
+
def evaluate(_task, lhs, rhs)
|
179
|
+
lhs == rhs
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
102
183
|
# :tag function
|
103
184
|
class TagPFunctionEvaluator < FunctionEvaluator
|
104
185
|
def matches?
|
105
186
|
fn?(task_selector, :tag)
|
106
187
|
end
|
107
188
|
|
189
|
+
# @param _index [Integer]
|
108
190
|
def evaluate_arg?(_index)
|
109
191
|
false
|
110
192
|
end
|
111
193
|
|
194
|
+
# @sg-ignore
|
195
|
+
# @param task [Asana::Resources::Task]
|
196
|
+
# @param tag_name [String]
|
197
|
+
# @return [Boolean]
|
112
198
|
def evaluate(task, tag_name)
|
113
199
|
task.tags.map(&:name).include? tag_name
|
114
200
|
end
|
@@ -120,6 +206,8 @@ module Checkoff
|
|
120
206
|
fn?(task_selector, :due)
|
121
207
|
end
|
122
208
|
|
209
|
+
# @param task [Asana::Resources::Task]
|
210
|
+
# @return [Boolean]
|
123
211
|
def evaluate(task)
|
124
212
|
@tasks.task_ready?(task)
|
125
213
|
end
|
@@ -131,6 +219,9 @@ module Checkoff
|
|
131
219
|
fn?(task_selector, :due_date_set)
|
132
220
|
end
|
133
221
|
|
222
|
+
# @sg-ignore
|
223
|
+
# @param task [Asana::Resources::Task]
|
224
|
+
# @return [Boolean]
|
134
225
|
def evaluate(task)
|
135
226
|
!task.due_at.nil? || !task.due_on.nil?
|
136
227
|
end
|
@@ -142,17 +233,16 @@ module Checkoff
|
|
142
233
|
fn?(task_selector, :custom_field_value)
|
143
234
|
end
|
144
235
|
|
236
|
+
# @param _index [Integer]
|
145
237
|
def evaluate_arg?(_index)
|
146
238
|
false
|
147
239
|
end
|
148
240
|
|
241
|
+
# @param task [Asana::Resources::Task]
|
242
|
+
# @param custom_field_name [String]
|
243
|
+
# @return [String, nil]
|
149
244
|
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 }
|
245
|
+
custom_field = pull_custom_field_by_name(task, custom_field_name)
|
156
246
|
return nil if custom_field.nil?
|
157
247
|
|
158
248
|
custom_field['display_value']
|
@@ -169,6 +259,10 @@ module Checkoff
|
|
169
259
|
false
|
170
260
|
end
|
171
261
|
|
262
|
+
# @sg-ignore
|
263
|
+
# @param task [Asana::Resources::Task]
|
264
|
+
# @param custom_field_gid [String]
|
265
|
+
# @return [String, nil]
|
172
266
|
def evaluate(task, custom_field_gid)
|
173
267
|
custom_field = pull_custom_field_or_raise(task, custom_field_gid)
|
174
268
|
custom_field['display_value']
|
@@ -185,6 +279,10 @@ module Checkoff
|
|
185
279
|
false
|
186
280
|
end
|
187
281
|
|
282
|
+
# @param task [Asana::Resources::Task]
|
283
|
+
# @param custom_field_gid [String]
|
284
|
+
# @param custom_field_values_gids [Array<String>]
|
285
|
+
# @return [Boolean]
|
188
286
|
def evaluate(task, custom_field_gid, custom_field_values_gids)
|
189
287
|
actual_custom_field_values_gids = pull_custom_field_values_gids(task, custom_field_gid)
|
190
288
|
|
@@ -204,6 +302,10 @@ module Checkoff
|
|
204
302
|
false
|
205
303
|
end
|
206
304
|
|
305
|
+
# @param task [Asana::Resources::Task]
|
306
|
+
# @param custom_field_gid [String]
|
307
|
+
# @param custom_field_values_gids [Array<String>]
|
308
|
+
# @return [Boolean]
|
207
309
|
def evaluate(task, custom_field_gid, custom_field_values_gids)
|
208
310
|
actual_custom_field_values_gids = pull_custom_field_values_gids(task, custom_field_gid)
|
209
311
|
|
@@ -213,8 +315,48 @@ module Checkoff
|
|
213
315
|
end
|
214
316
|
end
|
215
317
|
|
318
|
+
# :custom_field_less_than_n_days_from_now function
|
319
|
+
class CustomFieldLessThanNDaysFromNowFunctionEvaluator < FunctionEvaluator
|
320
|
+
def matches?
|
321
|
+
fn?(task_selector, :custom_field_less_than_n_days_from_now)
|
322
|
+
end
|
323
|
+
|
324
|
+
def evaluate_arg?(_index)
|
325
|
+
false
|
326
|
+
end
|
327
|
+
|
328
|
+
# @param task [Asana::Resources::Task]
|
329
|
+
# @param custom_field_name [String]
|
330
|
+
# @param num_days [Integer]
|
331
|
+
# @return [Boolean]
|
332
|
+
def evaluate(task, custom_field_name, num_days)
|
333
|
+
custom_field = pull_custom_field_by_name_or_raise(task, custom_field_name)
|
334
|
+
|
335
|
+
time_str = custom_field.fetch('display_value')
|
336
|
+
time = Time.parse(time_str)
|
337
|
+
n_days_from_now = (Time.now + (num_days * 24 * 60 * 60))
|
338
|
+
time < n_days_from_now
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
# String literals
|
343
|
+
class StringLiteralEvaluator < FunctionEvaluator
|
344
|
+
def matches?
|
345
|
+
task_selector.is_a?(String)
|
346
|
+
end
|
347
|
+
|
348
|
+
# @sg-ignore
|
349
|
+
# @param _task [Asana::Resources::Task]
|
350
|
+
# @return [String]
|
351
|
+
def evaluate(_task)
|
352
|
+
task_selector
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
216
356
|
# Evaluator task selectors against a task
|
217
357
|
class TaskSelectorEvaluator
|
358
|
+
# @param task [Asana::Resources::Task]
|
359
|
+
# @param tasks [Checkoff::Tasks]
|
218
360
|
def initialize(task:,
|
219
361
|
tasks: Checkoff::Tasks.new)
|
220
362
|
@task = task
|
@@ -224,6 +366,7 @@ module Checkoff
|
|
224
366
|
FUNCTION_EVALUTORS = [
|
225
367
|
NotFunctionEvaluator,
|
226
368
|
NilPFunctionEvaluator,
|
369
|
+
EqualsPFunctionEvaluator,
|
227
370
|
TagPFunctionEvaluator,
|
228
371
|
CustomFieldValueFunctionEvaluator,
|
229
372
|
CustomFieldGidValueFunctionEvaluator,
|
@@ -232,12 +375,19 @@ module Checkoff
|
|
232
375
|
AndFunctionEvaluator,
|
233
376
|
DuePFunctionEvaluator,
|
234
377
|
DueDateSetPFunctionEvaluator,
|
378
|
+
CustomFieldLessThanNDaysFromNowFunctionEvaluator,
|
379
|
+
StringLiteralEvaluator,
|
235
380
|
].freeze
|
236
381
|
|
382
|
+
# @param task_selector [Array]
|
383
|
+
# @return [Boolean, Object, nil]
|
237
384
|
def evaluate(task_selector)
|
238
|
-
return true if task_selector
|
385
|
+
return true if task_selector.empty?
|
239
386
|
|
387
|
+
# @param evaluator_class [Class<FunctionEvaluator>]
|
240
388
|
FUNCTION_EVALUTORS.each do |evaluator_class|
|
389
|
+
# @sg-ignore
|
390
|
+
# @type [FunctionEvaluator]
|
241
391
|
evaluator = evaluator_class.new(task_selector: task_selector,
|
242
392
|
tasks: tasks)
|
243
393
|
|
@@ -246,23 +396,37 @@ module Checkoff
|
|
246
396
|
return try_this_evaluator(task_selector, evaluator)
|
247
397
|
end
|
248
398
|
|
249
|
-
raise "Syntax issue trying to handle #{task_selector}"
|
399
|
+
raise "Syntax issue trying to handle #{task_selector.inspect}"
|
250
400
|
end
|
251
401
|
|
252
402
|
private
|
253
403
|
|
404
|
+
# @sg-ignore
|
405
|
+
# @param task_selector [Array]
|
406
|
+
# @param evaluator [FunctionEvaluator]
|
407
|
+
# @return [Boolean, Object, nil]
|
254
408
|
def try_this_evaluator(task_selector, evaluator)
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
409
|
+
# if task_selector is an array
|
410
|
+
evaluated_args = if task_selector.is_a?(Array)
|
411
|
+
task_selector[1..].map.with_index do |item, index|
|
412
|
+
if evaluator.evaluate_arg?(index)
|
413
|
+
evaluate(item)
|
414
|
+
else
|
415
|
+
item
|
416
|
+
end
|
417
|
+
end
|
418
|
+
else
|
419
|
+
[]
|
420
|
+
end
|
262
421
|
|
263
422
|
evaluator.evaluate(task, *evaluated_args)
|
264
423
|
end
|
265
424
|
|
266
|
-
|
425
|
+
# @return [Asana::Resources::Task]
|
426
|
+
attr_reader :task
|
427
|
+
# @return [Checkoff::Tasks]
|
428
|
+
attr_reader :tasks
|
429
|
+
# @return [Array<(Symbol, Array)>]
|
430
|
+
attr_reader :task_selector
|
267
431
|
end
|
268
432
|
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.42.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-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|