checkoff 0.243.0 → 0.244.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/checkoff.gemspec +39 -22
- data/lib/checkoff/attachments.rb +9 -9
- data/lib/checkoff/cli.rb +28 -16
- data/lib/checkoff/clients.rb +2 -0
- data/lib/checkoff/custom_fields.rb +5 -8
- data/lib/checkoff/events.rb +0 -2
- data/lib/checkoff/internal/asana_event_enrichment.rb +6 -4
- data/lib/checkoff/internal/asana_event_filter.rb +0 -3
- data/lib/checkoff/internal/config_loader.rb +1 -1
- data/lib/checkoff/internal/logging.rb +2 -0
- data/lib/checkoff/internal/project_hashes.rb +0 -1
- data/lib/checkoff/internal/project_timing.rb +0 -5
- data/lib/checkoff/internal/search_url/custom_field_param_converter.rb +6 -5
- data/lib/checkoff/internal/search_url/custom_field_variant.rb +0 -1
- data/lib/checkoff/internal/search_url/date_param_converter.rb +2 -5
- data/lib/checkoff/internal/search_url/parser.rb +4 -0
- data/lib/checkoff/internal/search_url/results_merger.rb +1 -0
- data/lib/checkoff/internal/search_url/simple_param_converter.rb +2 -4
- data/lib/checkoff/internal/selector_classes/common.rb +19 -1
- data/lib/checkoff/internal/selector_classes/function_evaluator.rb +1 -1
- data/lib/checkoff/internal/selector_classes/project.rb +8 -0
- data/lib/checkoff/internal/selector_classes/section.rb +2 -2
- data/lib/checkoff/internal/selector_classes/task/function_evaluator.rb +9 -0
- data/lib/checkoff/internal/selector_classes/task.rb +28 -27
- data/lib/checkoff/internal/selector_evaluator.rb +2 -0
- data/lib/checkoff/internal/task_hashes.rb +0 -3
- data/lib/checkoff/internal/task_timing.rb +4 -6
- data/lib/checkoff/internal/thread_local.rb +1 -1
- data/lib/checkoff/monkeypatches/resource_marshalling.rb +0 -2
- data/lib/checkoff/my_tasks.rb +2 -1
- data/lib/checkoff/portfolios.rb +1 -0
- data/lib/checkoff/projects.rb +5 -5
- data/lib/checkoff/resources.rb +0 -2
- data/lib/checkoff/section_selectors.rb +0 -1
- data/lib/checkoff/sections.rb +18 -14
- data/lib/checkoff/subtasks.rb +1 -1
- data/lib/checkoff/tags.rb +2 -4
- data/lib/checkoff/task_searches.rb +2 -8
- data/lib/checkoff/task_selectors.rb +2 -3
- data/lib/checkoff/tasks.rb +18 -11
- data/lib/checkoff/timelines.rb +12 -6
- data/lib/checkoff/timing.rb +5 -8
- data/lib/checkoff/version.rb +1 -1
- data/lib/checkoff/workspaces.rb +5 -4
- data/rbi/checkoff.rbi +618 -483
- data/sig/checkoff.rbs +574 -481
- metadata +1 -1
|
@@ -14,8 +14,8 @@ module Checkoff
|
|
|
14
14
|
@date_url_params = date_url_params
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
# @sg-ignore
|
|
18
17
|
# @return [Array(Hash{String => String}, Array<Symbol, Array>)]
|
|
18
|
+
# @sg-ignore
|
|
19
19
|
def convert
|
|
20
20
|
return [{}, []] if date_url_params.empty?
|
|
21
21
|
|
|
@@ -78,7 +78,6 @@ module Checkoff
|
|
|
78
78
|
|
|
79
79
|
validate_unit_is_day!(prefix)
|
|
80
80
|
|
|
81
|
-
# @sg-ignore
|
|
82
81
|
# @type [Date]
|
|
83
82
|
before = Date.today + value
|
|
84
83
|
|
|
@@ -99,7 +98,6 @@ module Checkoff
|
|
|
99
98
|
# Example value: 1702857600000
|
|
100
99
|
# +1 is because API seems to operate on inclusive ranges
|
|
101
100
|
# @type [Date]
|
|
102
|
-
# @sg-ignore
|
|
103
101
|
after = Time.at(after.to_i / 1000).to_date + 1
|
|
104
102
|
[{ "#{API_PREFIX.fetch(prefix)}.after" => after.to_s }, []]
|
|
105
103
|
end
|
|
@@ -111,7 +109,6 @@ module Checkoff
|
|
|
111
109
|
|
|
112
110
|
validate_unit_is_day!(prefix)
|
|
113
111
|
|
|
114
|
-
# @sg-ignore
|
|
115
112
|
# @type [Date]
|
|
116
113
|
after = Date.today - value
|
|
117
114
|
|
|
@@ -125,7 +122,6 @@ module Checkoff
|
|
|
125
122
|
|
|
126
123
|
validate_unit_is_day!(prefix)
|
|
127
124
|
|
|
128
|
-
# @sg-ignore
|
|
129
125
|
# @type [Date]
|
|
130
126
|
before = Date.today + value + 1
|
|
131
127
|
|
|
@@ -133,6 +129,7 @@ module Checkoff
|
|
|
133
129
|
end
|
|
134
130
|
|
|
135
131
|
# @param param_key [String]
|
|
132
|
+
# @sg-ignore
|
|
136
133
|
# @return [String]
|
|
137
134
|
def get_single_param(param_key)
|
|
138
135
|
raise "Expected #{param_key} to have at least one value" unless date_url_params.key? param_key
|
|
@@ -24,7 +24,9 @@ module Checkoff
|
|
|
24
24
|
# @param url [String]
|
|
25
25
|
# @return [Array(Hash{String => String}, Array)]
|
|
26
26
|
def convert_params(url)
|
|
27
|
+
# @sg-ignore
|
|
27
28
|
url_params = CGI.parse(URI.parse(url).query)
|
|
29
|
+
# @sg-ignore
|
|
28
30
|
custom_field_params, date_url_params, simple_url_params = partition_url_params(url_params)
|
|
29
31
|
custom_field_args, custom_field_task_selector = convert_custom_field_params(custom_field_params)
|
|
30
32
|
date_url_args, date_task_selector = convert_date_params(date_url_params)
|
|
@@ -58,8 +60,10 @@ module Checkoff
|
|
|
58
60
|
# @return [Array(Hash{String => Array<String>}, Hash{String => Array<String>}, Hash{String => Array<String>})]
|
|
59
61
|
def partition_url_params(url_params)
|
|
60
62
|
groups = T.let(url_params.to_a.group_by do |key, _values|
|
|
63
|
+
# @sg-ignore
|
|
61
64
|
if key.start_with? 'custom_field_'
|
|
62
65
|
:custom_field
|
|
66
|
+
# @sg-ignore
|
|
63
67
|
elsif key.include? '_date'
|
|
64
68
|
:date
|
|
65
69
|
else
|
|
@@ -21,7 +21,6 @@ module Checkoff
|
|
|
21
21
|
|
|
22
22
|
private
|
|
23
23
|
|
|
24
|
-
# @sg-ignore
|
|
25
24
|
# @return [String] the single value of the search url param
|
|
26
25
|
def single_value
|
|
27
26
|
@single_value ||= begin
|
|
@@ -48,7 +47,6 @@ module Checkoff
|
|
|
48
47
|
# @return [void]
|
|
49
48
|
def parse_projects_and_sections(projects, sections)
|
|
50
49
|
single_value.split('~').each do |project_section_pair|
|
|
51
|
-
# @sg-ignore
|
|
52
50
|
project, section = project_section_pair.split('_column_')
|
|
53
51
|
raise "Invalid query string: #{project_section_pair}" if project.nil?
|
|
54
52
|
|
|
@@ -228,6 +226,7 @@ module Checkoff
|
|
|
228
226
|
# @return [Hash{String => String}] the converted params
|
|
229
227
|
def convert
|
|
230
228
|
# @type [Array<Array(String, String)>]
|
|
229
|
+
# @sg-ignore
|
|
231
230
|
arr_of_tuples = simple_url_params.to_a.flat_map do |key, values|
|
|
232
231
|
# @type
|
|
233
232
|
entry = convert_arg(key, values).each_slice(2).to_a
|
|
@@ -264,14 +263,13 @@ module Checkoff
|
|
|
264
263
|
private_constant :ARGS
|
|
265
264
|
|
|
266
265
|
# https://developers.asana.com/docs/search-tasks-in-a-workspace
|
|
267
|
-
# @sg-ignore
|
|
268
266
|
# @param key [String] the name of the search url param
|
|
269
267
|
# @param values [Array<String>] the values of the search url param
|
|
268
|
+
# @sg-ignore
|
|
270
269
|
# @return [Hash{String => String}] the converted params
|
|
271
270
|
def convert_arg(key, values)
|
|
272
271
|
# @type [Class<SimpleParam::SimpleParam>]
|
|
273
272
|
clazz = ARGS.fetch(key)
|
|
274
|
-
# @sg-ignore
|
|
275
273
|
# @type [SimpleParam::SimpleParam]
|
|
276
274
|
obj = clazz.new(key:, values:)
|
|
277
275
|
# @sg-ignore
|
|
@@ -105,9 +105,12 @@ module Checkoff
|
|
|
105
105
|
# @param custom_field_name [String]
|
|
106
106
|
# @return [String, nil]
|
|
107
107
|
def evaluate(resource, custom_field_name)
|
|
108
|
+
# @sg-ignore
|
|
108
109
|
custom_field = @custom_fields.resource_custom_field_by_name(resource, custom_field_name)
|
|
110
|
+
# @sg-ignore
|
|
109
111
|
return nil if custom_field.nil?
|
|
110
112
|
|
|
113
|
+
# @sg-ignore
|
|
111
114
|
custom_field['display_value']
|
|
112
115
|
end
|
|
113
116
|
end
|
|
@@ -124,10 +127,14 @@ module Checkoff
|
|
|
124
127
|
|
|
125
128
|
# @sg-ignore
|
|
126
129
|
# @param resource [Asana::Resources::Task,Asana::Resources::Project]
|
|
130
|
+
# @sg-ignore
|
|
127
131
|
# @param custom_field_gid [String]
|
|
128
132
|
# @return [String, nil]
|
|
133
|
+
# @sg-ignore
|
|
129
134
|
def evaluate(resource, custom_field_gid)
|
|
135
|
+
# @sg-ignore
|
|
130
136
|
custom_field = @custom_fields.resource_custom_field_by_gid_or_raise(resource, custom_field_gid)
|
|
137
|
+
# @sg-ignore
|
|
131
138
|
custom_field['display_value']
|
|
132
139
|
end
|
|
133
140
|
end
|
|
@@ -145,13 +152,17 @@ module Checkoff
|
|
|
145
152
|
end
|
|
146
153
|
|
|
147
154
|
# @param resource [Asana::Resources::Task,Asana::Resources::Project]
|
|
155
|
+
# @sg-ignore
|
|
148
156
|
# @param custom_field_gid [String]
|
|
149
157
|
# @param custom_field_values_gids [Array<String>]
|
|
150
158
|
# @return [Boolean]
|
|
159
|
+
# @sg-ignore
|
|
151
160
|
def evaluate(resource, custom_field_gid, custom_field_values_gids)
|
|
161
|
+
# @sg-ignore
|
|
152
162
|
actual_custom_field_values_gids = @custom_fields.resource_custom_field_values_gids_or_raise(resource,
|
|
153
163
|
custom_field_gid)
|
|
154
164
|
|
|
165
|
+
# @sg-ignore
|
|
155
166
|
actual_custom_field_values_gids.any? do |custom_field_value|
|
|
156
167
|
custom_field_values_gids.include?(custom_field_value)
|
|
157
168
|
end
|
|
@@ -172,13 +183,17 @@ module Checkoff
|
|
|
172
183
|
|
|
173
184
|
# @param resource [Asana::Resources::Task,Asana::Resources::Project]
|
|
174
185
|
# @param custom_field_name [String]
|
|
186
|
+
# @sg-ignore
|
|
175
187
|
# @param custom_field_value_names [Array<String>]
|
|
188
|
+
# @sg-ignore
|
|
176
189
|
# @return [Boolean]
|
|
177
190
|
def evaluate(resource, custom_field_name, custom_field_value_names)
|
|
178
191
|
actual_custom_field_values_names =
|
|
192
|
+
# @sg-ignore
|
|
179
193
|
@custom_fields.resource_custom_field_values_names_by_name(resource,
|
|
180
194
|
custom_field_name)
|
|
181
195
|
|
|
196
|
+
# @sg-ignore
|
|
182
197
|
actual_custom_field_values_names.any? do |custom_field_value|
|
|
183
198
|
custom_field_value_names.include?(custom_field_value)
|
|
184
199
|
end
|
|
@@ -203,10 +218,12 @@ module Checkoff
|
|
|
203
218
|
# @return [Boolean]
|
|
204
219
|
def evaluate(resource, custom_field_gid, custom_field_values_gids)
|
|
205
220
|
actual_custom_field_values_gids =
|
|
221
|
+
# @sg-ignore
|
|
206
222
|
@custom_fields.resource_custom_field_values_gids_or_raise(resource,
|
|
207
223
|
custom_field_gid)
|
|
208
224
|
|
|
209
225
|
custom_field_values_gids.all? do |custom_field_value|
|
|
226
|
+
# @sg-ignore
|
|
210
227
|
actual_custom_field_values_gids.include?(custom_field_value)
|
|
211
228
|
end
|
|
212
229
|
end
|
|
@@ -225,6 +242,7 @@ module Checkoff
|
|
|
225
242
|
end
|
|
226
243
|
|
|
227
244
|
# @param resource [Asana::Resources::Task, Asana::Resources::Project]
|
|
245
|
+
# @sg-ignore
|
|
228
246
|
# @param prefix [String]
|
|
229
247
|
# @return [boolish]
|
|
230
248
|
def evaluate(resource, prefix)
|
|
@@ -238,9 +256,9 @@ module Checkoff
|
|
|
238
256
|
selector.is_a?(String)
|
|
239
257
|
end
|
|
240
258
|
|
|
241
|
-
# @sg-ignore
|
|
242
259
|
# @param _resource [Asana::Resources::Task,Asana::Resources::Project]
|
|
243
260
|
# @return [String]
|
|
261
|
+
# @sg-ignore
|
|
244
262
|
def evaluate(_resource)
|
|
245
263
|
T.cast(selector, String)
|
|
246
264
|
end
|
|
@@ -17,6 +17,7 @@ module Checkoff
|
|
|
17
17
|
|
|
18
18
|
# @param resource [Asana::Resources::Project]
|
|
19
19
|
# @return [String, nil]
|
|
20
|
+
# @sg-ignore
|
|
20
21
|
def evaluate(resource)
|
|
21
22
|
resource.due_date
|
|
22
23
|
end
|
|
@@ -32,8 +33,10 @@ module Checkoff
|
|
|
32
33
|
|
|
33
34
|
# @param project [Asana::Resources::Project]
|
|
34
35
|
# @param period [Symbol] - :now_or_before or :this_week
|
|
36
|
+
# @sg-ignore
|
|
35
37
|
# @return [Boolean]
|
|
36
38
|
def evaluate(project, period = :now_or_before)
|
|
39
|
+
# @sg-ignore
|
|
37
40
|
@projects.project_ready?(project, period:)
|
|
38
41
|
end
|
|
39
42
|
end
|
|
@@ -50,13 +53,18 @@ module Checkoff
|
|
|
50
53
|
# @param portfolio_name [String]
|
|
51
54
|
# @param workspace_name [String, nil]
|
|
52
55
|
# @param extra_project_fields [Array<String>]
|
|
56
|
+
# @sg-ignore
|
|
53
57
|
#
|
|
58
|
+
# @sg-ignore
|
|
54
59
|
# @return [Boolean]
|
|
55
60
|
def evaluate(project, portfolio_name, workspace_name: nil, extra_project_fields: [])
|
|
56
61
|
workspace_name ||= project.workspace&.name
|
|
62
|
+
# @sg-ignore
|
|
57
63
|
workspace_name ||= @workspaces.default_workspace.name
|
|
64
|
+
# @sg-ignore
|
|
58
65
|
projects = @portfolios.projects_in_portfolio(workspace_name, portfolio_name,
|
|
59
66
|
extra_project_fields:)
|
|
67
|
+
# @sg-ignore
|
|
60
68
|
projects.any? { |p| p.name == project.name }
|
|
61
69
|
end
|
|
62
70
|
end
|
|
@@ -17,13 +17,12 @@ module Checkoff
|
|
|
17
17
|
|
|
18
18
|
# @param section [Asana::Resources::Section]
|
|
19
19
|
#
|
|
20
|
-
# @sg-ignore
|
|
21
20
|
# @return [Boolean]
|
|
21
|
+
# @sg-ignore
|
|
22
22
|
def evaluate(section)
|
|
23
23
|
tasks = client.tasks.get_tasks(section: section.gid,
|
|
24
24
|
per_page: 100,
|
|
25
25
|
options: { fields: ['resource_subtype'] })
|
|
26
|
-
# @sg-ignore
|
|
27
26
|
T.unsafe(tasks).last&.resource_subtype == 'milestone'
|
|
28
27
|
end
|
|
29
28
|
end
|
|
@@ -41,6 +40,7 @@ module Checkoff
|
|
|
41
40
|
# @sg-ignore
|
|
42
41
|
# @return [Boolean]
|
|
43
42
|
def evaluate(section)
|
|
43
|
+
# @sg-ignore
|
|
44
44
|
@sections.tasks_by_section_gid(section.gid).any?
|
|
45
45
|
end
|
|
46
46
|
end
|
|
@@ -25,6 +25,15 @@ module Checkoff
|
|
|
25
25
|
super()
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
# @return [Checkoff::Tasks]
|
|
29
|
+
attr_reader :tasks
|
|
30
|
+
|
|
31
|
+
# @return [Checkoff::Timelines]
|
|
32
|
+
attr_reader :timelines
|
|
33
|
+
|
|
34
|
+
# @return [Checkoff::CustomFields]
|
|
35
|
+
attr_reader :custom_fields
|
|
36
|
+
|
|
28
37
|
private
|
|
29
38
|
|
|
30
39
|
# @return [Array(Symbol, Array)]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: true
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require_relative 'task/function_evaluator'
|
|
@@ -18,12 +18,11 @@ module Checkoff
|
|
|
18
18
|
false
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
# @sg-ignore
|
|
22
21
|
# @param task [Asana::Resources::Task]
|
|
23
22
|
# @return [Boolean]
|
|
24
23
|
def evaluate(task)
|
|
25
24
|
# @type [Hash{'unwrapped' => Hash}]
|
|
26
|
-
task_data =
|
|
25
|
+
task_data = tasks.task_to_h(task)
|
|
27
26
|
# @type [Hash{'membership_by_project_name' => Hash}]
|
|
28
27
|
unwrapped = task_data.fetch('unwrapped')
|
|
29
28
|
# @type [Array]
|
|
@@ -43,14 +42,14 @@ module Checkoff
|
|
|
43
42
|
false
|
|
44
43
|
end
|
|
45
44
|
|
|
46
|
-
# @sg-ignore
|
|
47
45
|
# @param task [Asana::Resources::Task]
|
|
48
46
|
# @param section_name_prefix [String]
|
|
49
47
|
# @return [Boolean]
|
|
48
|
+
# @sg-ignore
|
|
50
49
|
def evaluate(task, section_name_prefix)
|
|
51
|
-
task_data =
|
|
50
|
+
task_data = tasks.task_to_h(task)
|
|
52
51
|
task_data.fetch('unwrapped').fetch('membership_by_section_name').keys.any? do |section_name|
|
|
53
|
-
section_name.start_with?
|
|
52
|
+
String(section_name).start_with?(section_name_prefix)
|
|
54
53
|
end
|
|
55
54
|
end
|
|
56
55
|
end
|
|
@@ -71,7 +70,7 @@ module Checkoff
|
|
|
71
70
|
# @param section_name [String]
|
|
72
71
|
# @return [Boolean]
|
|
73
72
|
def evaluate(task, section_name)
|
|
74
|
-
task_data =
|
|
73
|
+
task_data = tasks.task_to_h(task)
|
|
75
74
|
task_data.fetch('unwrapped').fetch('membership_by_section_name').keys.any?(section_name)
|
|
76
75
|
end
|
|
77
76
|
end
|
|
@@ -87,15 +86,16 @@ module Checkoff
|
|
|
87
86
|
false
|
|
88
87
|
end
|
|
89
88
|
|
|
90
|
-
# @sg-ignore
|
|
91
89
|
# @param task [Asana::Resources::Task]
|
|
92
90
|
# @param project_name [String]
|
|
93
91
|
# @return [Boolean]
|
|
92
|
+
# @sg-ignore
|
|
94
93
|
def evaluate(task, project_name)
|
|
95
94
|
project_names = task.memberships.map do |membership|
|
|
96
|
-
|
|
95
|
+
m = T.cast(membership, T::Hash[String, T.untyped])
|
|
96
|
+
T.cast(m.fetch('project'), T::Hash[String, T.untyped]).fetch('name')
|
|
97
97
|
end
|
|
98
|
-
project_names.include?
|
|
98
|
+
T.cast(project_names.include?(project_name), T::Boolean)
|
|
99
99
|
end
|
|
100
100
|
end
|
|
101
101
|
|
|
@@ -110,12 +110,11 @@ module Checkoff
|
|
|
110
110
|
false
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
-
# @sg-ignore
|
|
114
113
|
# @param task [Asana::Resources::Task]
|
|
115
114
|
# @param portfolio_name [String]
|
|
116
115
|
# @return [Boolean]
|
|
117
116
|
def evaluate(task, portfolio_name)
|
|
118
|
-
|
|
117
|
+
tasks.in_portfolio_more_than_once?(task, portfolio_name)
|
|
119
118
|
end
|
|
120
119
|
end
|
|
121
120
|
|
|
@@ -130,10 +129,10 @@ module Checkoff
|
|
|
130
129
|
false
|
|
131
130
|
end
|
|
132
131
|
|
|
133
|
-
# @sg-ignore
|
|
134
132
|
# @param task [Asana::Resources::Task]
|
|
135
133
|
# @param tag_name [String]
|
|
136
134
|
# @return [Boolean]
|
|
135
|
+
# @sg-ignore
|
|
137
136
|
def evaluate(task, tag_name)
|
|
138
137
|
task.tags.map(&:name).include? tag_name
|
|
139
138
|
end
|
|
@@ -158,7 +157,7 @@ module Checkoff
|
|
|
158
157
|
# @return [Boolean]
|
|
159
158
|
# rubocop:disable Style/OptionalBooleanParameter
|
|
160
159
|
def evaluate(task, period = :now_or_before, ignore_dependencies = false)
|
|
161
|
-
|
|
160
|
+
tasks.task_ready?(task, period:, ignore_dependencies:)
|
|
162
161
|
end
|
|
163
162
|
# rubocop:enable Style/OptionalBooleanParameter
|
|
164
163
|
end
|
|
@@ -179,7 +178,7 @@ module Checkoff
|
|
|
179
178
|
# @param period [Symbol,Array<Symbol>] See Checkoff::Timing#in_period?
|
|
180
179
|
# @return [Boolean]
|
|
181
180
|
def evaluate(task, field_name, period)
|
|
182
|
-
|
|
181
|
+
tasks.in_period?(task, field_name, period)
|
|
183
182
|
end
|
|
184
183
|
end
|
|
185
184
|
|
|
@@ -191,6 +190,7 @@ module Checkoff
|
|
|
191
190
|
|
|
192
191
|
# @param task [Asana::Resources::Task]
|
|
193
192
|
# @return [Boolean]
|
|
193
|
+
# @sg-ignore
|
|
194
194
|
def evaluate(task)
|
|
195
195
|
task.assignee.nil?
|
|
196
196
|
end
|
|
@@ -204,9 +204,9 @@ module Checkoff
|
|
|
204
204
|
fn?(selector, FUNCTION_NAME)
|
|
205
205
|
end
|
|
206
206
|
|
|
207
|
-
# @sg-ignore
|
|
208
207
|
# @param task [Asana::Resources::Task]
|
|
209
208
|
# @return [Boolean]
|
|
209
|
+
# @sg-ignore
|
|
210
210
|
def evaluate(task)
|
|
211
211
|
!task.due_at.nil? || !task.due_on.nil?
|
|
212
212
|
end
|
|
@@ -233,13 +233,15 @@ module Checkoff
|
|
|
233
233
|
|
|
234
234
|
# @type [Array<Asana::Resources::Story>]
|
|
235
235
|
stories = task.stories(per_page: 100).to_a.reject do |story|
|
|
236
|
+
# @sg-ignore
|
|
236
237
|
excluding_resource_subtypes.include? story.resource_subtype
|
|
237
238
|
end
|
|
238
239
|
return true if stories.empty? # no stories == infinitely old!
|
|
239
240
|
|
|
240
241
|
last_story = stories.last
|
|
242
|
+
# @sg-ignore
|
|
241
243
|
last_story_created_at = Time.parse(last_story.created_at)
|
|
242
|
-
n_days_ago = Time.now - (num_days *
|
|
244
|
+
n_days_ago = Time.at(Time.now.to_i - (num_days * 86_400))
|
|
243
245
|
last_story_created_at < n_days_ago
|
|
244
246
|
end
|
|
245
247
|
end
|
|
@@ -260,7 +262,6 @@ module Checkoff
|
|
|
260
262
|
start_on = Date.parse(task.start_on) unless task.start_on.nil?
|
|
261
263
|
due_on = Date.parse(task.due_on) unless task.due_on.nil?
|
|
262
264
|
allocated_hours = 8.0
|
|
263
|
-
# @sg-ignore
|
|
264
265
|
allocated_hours = (due_on - start_on + 1).to_i * 8.0 if start_on && due_on
|
|
265
266
|
allocated_hours
|
|
266
267
|
end
|
|
@@ -268,11 +269,10 @@ module Checkoff
|
|
|
268
269
|
# @param task [Asana::Resources::Task]
|
|
269
270
|
# @return [Boolean]
|
|
270
271
|
def evaluate(task)
|
|
271
|
-
custom_field =
|
|
272
|
+
custom_field = custom_fields.resource_custom_field_by_name(task, 'Estimated time')
|
|
272
273
|
|
|
273
274
|
return false if custom_field.nil?
|
|
274
275
|
|
|
275
|
-
# @sg-ignore
|
|
276
276
|
# @type [Integer, nil]
|
|
277
277
|
estimate_minutes = custom_field.fetch('number_value')
|
|
278
278
|
|
|
@@ -301,8 +301,8 @@ module Checkoff
|
|
|
301
301
|
#
|
|
302
302
|
# @return [Boolean]
|
|
303
303
|
def evaluate(task, limit_to_portfolio_gid: nil)
|
|
304
|
-
|
|
305
|
-
|
|
304
|
+
timelines.task_dependent_on_previous_section_last_milestone?(task,
|
|
305
|
+
limit_to_portfolio_gid:)
|
|
306
306
|
end
|
|
307
307
|
end
|
|
308
308
|
|
|
@@ -319,7 +319,7 @@ module Checkoff
|
|
|
319
319
|
#
|
|
320
320
|
# @return [Boolean]
|
|
321
321
|
def evaluate(task, portfolio_name)
|
|
322
|
-
|
|
322
|
+
tasks.in_portfolio_named?(task, portfolio_name)
|
|
323
323
|
end
|
|
324
324
|
end
|
|
325
325
|
|
|
@@ -340,8 +340,8 @@ module Checkoff
|
|
|
340
340
|
#
|
|
341
341
|
# @return [Boolean]
|
|
342
342
|
def evaluate(task, limit_to_portfolio_name = nil)
|
|
343
|
-
|
|
344
|
-
|
|
343
|
+
!timelines.last_task_milestone_depends_on_this_task?(task,
|
|
344
|
+
limit_to_portfolio_name:)
|
|
345
345
|
end
|
|
346
346
|
end
|
|
347
347
|
|
|
@@ -360,6 +360,7 @@ module Checkoff
|
|
|
360
360
|
# @param task [Asana::Resources::Task]
|
|
361
361
|
#
|
|
362
362
|
# @return [Boolean]
|
|
363
|
+
# @sg-ignore
|
|
363
364
|
def evaluate(task)
|
|
364
365
|
raise 'Please add resource_subtype to extra_fields' if task.resource_subtype.nil?
|
|
365
366
|
|
|
@@ -384,8 +385,8 @@ module Checkoff
|
|
|
384
385
|
#
|
|
385
386
|
# @return [Boolean]
|
|
386
387
|
def evaluate(task, limit_to_portfolio_name = nil)
|
|
387
|
-
|
|
388
|
-
|
|
388
|
+
!timelines.any_milestone_depends_on_this_task?(task,
|
|
389
|
+
limit_to_portfolio_name:)
|
|
389
390
|
end
|
|
390
391
|
end
|
|
391
392
|
end
|
|
@@ -17,6 +17,7 @@ module Checkoff
|
|
|
17
17
|
|
|
18
18
|
next unless evaluator.matches?
|
|
19
19
|
|
|
20
|
+
# @sg-ignore
|
|
20
21
|
return try_this_evaluator(selector, evaluator)
|
|
21
22
|
end
|
|
22
23
|
|
|
@@ -42,6 +43,7 @@ module Checkoff
|
|
|
42
43
|
def evaluate_args(selector, evaluator)
|
|
43
44
|
return [] unless selector.is_a?(Array)
|
|
44
45
|
|
|
46
|
+
# @sg-ignore
|
|
45
47
|
selector[1..].map.with_index do |item, index|
|
|
46
48
|
if evaluator.evaluate_arg?(index)
|
|
47
49
|
evaluate(item)
|
|
@@ -34,7 +34,6 @@ module Checkoff
|
|
|
34
34
|
# @param task_hash [Hash]
|
|
35
35
|
# @return [void]
|
|
36
36
|
def unwrap_custom_fields(task_hash)
|
|
37
|
-
# @sg-ignore
|
|
38
37
|
# @type [Array<Hash>,nil]
|
|
39
38
|
custom_fields = task_hash.fetch('custom_fields', nil)
|
|
40
39
|
|
|
@@ -72,7 +71,6 @@ module Checkoff
|
|
|
72
71
|
#
|
|
73
72
|
# @return [void]
|
|
74
73
|
def unwrap_memberships(task_hash, memberships, resource, key)
|
|
75
|
-
# @sg-ignore
|
|
76
74
|
# @type [Hash]
|
|
77
75
|
unwrapped = task_hash.fetch('unwrapped')
|
|
78
76
|
unwrapped["membership_by_#{resource}_#{key}"] = memberships.group_by do |membership|
|
|
@@ -83,7 +81,6 @@ module Checkoff
|
|
|
83
81
|
# @param task_hash [Hash]
|
|
84
82
|
# @return [void]
|
|
85
83
|
def unwrap_all_memberships(task_hash)
|
|
86
|
-
# @sg-ignore
|
|
87
84
|
# @type [Array<Hash>]
|
|
88
85
|
memberships = task_hash.fetch('memberships', []).dup
|
|
89
86
|
add_user_task_list(task_hash, memberships)
|
|
@@ -19,11 +19,13 @@ module Checkoff
|
|
|
19
19
|
|
|
20
20
|
# @param task [Asana::Resources::Task]
|
|
21
21
|
# @return [Time, nil]
|
|
22
|
+
# @sg-ignore
|
|
22
23
|
def start_time(task)
|
|
23
24
|
date_or_time_field_by_name(task, :start)&.to_time
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
# @param task [Asana::Resources::Task]
|
|
28
|
+
# @sg-ignore
|
|
27
29
|
# @return [Time, nil]
|
|
28
30
|
def due_time(task)
|
|
29
31
|
date_or_time_field_by_name(task, :due)&.to_time
|
|
@@ -31,9 +33,9 @@ module Checkoff
|
|
|
31
33
|
|
|
32
34
|
# @param task [Asana::Resources::Task]
|
|
33
35
|
#
|
|
34
|
-
# @sg-ignore
|
|
35
36
|
# @return [Date, Time, nil]
|
|
36
37
|
def start_date_or_time(task)
|
|
38
|
+
# @sg-ignore
|
|
37
39
|
return @time_class.parse(task.start_at).localtime unless task.start_at.nil?
|
|
38
40
|
|
|
39
41
|
return @date_class.parse(task.start_on) unless task.start_on.nil?
|
|
@@ -43,10 +45,9 @@ module Checkoff
|
|
|
43
45
|
|
|
44
46
|
# @param task [Asana::Resources::Task]
|
|
45
47
|
#
|
|
46
|
-
# @sg-ignore
|
|
47
48
|
# @return [Date, Time, nil]
|
|
48
49
|
def due_date_or_time(task)
|
|
49
|
-
return @time_class.parse(task.due_at).localtime unless task.due_at.nil?
|
|
50
|
+
return @time_class.parse(T.must(task.due_at)).localtime unless task.due_at.nil?
|
|
50
51
|
|
|
51
52
|
return @date_class.parse(task.due_on) unless task.due_on.nil?
|
|
52
53
|
|
|
@@ -66,7 +67,6 @@ module Checkoff
|
|
|
66
67
|
# @return [Time, Date, nil]
|
|
67
68
|
def custom_field(task, custom_field_name)
|
|
68
69
|
custom_field = @custom_fields.resource_custom_field_by_name_or_raise(task, custom_field_name)
|
|
69
|
-
# @sg-ignore
|
|
70
70
|
# @type [String, nil]
|
|
71
71
|
time_str = custom_field.fetch('display_value')
|
|
72
72
|
return nil if time_str.nil?
|
|
@@ -77,7 +77,6 @@ module Checkoff
|
|
|
77
77
|
# @param task [Asana::Resources::Task]
|
|
78
78
|
# @param field_name [Symbol,Array]
|
|
79
79
|
#
|
|
80
|
-
# @sg-ignore
|
|
81
80
|
# @return [Date, Time, nil]
|
|
82
81
|
def date_or_time_field_by_name(task, field_name)
|
|
83
82
|
return due_date_or_time(task) if field_name == :due
|
|
@@ -89,7 +88,6 @@ module Checkoff
|
|
|
89
88
|
return start_date_or_time(task) || due_date_or_time(task) if field_name == :ready
|
|
90
89
|
|
|
91
90
|
if field_name.is_a?(Array)
|
|
92
|
-
# @sg-ignore
|
|
93
91
|
# @type [Symbol]
|
|
94
92
|
actual_field_name = field_name.first
|
|
95
93
|
args = field_name[1..]
|
|
@@ -9,8 +9,8 @@ module Checkoff
|
|
|
9
9
|
# @param name [Symbol]
|
|
10
10
|
# @param value [Object,Boolean]
|
|
11
11
|
# @yieldreturn [generic<T>]
|
|
12
|
-
# @sg-ignore
|
|
13
12
|
# @return [generic<T>]
|
|
13
|
+
# @sg-ignore
|
|
14
14
|
def with_thread_local_variable(name, value, &block)
|
|
15
15
|
old_value = Thread.current[name]
|
|
16
16
|
Thread.current[name] = value
|
data/lib/checkoff/my_tasks.rb
CHANGED
|
@@ -46,7 +46,7 @@ module Checkoff
|
|
|
46
46
|
by_my_tasks_section(active_tasks, project.gid)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
# @param name [String]
|
|
49
|
+
# @param name [String, nil]
|
|
50
50
|
# @return [String, nil]
|
|
51
51
|
def section_key(name)
|
|
52
52
|
return nil if name == 'Recently assigned'
|
|
@@ -67,6 +67,7 @@ module Checkoff
|
|
|
67
67
|
sections.each_entry { |section| by_section[section_key(section.name)] = [] }
|
|
68
68
|
tasks.each do |task|
|
|
69
69
|
assignee_section = task.assignee_section
|
|
70
|
+
# @sg-ignore
|
|
70
71
|
current_section = section_key(assignee_section.name)
|
|
71
72
|
by_section[current_section] ||= []
|
|
72
73
|
by_section[current_section] << task
|
data/lib/checkoff/portfolios.rb
CHANGED
|
@@ -47,6 +47,7 @@ module Checkoff
|
|
|
47
47
|
# @param portfolio_name [String]
|
|
48
48
|
#
|
|
49
49
|
# @return [Asana::Resources::Portfolio]
|
|
50
|
+
# @sg-ignore
|
|
50
51
|
def portfolio_or_raise(workspace_name, portfolio_name)
|
|
51
52
|
portfolio_obj = portfolio(workspace_name, portfolio_name)
|
|
52
53
|
raise "Could not find portfolio #{portfolio_name} under workspace #{workspace_name}." if portfolio_obj.nil?
|