checkoff 0.40.0 → 0.41.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 +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
|