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
data/lib/checkoff/projects.rb
CHANGED
|
@@ -97,7 +97,7 @@ module Checkoff
|
|
|
97
97
|
end
|
|
98
98
|
|
|
99
99
|
# pulls an Asana API project class given a name
|
|
100
|
-
# @param [String] workspace_name
|
|
100
|
+
# @param [String, Symbol] workspace_name
|
|
101
101
|
# @param [String,Symbol] project_name - :my_tasks or a project name
|
|
102
102
|
# @param [Array<String>] extra_fields
|
|
103
103
|
#
|
|
@@ -116,10 +116,11 @@ module Checkoff
|
|
|
116
116
|
end
|
|
117
117
|
cache_method :project, REALLY_LONG_CACHE_TIME
|
|
118
118
|
|
|
119
|
-
# @param workspace_name [String]
|
|
119
|
+
# @param workspace_name [String, Symbol]
|
|
120
120
|
# @param project_name [String,Symbol] - :my_tasks or a project name
|
|
121
121
|
# @param [Array<String>] extra_fields
|
|
122
122
|
#
|
|
123
|
+
# @sg-ignore
|
|
123
124
|
# @return [Asana::Resources::Project]
|
|
124
125
|
def project_or_raise(workspace_name, project_name, extra_fields: [])
|
|
125
126
|
p = project(workspace_name, project_name, extra_fields:)
|
|
@@ -182,7 +183,7 @@ module Checkoff
|
|
|
182
183
|
end
|
|
183
184
|
cache_method :tasks_from_project_gid, REALLY_LONG_CACHE_TIME
|
|
184
185
|
|
|
185
|
-
# @param [String] workspace_name
|
|
186
|
+
# @param [String, Symbol] workspace_name
|
|
186
187
|
# @param [Array<String>] extra_fields
|
|
187
188
|
# @return [Enumerable<Asana::Resources::Project>]
|
|
188
189
|
def projects_by_workspace_name(workspace_name, extra_fields: [])
|
|
@@ -254,12 +255,11 @@ module Checkoff
|
|
|
254
255
|
end
|
|
255
256
|
cache_method :projects, LONG_CACHE_TIME
|
|
256
257
|
|
|
257
|
-
# @param [String] workspace_name
|
|
258
|
+
# @param [String, Symbol] workspace_name
|
|
258
259
|
#
|
|
259
260
|
# @return [Asana::Resources::Project]
|
|
260
261
|
def my_tasks(workspace_name)
|
|
261
262
|
workspace = @workspaces.workspace_or_raise(workspace_name)
|
|
262
|
-
# @sg-ignore
|
|
263
263
|
result = client.user_task_lists.get_user_task_list_for_user(user_gid: 'me',
|
|
264
264
|
workspace: workspace.gid)
|
|
265
265
|
gid = result.gid
|
data/lib/checkoff/resources.rb
CHANGED
|
@@ -113,10 +113,8 @@ module Checkoff
|
|
|
113
113
|
class << self
|
|
114
114
|
# @return [void]
|
|
115
115
|
def run
|
|
116
|
-
# # @sg-ignore
|
|
117
116
|
# # @type [String]
|
|
118
117
|
# workspace_name = ARGV[0] || raise('Please pass workspace name as first argument')
|
|
119
|
-
# # @sg-ignore
|
|
120
118
|
# # @type [String]
|
|
121
119
|
# resource_name = ARGV[1] || raise('Please pass resource name as second argument')
|
|
122
120
|
# resources = Checkoff::Resources.new
|
|
@@ -43,7 +43,6 @@ module Checkoff
|
|
|
43
43
|
# section details. Examples: [:tag, 'foo'] [:not, [:tag, 'foo']] [:tag, 'foo']
|
|
44
44
|
# @return [Boolean]
|
|
45
45
|
def filter_via_section_selector(section, section_selector)
|
|
46
|
-
# @sg-ignore
|
|
47
46
|
evaluator = SectionSelectorEvaluator.new(section:, sections:, custom_fields:,
|
|
48
47
|
client:)
|
|
49
48
|
!!evaluator.evaluate(section_selector)
|
data/lib/checkoff/sections.rb
CHANGED
|
@@ -55,7 +55,7 @@ module Checkoff
|
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
# Returns a list of Asana API section objects for a given project
|
|
58
|
-
# @param workspace_name [String]
|
|
58
|
+
# @param workspace_name [String, Symbol]
|
|
59
59
|
# @param project_name [String, Symbol]
|
|
60
60
|
# @param extra_fields [Array<String>]
|
|
61
61
|
#
|
|
@@ -115,9 +115,9 @@ module Checkoff
|
|
|
115
115
|
|
|
116
116
|
# Pulls task objects from a specified section
|
|
117
117
|
#
|
|
118
|
-
# @param workspace_name [String]
|
|
118
|
+
# @param workspace_name [String, Symbol]
|
|
119
119
|
# @param project_name [String, Symbol]
|
|
120
|
-
# @param section_name [String, nil]
|
|
120
|
+
# @param section_name [String, nil, Symbol]
|
|
121
121
|
# @param only_uncompleted [Boolean]
|
|
122
122
|
# @param extra_fields [Array<String>]
|
|
123
123
|
#
|
|
@@ -141,8 +141,8 @@ module Checkoff
|
|
|
141
141
|
# @param project_name [String, Symbol]
|
|
142
142
|
# @param section_name [String, nil]
|
|
143
143
|
#
|
|
144
|
-
# @sg-ignore
|
|
145
144
|
# @return [Array<String>]
|
|
145
|
+
# @sg-ignore
|
|
146
146
|
def section_task_names(workspace_name, project_name, section_name)
|
|
147
147
|
task_array = tasks(workspace_name, project_name, section_name)
|
|
148
148
|
# @type [Array<String>]
|
|
@@ -150,13 +150,13 @@ module Checkoff
|
|
|
150
150
|
end
|
|
151
151
|
cache_method :section_task_names, SHORT_CACHE_TIME
|
|
152
152
|
|
|
153
|
-
# @param workspace_name [String]
|
|
153
|
+
# @param workspace_name [String, Symbol]
|
|
154
154
|
# @param project_name [String, Symbol]
|
|
155
|
-
# @param section_name [String, nil]
|
|
155
|
+
# @param section_name [String, nil, Symbol]
|
|
156
156
|
# @param extra_section_fields [Array<String>]
|
|
157
157
|
#
|
|
158
|
-
# @sg-ignore
|
|
159
158
|
# @return [Asana::Resources::Section]
|
|
159
|
+
# @sg-ignore section() is nil-checked below
|
|
160
160
|
def section_or_raise(workspace_name, project_name, section_name, extra_section_fields: [])
|
|
161
161
|
s = section(workspace_name, project_name, section_name,
|
|
162
162
|
extra_section_fields:)
|
|
@@ -171,7 +171,7 @@ module Checkoff
|
|
|
171
171
|
end
|
|
172
172
|
cache_method :section_or_raise, LONG_CACHE_TIME
|
|
173
173
|
|
|
174
|
-
# @param name [String]
|
|
174
|
+
# @param name [String, nil]
|
|
175
175
|
# @return [String, nil]
|
|
176
176
|
def section_key(name)
|
|
177
177
|
inbox_section_names = ['(no section)', 'Untitled section', 'Inbox', 'Recently assigned']
|
|
@@ -187,11 +187,13 @@ module Checkoff
|
|
|
187
187
|
sections = sections_by_project_gid(section.project.fetch('gid'))
|
|
188
188
|
|
|
189
189
|
# @type [Array<Asana::Resources::Section>]
|
|
190
|
+
# @sg-ignore
|
|
190
191
|
sections = sections.to_a
|
|
191
192
|
|
|
192
193
|
index = sections.find_index { |s| s.gid == section.gid }
|
|
193
194
|
return nil if index.nil? || index.zero?
|
|
194
195
|
|
|
196
|
+
# @sg-ignore
|
|
195
197
|
sections[index - 1]
|
|
196
198
|
end
|
|
197
199
|
cache_method :previous_section, SHORT_CACHE_TIME
|
|
@@ -214,10 +216,9 @@ module Checkoff
|
|
|
214
216
|
{}
|
|
215
217
|
end
|
|
216
218
|
|
|
217
|
-
# @
|
|
218
|
-
# @param workspace_name [String]
|
|
219
|
+
# @param workspace_name [String, Symbol]
|
|
219
220
|
# @param project_name [String, Symbol]
|
|
220
|
-
# @param section_name [String, nil]
|
|
221
|
+
# @param section_name [String, nil, Symbol]
|
|
221
222
|
# @param extra_section_fields [Array<String>]
|
|
222
223
|
#
|
|
223
224
|
# @return [Asana::Resources::Section, nil]
|
|
@@ -268,7 +269,6 @@ module Checkoff
|
|
|
268
269
|
# @return [Hash{String, nil => Enumerable<Asana::Resources::Task>}]
|
|
269
270
|
def by_section(tasks, project_gid)
|
|
270
271
|
by_section = {}
|
|
271
|
-
# @sg-ignore
|
|
272
272
|
sections = client.sections.get_sections_for_project(project_gid:,
|
|
273
273
|
options: { fields: ['name'] })
|
|
274
274
|
sections.each_entry { |section| by_section[section_key(section.name)] = [] }
|
|
@@ -282,7 +282,10 @@ module Checkoff
|
|
|
282
282
|
# @param project_gid [String]
|
|
283
283
|
# @return [void]
|
|
284
284
|
def file_task_by_section(by_section, task, project_gid)
|
|
285
|
-
membership = task.memberships.find
|
|
285
|
+
membership = task.memberships.find do |m|
|
|
286
|
+
# @sg-ignore
|
|
287
|
+
T.cast(m['project'], T::Hash[String, T.untyped])['gid'] == project_gid
|
|
288
|
+
end
|
|
286
289
|
raise "Could not find task in project_gid #{project_gid}: #{task}" if membership.nil?
|
|
287
290
|
|
|
288
291
|
section = T.cast(membership['section'], T::Hash[String, T.untyped])
|
|
@@ -295,9 +298,10 @@ module Checkoff
|
|
|
295
298
|
by_section.fetch(current_section) << task
|
|
296
299
|
end
|
|
297
300
|
|
|
298
|
-
# @param workspace_name [String]
|
|
301
|
+
# @param workspace_name [String, Symbol]
|
|
299
302
|
# @param project_name [String, Symbol]
|
|
300
303
|
# @return [Asana::Resources::Project]
|
|
304
|
+
# @sg-ignore projects.project may be nil but is raised below
|
|
301
305
|
def project_or_raise(workspace_name, project_name)
|
|
302
306
|
raise ArgumentError, 'Provide nil project_name' if T.unsafe(project_name).nil?
|
|
303
307
|
|
data/lib/checkoff/subtasks.rb
CHANGED
|
@@ -46,7 +46,6 @@ module Checkoff
|
|
|
46
46
|
current_section = nil
|
|
47
47
|
by_section = { nil => [] }
|
|
48
48
|
tasks.each do |task|
|
|
49
|
-
# @sg-ignore
|
|
50
49
|
current_section, by_section = file_task_by_section(current_section,
|
|
51
50
|
by_section, task)
|
|
52
51
|
end
|
|
@@ -90,6 +89,7 @@ module Checkoff
|
|
|
90
89
|
# as memberships with a separate API within a task.
|
|
91
90
|
#
|
|
92
91
|
# @param subtask [Asana::Resources::Task]
|
|
92
|
+
# @sg-ignore
|
|
93
93
|
def subtask_section?(subtask)
|
|
94
94
|
subtask.is_rendered_as_separator
|
|
95
95
|
end
|
data/lib/checkoff/tags.rb
CHANGED
|
@@ -79,6 +79,7 @@ module Checkoff
|
|
|
79
79
|
# @param tag_name [String]
|
|
80
80
|
#
|
|
81
81
|
# @return [Asana::Resources::Tag]
|
|
82
|
+
# @sg-ignore
|
|
82
83
|
def tag_or_raise(workspace_name, tag_name)
|
|
83
84
|
t = tag(workspace_name, tag_name)
|
|
84
85
|
|
|
@@ -92,7 +93,6 @@ module Checkoff
|
|
|
92
93
|
# @param tag_name [String]
|
|
93
94
|
#
|
|
94
95
|
# @return [Asana::Resources::Tag,nil]
|
|
95
|
-
# @sg-ignore
|
|
96
96
|
def tag(workspace_name, tag_name)
|
|
97
97
|
workspace = workspaces.workspace_or_raise(workspace_name)
|
|
98
98
|
tags = client.tags.get_tags_for_workspace(workspace_gid: workspace.gid)
|
|
@@ -111,10 +111,10 @@ module Checkoff
|
|
|
111
111
|
|
|
112
112
|
# @param options [Hash{Symbol => Object}]
|
|
113
113
|
#
|
|
114
|
-
# @sg-ignore
|
|
115
114
|
# @return [Hash{Symbol => Object}]
|
|
116
115
|
def build_params(options)
|
|
117
116
|
{ limit: options[:per_page], completed_since: options[:completed_since] }.reject do |_, v|
|
|
117
|
+
# @sg-ignore
|
|
118
118
|
v.nil? || Array(v).empty?
|
|
119
119
|
end
|
|
120
120
|
end
|
|
@@ -135,10 +135,8 @@ module Checkoff
|
|
|
135
135
|
class << self
|
|
136
136
|
# @return [void]
|
|
137
137
|
def run
|
|
138
|
-
# @sg-ignore
|
|
139
138
|
# @type [String]
|
|
140
139
|
workspace_name = ARGV[0] || raise('Please pass workspace name as first argument')
|
|
141
|
-
# @sg-ignore
|
|
142
140
|
# @type [String]
|
|
143
141
|
tag_name = ARGV[1] || raise('Please pass tag name as second argument')
|
|
144
142
|
tags = Checkoff::Tags.new
|
|
@@ -71,9 +71,9 @@ module Checkoff
|
|
|
71
71
|
# @return [Enumerable<Asana::Resources::Task>]
|
|
72
72
|
def task_search(workspace_name, url, extra_fields: [])
|
|
73
73
|
workspace = workspaces.workspace_or_raise(workspace_name)
|
|
74
|
-
# @sg-ignore
|
|
75
74
|
api_params, task_selector = @search_url_parser.convert_params(url)
|
|
76
75
|
debug { "Task search params: api_params=#{api_params}, task_selector=#{task_selector}" }
|
|
76
|
+
# @sg-ignore
|
|
77
77
|
raw_task_search(api_params, workspace_gid: workspace.gid, task_selector:,
|
|
78
78
|
extra_fields:)
|
|
79
79
|
end
|
|
@@ -93,13 +93,9 @@ module Checkoff
|
|
|
93
93
|
def raw_task_search(api_params,
|
|
94
94
|
workspace_gid:, extra_fields: [], task_selector: [],
|
|
95
95
|
fetch_all: true)
|
|
96
|
-
# @sg-ignore
|
|
97
96
|
tasks = api_task_search_request(api_params, workspace_gid:, extra_fields:)
|
|
98
97
|
|
|
99
|
-
if fetch_all && tasks.count == 100
|
|
100
|
-
# @sg-ignore
|
|
101
|
-
tasks = iterated_raw_task_search(api_params, workspace_gid:, extra_fields:)
|
|
102
|
-
end
|
|
98
|
+
tasks = iterated_raw_task_search(api_params, workspace_gid:, extra_fields:) if fetch_all && tasks.count == 100
|
|
103
99
|
|
|
104
100
|
debug { "#{tasks.count} raw tasks returned" }
|
|
105
101
|
|
|
@@ -203,10 +199,8 @@ module Checkoff
|
|
|
203
199
|
class << self
|
|
204
200
|
# @return [void]
|
|
205
201
|
def run
|
|
206
|
-
# @sg-ignore
|
|
207
202
|
# @type [String]
|
|
208
203
|
workspace_name = ARGV[0] || raise('Please pass workspace name as first argument')
|
|
209
|
-
# @sg-ignore
|
|
210
204
|
# @type [String]
|
|
211
205
|
url = ARGV[1] || raise('Please pass task search URL as second argument')
|
|
212
206
|
task_searches = Checkoff::TaskSearches.new
|
|
@@ -20,7 +20,6 @@ module Checkoff
|
|
|
20
20
|
LONG_CACHE_TIME = MINUTE * 15
|
|
21
21
|
SHORT_CACHE_TIME = MINUTE
|
|
22
22
|
|
|
23
|
-
# @sg-ignore
|
|
24
23
|
# @param [Hash,Checkoff::Internal::EnvFallbackConfigLoader] config
|
|
25
24
|
# @param [Asana::Client] client
|
|
26
25
|
# @param [Checkoff::Tasks] tasks
|
|
@@ -64,8 +63,8 @@ module Checkoff
|
|
|
64
63
|
# bundle exec ./task_selectors.rb
|
|
65
64
|
# :nocov:
|
|
66
65
|
class << self
|
|
67
|
-
# @sg-ignore
|
|
68
66
|
# @return [String]
|
|
67
|
+
# @sg-ignore
|
|
69
68
|
def project_name
|
|
70
69
|
ARGV[1] || raise('Please pass project name to pull tasks from as first argument')
|
|
71
70
|
end
|
|
@@ -76,8 +75,8 @@ module Checkoff
|
|
|
76
75
|
ARGV[0] || raise('Please pass workspace name as first argument')
|
|
77
76
|
end
|
|
78
77
|
|
|
79
|
-
# @sg-ignore
|
|
80
78
|
# @return [Array(Symbol, Array)]
|
|
79
|
+
# @sg-ignore
|
|
81
80
|
def task_selector
|
|
82
81
|
task_selector_json = ARGV[2] || raise('Please pass task_selector in JSON form as third argument')
|
|
83
82
|
|
data/lib/checkoff/tasks.rb
CHANGED
|
@@ -110,13 +110,12 @@ module Checkoff
|
|
|
110
110
|
|
|
111
111
|
# Pull a specific task by name
|
|
112
112
|
#
|
|
113
|
-
# @param workspace_name [String]
|
|
113
|
+
# @param workspace_name [String, Symbol]
|
|
114
114
|
# @param project_name [String, Symbol]
|
|
115
115
|
# @param section_name [String, nil, Symbol]
|
|
116
116
|
# @param task_name [String]
|
|
117
117
|
# @param only_uncompleted [Boolean]
|
|
118
118
|
# @param extra_fields [Array<String>]
|
|
119
|
-
# @sg-ignore
|
|
120
119
|
# @return [Asana::Resources::Task, nil]
|
|
121
120
|
def task(workspace_name, project_name, task_name,
|
|
122
121
|
section_name: :unspecified,
|
|
@@ -124,6 +123,7 @@ module Checkoff
|
|
|
124
123
|
extra_fields: [])
|
|
125
124
|
thread_local = Checkoff::Internal::ThreadLocal.new
|
|
126
125
|
# @type [String]
|
|
126
|
+
# @sg-ignore
|
|
127
127
|
task_gid = thread_local.with_thread_local_variable(:suppress_asana_webhook_watch_creation,
|
|
128
128
|
true) do
|
|
129
129
|
gid_for_task(workspace_name, project_name, section_name, task_name)
|
|
@@ -134,21 +134,21 @@ module Checkoff
|
|
|
134
134
|
end
|
|
135
135
|
cache_method :task, LONG_CACHE_TIME
|
|
136
136
|
|
|
137
|
-
# @param workspace_name [String]
|
|
137
|
+
# @param workspace_name [String, Symbol]
|
|
138
138
|
# @param project_name [String, Symbol]
|
|
139
139
|
# @param section_name [String, nil, Symbol]
|
|
140
140
|
# @param task_name [String]
|
|
141
141
|
#
|
|
142
142
|
# @return [String, nil]
|
|
143
|
-
# @sg-ignore
|
|
144
143
|
def gid_for_task(workspace_name, project_name, section_name, task_name)
|
|
145
|
-
# @sg-ignore
|
|
146
144
|
t = tasks(workspace_name,
|
|
147
145
|
project_name,
|
|
148
146
|
section_name:,
|
|
149
147
|
only_uncompleted: false)
|
|
150
148
|
task_for_gid = t.find { |task| task.name == task_name }
|
|
151
|
-
task_for_gid
|
|
149
|
+
return nil if task_for_gid.nil?
|
|
150
|
+
|
|
151
|
+
task_for_gid.gid
|
|
152
152
|
end
|
|
153
153
|
cache_method :gid_for_task, REALLY_LONG_CACHE_TIME
|
|
154
154
|
|
|
@@ -179,6 +179,7 @@ module Checkoff
|
|
|
179
179
|
# @param name [String]
|
|
180
180
|
# @param workspace_gid [String]
|
|
181
181
|
# @param assignee_gid [String]
|
|
182
|
+
# @sg-ignore
|
|
182
183
|
#
|
|
183
184
|
# @return [Asana::Resources::Task]
|
|
184
185
|
def add_task(name,
|
|
@@ -200,6 +201,7 @@ module Checkoff
|
|
|
200
201
|
|
|
201
202
|
# True if any of the task's dependencies are marked incomplete
|
|
202
203
|
#
|
|
204
|
+
# @sg-ignore
|
|
203
205
|
# Include 'dependencies.gid' in extra_fields of task passed in.
|
|
204
206
|
#
|
|
205
207
|
# @param task [Asana::Resources::Task]
|
|
@@ -212,7 +214,6 @@ module Checkoff
|
|
|
212
214
|
#
|
|
213
215
|
# https://github.com/Asana/ruby-asana/issues/125
|
|
214
216
|
|
|
215
|
-
# @sg-ignore
|
|
216
217
|
# @type [Enumerable<Asana::Resources::Task>, nil]
|
|
217
218
|
dependencies = task.instance_variable_get(:@dependencies) || []
|
|
218
219
|
|
|
@@ -220,6 +221,7 @@ module Checkoff
|
|
|
220
221
|
# the real bummer though is that asana doesn't let you fetch
|
|
221
222
|
# the completion status of dependencies, so we need to do this
|
|
222
223
|
# regardless:
|
|
224
|
+
# @sg-ignore
|
|
223
225
|
parent_task_gid = parent_task_info.fetch('gid')
|
|
224
226
|
|
|
225
227
|
parent_task = task_by_gid(parent_task_gid, only_uncompleted: false)
|
|
@@ -289,12 +291,15 @@ module Checkoff
|
|
|
289
291
|
# @param task [Asana::Resources::Task]
|
|
290
292
|
# @param portfolio_name [String]
|
|
291
293
|
# @param workspace_name [String]
|
|
294
|
+
# @return [Boolean]
|
|
295
|
+
# @sg-ignore
|
|
292
296
|
def in_portfolio_named?(task,
|
|
293
297
|
portfolio_name,
|
|
294
298
|
workspace_name: @workspaces.default_workspace.name)
|
|
295
299
|
portfolio_projects = @portfolios.projects_in_portfolio(workspace_name, portfolio_name)
|
|
296
300
|
task.memberships.any? do |membership|
|
|
297
|
-
|
|
301
|
+
m = T.cast(membership, T::Hash[String, T.untyped])
|
|
302
|
+
project_gid = T.cast(m.fetch('project'), T::Hash[String, T.untyped]).fetch('gid')
|
|
298
303
|
portfolio_projects.any? do |portfolio_project|
|
|
299
304
|
portfolio_project.gid == project_gid
|
|
300
305
|
end
|
|
@@ -306,6 +311,7 @@ module Checkoff
|
|
|
306
311
|
# @param task [Asana::Resources::Task]
|
|
307
312
|
# @param portfolio_name [String]
|
|
308
313
|
# @param workspace_name [String]
|
|
314
|
+
# @return [Boolean]
|
|
309
315
|
def in_portfolio_more_than_once?(task,
|
|
310
316
|
portfolio_name,
|
|
311
317
|
workspace_name: @workspaces.default_workspace.name)
|
|
@@ -313,7 +319,8 @@ module Checkoff
|
|
|
313
319
|
portfolio_project_gids = portfolio_projects.map(&:gid)
|
|
314
320
|
seen = T.let(false, T::Boolean)
|
|
315
321
|
task.memberships.each do |membership|
|
|
316
|
-
|
|
322
|
+
m = T.cast(membership, T::Hash[String, T.untyped])
|
|
323
|
+
project_gid = T.cast(m.fetch('project'), T::Hash[String, T.untyped]).fetch('gid')
|
|
317
324
|
next unless portfolio_project_gids.include?(project_gid)
|
|
318
325
|
return true if seen
|
|
319
326
|
|
|
@@ -341,7 +348,7 @@ module Checkoff
|
|
|
341
348
|
@task_hashes ||= Checkoff::Internal::TaskHashes.new
|
|
342
349
|
end
|
|
343
350
|
|
|
344
|
-
# @param workspace_name [String]
|
|
351
|
+
# @param workspace_name [String, Symbol]
|
|
345
352
|
# @param project_name [String, Symbol]
|
|
346
353
|
# @param section_name [String, nil, Symbol] - :unspecified
|
|
347
354
|
# @param only_uncompleted [Boolean]
|
|
@@ -377,8 +384,8 @@ module Checkoff
|
|
|
377
384
|
@projects ||= @sections.projects
|
|
378
385
|
end
|
|
379
386
|
|
|
380
|
-
# @sg-ignore
|
|
381
387
|
# @return [String]
|
|
388
|
+
# @sg-ignore
|
|
382
389
|
def default_assignee_gid
|
|
383
390
|
@config.fetch(:default_assignee_gid)
|
|
384
391
|
end
|
data/lib/checkoff/timelines.rb
CHANGED
|
@@ -47,9 +47,9 @@ module Checkoff
|
|
|
47
47
|
|
|
48
48
|
# @param task [Asana::Resources::Task]
|
|
49
49
|
# @param limit_to_portfolio_gid [String, nil]
|
|
50
|
+
# @return [Boolean]
|
|
50
51
|
def task_dependent_on_previous_section_last_milestone?(task, limit_to_portfolio_gid: nil)
|
|
51
52
|
task_data = @tasks.task_to_h(task)
|
|
52
|
-
# @sg-ignore
|
|
53
53
|
# @type [Array<Hash{String => Hash{String => String}}>]
|
|
54
54
|
memberships_data = task_data.fetch('memberships')
|
|
55
55
|
memberships_data.all? do |membership_data|
|
|
@@ -57,12 +57,15 @@ module Checkoff
|
|
|
57
57
|
section_data = membership_data.fetch('section')
|
|
58
58
|
section_gid = section_data.fetch('gid')
|
|
59
59
|
section = @sections.section_by_gid(section_gid)
|
|
60
|
+
# @sg-ignore
|
|
60
61
|
task_data_dependent_on_previous_section_last_milestone?(task_data, section)
|
|
61
62
|
end
|
|
62
63
|
end
|
|
63
64
|
|
|
64
65
|
# @param task [Asana::Resources::Task]
|
|
65
66
|
# @param limit_to_portfolio_name [String, nil]
|
|
67
|
+
# @return [Boolean]
|
|
68
|
+
# @sg-ignore
|
|
66
69
|
def last_task_milestone_depends_on_this_task?(task, limit_to_portfolio_name: nil)
|
|
67
70
|
unless limit_to_portfolio_name.nil?
|
|
68
71
|
limit_to_projects = @portfolios.projects_in_portfolio(@workspaces.default_workspace.name,
|
|
@@ -72,12 +75,13 @@ module Checkoff
|
|
|
72
75
|
all_dependent_task_gids = nil
|
|
73
76
|
task.memberships.all? do |membership_data|
|
|
74
77
|
unless limit_to_portfolio_name.nil?
|
|
78
|
+
# @sg-ignore
|
|
75
79
|
project_gid = membership_data.fetch('project').fetch('gid')
|
|
76
80
|
next true unless limit_to_projects.map(&:gid).include? project_gid
|
|
77
81
|
end
|
|
78
|
-
# @
|
|
82
|
+
# @sg-ignore
|
|
79
83
|
section_data = membership_data.fetch('section')
|
|
80
|
-
# @
|
|
84
|
+
# @sg-ignore
|
|
81
85
|
section_gid = section_data.fetch('gid')
|
|
82
86
|
|
|
83
87
|
last_milestone = last_milestone_in_section(section_gid)
|
|
@@ -88,12 +92,15 @@ module Checkoff
|
|
|
88
92
|
|
|
89
93
|
all_dependent_task_gids ||= @tasks.all_dependent_tasks(task).map(&:gid)
|
|
90
94
|
|
|
95
|
+
# @sg-ignore
|
|
91
96
|
all_dependent_task_gids.include? last_milestone.gid
|
|
92
97
|
end
|
|
93
98
|
end
|
|
94
99
|
|
|
95
100
|
# @param task [Asana::Resources::Task]
|
|
96
101
|
# @param limit_to_portfolio_name [String, nil]
|
|
102
|
+
# @return [Boolean]
|
|
103
|
+
# @sg-ignore
|
|
97
104
|
def any_milestone_depends_on_this_task?(task, limit_to_portfolio_name: nil)
|
|
98
105
|
unless limit_to_portfolio_name.nil?
|
|
99
106
|
limit_to_projects = @portfolios.projects_in_portfolio(@workspaces.default_workspace.name,
|
|
@@ -103,6 +110,7 @@ module Checkoff
|
|
|
103
110
|
all_dependent_milestones = nil
|
|
104
111
|
task.memberships.all? do |membership_data|
|
|
105
112
|
unless limit_to_portfolio_name.nil?
|
|
113
|
+
# @sg-ignore
|
|
106
114
|
project_gid = membership_data.fetch('project').fetch('gid')
|
|
107
115
|
next true unless limit_to_projects.map(&:gid).include? project_gid
|
|
108
116
|
end
|
|
@@ -116,6 +124,7 @@ module Checkoff
|
|
|
116
124
|
dependent_task.resource_subtype == 'milestone'
|
|
117
125
|
end
|
|
118
126
|
|
|
127
|
+
# @sg-ignore
|
|
119
128
|
all_dependent_milestones.any? do |milestone|
|
|
120
129
|
milestone.memberships.any? do |milestone_membership_data|
|
|
121
130
|
milestone_membership_data.fetch('project').fetch('gid') == project_gid
|
|
@@ -148,7 +157,6 @@ module Checkoff
|
|
|
148
157
|
previous_section_last_milestone = last_milestone_in_section(previous_section.gid)
|
|
149
158
|
return true if previous_section_last_milestone.nil?
|
|
150
159
|
|
|
151
|
-
# @sg-ignore
|
|
152
160
|
# @type [Array<Hash{String => String}>]
|
|
153
161
|
dependencies = task_data.fetch('dependencies')
|
|
154
162
|
return false if dependencies.empty?
|
|
@@ -167,10 +175,8 @@ module Checkoff
|
|
|
167
175
|
class << self
|
|
168
176
|
# @return [void]
|
|
169
177
|
def run
|
|
170
|
-
# @sg-ignore
|
|
171
178
|
# @type [String]
|
|
172
179
|
# workspace_name = ARGV[0] || raise('Please pass workspace name as first argument')
|
|
173
|
-
# @sg-ignore
|
|
174
180
|
# @type [String]
|
|
175
181
|
# timeline_name = ARGV[1] || raise('Please pass timeline name as second argument')
|
|
176
182
|
# timelines = Checkoff::Timelines.new
|
data/lib/checkoff/timing.rb
CHANGED
|
@@ -43,6 +43,7 @@ module Checkoff
|
|
|
43
43
|
|
|
44
44
|
return next_week?(date_or_time) if period == :next_week
|
|
45
45
|
|
|
46
|
+
# @sg-ignore
|
|
46
47
|
return day_of_week?(date_or_time, period) if %i[monday tuesday wednesday thursday friday saturday
|
|
47
48
|
sunday].include?(period)
|
|
48
49
|
|
|
@@ -71,9 +72,7 @@ module Checkoff
|
|
|
71
72
|
|
|
72
73
|
date = date_or_time.to_date
|
|
73
74
|
|
|
74
|
-
# @sg-ignore
|
|
75
75
|
n_days_from_today = @today_getter.today + num_days
|
|
76
|
-
# @sg-ignore
|
|
77
76
|
date >= n_days_from_today
|
|
78
77
|
end
|
|
79
78
|
|
|
@@ -92,9 +91,7 @@ module Checkoff
|
|
|
92
91
|
|
|
93
92
|
date = date_or_time.to_date
|
|
94
93
|
|
|
95
|
-
# @sg-ignore
|
|
96
94
|
n_days_ago = @today_getter.today - num_days
|
|
97
|
-
# @sg-ignore
|
|
98
95
|
date < n_days_ago
|
|
99
96
|
end
|
|
100
97
|
|
|
@@ -174,14 +171,15 @@ module Checkoff
|
|
|
174
171
|
#
|
|
175
172
|
# @return [Time]
|
|
176
173
|
def n_days_from_now(num_days)
|
|
177
|
-
(@now_getter.now + (num_days *
|
|
174
|
+
Time.at(@now_getter.now.to_i + (num_days * 86_400))
|
|
178
175
|
end
|
|
179
176
|
|
|
180
177
|
# @param num_days [Integer]
|
|
181
178
|
#
|
|
182
179
|
# @return [Date]
|
|
180
|
+
# @sg-ignore
|
|
183
181
|
def n_days_from_today(num_days)
|
|
184
|
-
|
|
182
|
+
@today_getter.today + num_days
|
|
185
183
|
end
|
|
186
184
|
|
|
187
185
|
# @param date_or_time [Date,Time,nil]
|
|
@@ -212,7 +210,6 @@ module Checkoff
|
|
|
212
210
|
# @param period_name [Symbol]
|
|
213
211
|
# @param args [Object]
|
|
214
212
|
def compound_in_period?(date_or_time, period_name, *args)
|
|
215
|
-
# @sg-ignore
|
|
216
213
|
return less_than_n_days_ago?(date_or_time, *args) if period_name == :less_than_n_days_ago
|
|
217
214
|
|
|
218
215
|
return less_than_n_days_from_now?(date_or_time, *args) if period_name == :less_than_n_days_from_now
|
|
@@ -225,9 +222,9 @@ module Checkoff
|
|
|
225
222
|
return greater_than_or_equal_to_n_days_from_today?(date_or_time, *args)
|
|
226
223
|
end
|
|
227
224
|
|
|
228
|
-
# @sg-ignore
|
|
229
225
|
return between_relative_days?(date_or_time, *args) if period_name == :between_relative_days
|
|
230
226
|
|
|
227
|
+
# @sg-ignore
|
|
231
228
|
raise "Teach me how to handle period [#{period_name.inspect}, #{args.map(&:inspect).join(', ')}]"
|
|
232
229
|
end
|
|
233
230
|
|
data/lib/checkoff/version.rb
CHANGED
data/lib/checkoff/workspaces.rb
CHANGED
|
@@ -34,8 +34,7 @@ module Checkoff
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# Pulls an Asana workspace object
|
|
37
|
-
# @param [String] workspace_name
|
|
38
|
-
# @sg-ignore
|
|
37
|
+
# @param [String, Symbol] workspace_name
|
|
39
38
|
# @return [Asana::Resources::Workspace, nil]
|
|
40
39
|
def workspace(workspace_name)
|
|
41
40
|
client.workspaces.find_all.find do |workspace|
|
|
@@ -45,13 +44,15 @@ module Checkoff
|
|
|
45
44
|
cache_method :workspace, LONG_CACHE_TIME
|
|
46
45
|
|
|
47
46
|
# @return [Asana::Resources::Workspace]
|
|
47
|
+
# @sg-ignore
|
|
48
48
|
def default_workspace
|
|
49
49
|
@asana_workspace.find_by_id(client, default_workspace_gid)
|
|
50
50
|
end
|
|
51
51
|
cache_method :default_workspace, REALLY_LONG_CACHE_TIME
|
|
52
52
|
|
|
53
|
-
# @param [String] workspace_name
|
|
53
|
+
# @param [String, Symbol] workspace_name
|
|
54
54
|
# @return [Asana::Resources::Workspace]
|
|
55
|
+
# @sg-ignore workspace() is nil-checked below
|
|
55
56
|
def workspace_or_raise(workspace_name)
|
|
56
57
|
w = workspace(workspace_name)
|
|
57
58
|
raise "Could not find workspace #{workspace_name}" if w.nil?
|
|
@@ -59,8 +60,8 @@ module Checkoff
|
|
|
59
60
|
w
|
|
60
61
|
end
|
|
61
62
|
|
|
62
|
-
# @sg-ignore
|
|
63
63
|
# @return [String]
|
|
64
|
+
# @sg-ignore
|
|
64
65
|
def default_workspace_gid
|
|
65
66
|
@config.fetch(:default_workspace_gid)
|
|
66
67
|
end
|