checkoff 0.97.0 → 0.99.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/config/definitions.rb +8 -0
- data/lib/checkoff/internal/selector_classes/task/function_evaluator.rb +6 -26
- data/lib/checkoff/internal/selector_classes/task.rb +22 -2
- data/lib/checkoff/internal/task_selector_evaluator.rb +7 -2
- data/lib/checkoff/internal/task_timing.rb +42 -7
- data/lib/checkoff/sections.rb +65 -3
- data/lib/checkoff/task_selectors.rb +9 -2
- data/lib/checkoff/tasks.rb +4 -1
- data/lib/checkoff/timelines.rb +121 -0
- data/lib/checkoff/version.rb +1 -1
- data/lib/checkoff.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0aa9c88e96e1094d284c59a9c7146ff72f393f2e16442c8e320b3253ab0221d
|
4
|
+
data.tar.gz: 7f6021289d7fb0e331cb6b993b10307287c365d9a00d5fbe789e5b6c13972dad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab4476c149bf918e5d138f71a5d1c5df78192f3fa3fa13e8ef0e8a2d7c7198b34268a78ab14e6009237d3cf0113316f3ff91222d22893606c5ecc1ab31830d55
|
7
|
+
data.tar.gz: 4f804bcfc9548b7106fcd80c5764da4250bf024e9dc7f1c2b95460078b89de3a238ba2b28f6d44188cd3fee074a0d348925ef780b751534a0f801f08b74fab24
|
data/Gemfile.lock
CHANGED
data/config/definitions.rb
CHANGED
@@ -42,6 +42,8 @@
|
|
42
42
|
# module Resources
|
43
43
|
# # https://developers.asana.com/reference/gettask
|
44
44
|
# class Task
|
45
|
+
# # @return [String]
|
46
|
+
# def resource_subtype; end
|
45
47
|
# # @return [String,nil]
|
46
48
|
# def due_at; end
|
47
49
|
# # @return [String,nil]
|
@@ -136,6 +138,12 @@
|
|
136
138
|
# # @param project_gid [String]
|
137
139
|
# # @return [Enumerable<Asana::Resources::Section>]
|
138
140
|
# def get_sections_for_project(project_gid:, options: {}); end
|
141
|
+
# # Returns the complete record for a single section.
|
142
|
+
# #
|
143
|
+
# # @param [String] id - The section to get.
|
144
|
+
# # @param options [Hash] - the request I/O options.
|
145
|
+
# # @return [Asana::Resources::Section]
|
146
|
+
# def find_by_id(id, options: {}); end
|
139
147
|
# end
|
140
148
|
# class Project
|
141
149
|
# # Returns the compact project records for all projects in the workspace.
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative '../function_evaluator'
|
4
|
+
require 'checkoff/internal/task_timing'
|
4
5
|
|
5
6
|
module Checkoff
|
6
7
|
module SelectorClasses
|
@@ -9,10 +10,14 @@ module Checkoff
|
|
9
10
|
class FunctionEvaluator < ::Checkoff::SelectorClasses::FunctionEvaluator
|
10
11
|
# @param selector [Array<(Symbol, Array)>,String]
|
11
12
|
# @param tasks [Checkoff::Tasks]
|
13
|
+
# @param timelines [Checkoff::Timelines]
|
12
14
|
def initialize(selector:,
|
13
|
-
tasks
|
15
|
+
tasks:,
|
16
|
+
timelines:)
|
14
17
|
@selector = selector
|
15
18
|
@tasks = tasks
|
19
|
+
@timelines = timelines
|
20
|
+
@task_timing = ::Checkoff::Internal::TaskTiming.new
|
16
21
|
super()
|
17
22
|
end
|
18
23
|
|
@@ -41,31 +46,6 @@ module Checkoff
|
|
41
46
|
raise "Teach me how to handle field #{field_name}"
|
42
47
|
end
|
43
48
|
|
44
|
-
# @param task [Asana::Resources::Task]
|
45
|
-
# @param field_name [Symbol]
|
46
|
-
#
|
47
|
-
# @sg-ignore
|
48
|
-
# @return [Date, Time, nil]
|
49
|
-
def pull_date_or_time_field_by_name(task, field_name)
|
50
|
-
if field_name == :due
|
51
|
-
return Time.parse(task.due_at) unless task.due_at.nil?
|
52
|
-
|
53
|
-
return Date.parse(task.due_on) unless task.due_on.nil?
|
54
|
-
|
55
|
-
return nil
|
56
|
-
end
|
57
|
-
|
58
|
-
if field_name == :start
|
59
|
-
return Time.parse(task.start_at) unless task.start_at.nil?
|
60
|
-
|
61
|
-
return Date.parse(task.start_on) unless task.start_on.nil?
|
62
|
-
|
63
|
-
return nil
|
64
|
-
end
|
65
|
-
|
66
|
-
raise "Teach me how to handle field #{field_name}"
|
67
|
-
end
|
68
|
-
|
69
49
|
# @sg-ignore
|
70
50
|
# @param task [Asana::Resources::Task]
|
71
51
|
# @param custom_field_gid [String]
|
@@ -138,8 +138,8 @@ module Checkoff
|
|
138
138
|
end_n_days_from_now_time = (Time.now + (end_num_days_from_now * 24 * 60 * 60))
|
139
139
|
|
140
140
|
# @type [Date, Time, nil]
|
141
|
-
task_date_or_time =
|
142
|
-
|
141
|
+
task_date_or_time = @task_timing.date_or_time_field_by_name(task, :start) ||
|
142
|
+
@task_timing.date_or_time_field_by_name(task, :due)
|
143
143
|
|
144
144
|
return false if task_date_or_time.nil?
|
145
145
|
|
@@ -353,6 +353,26 @@ module Checkoff
|
|
353
353
|
estimate_hours > allocated_hours
|
354
354
|
end
|
355
355
|
end
|
356
|
+
|
357
|
+
# :dependent_on_previous_section_last_milestone
|
358
|
+
class DependentOnPreviousSectionLastMilestoneFunctionEvaluator < FunctionEvaluator
|
359
|
+
FUNCTION_NAME = :dependent_on_previous_section_last_milestone
|
360
|
+
|
361
|
+
def matches?
|
362
|
+
fn?(selector, FUNCTION_NAME)
|
363
|
+
end
|
364
|
+
|
365
|
+
# @param task [Asana::Resources::Task]
|
366
|
+
# @param project_name [String]
|
367
|
+
# @param limit_to_portfolio_gid [String, nil] If specified,
|
368
|
+
# only projects in this portfolio will be evaluated.
|
369
|
+
#
|
370
|
+
# @return [Boolean]
|
371
|
+
def evaluate(task, limit_to_portfolio_gid: nil)
|
372
|
+
@timelines.task_dependent_on_previous_section_last_milestone?(task,
|
373
|
+
limit_to_portfolio_gid: limit_to_portfolio_gid)
|
374
|
+
end
|
375
|
+
end
|
356
376
|
end
|
357
377
|
end
|
358
378
|
end
|
@@ -9,10 +9,13 @@ module Checkoff
|
|
9
9
|
class TaskSelectorEvaluator < SelectorEvaluator
|
10
10
|
# @param task [Asana::Resources::Task]
|
11
11
|
# @param tasks [Checkoff::Tasks]
|
12
|
+
# @param timelines [Checkoff::Timelines]
|
12
13
|
def initialize(task:,
|
13
|
-
tasks: Checkoff::Tasks.new
|
14
|
+
tasks: Checkoff::Tasks.new,
|
15
|
+
timelines: Checkoff::Timelines.new)
|
14
16
|
@item = task
|
15
17
|
@tasks = tasks
|
18
|
+
@timelines = timelines
|
16
19
|
super()
|
17
20
|
end
|
18
21
|
|
@@ -35,12 +38,14 @@ module Checkoff
|
|
35
38
|
|
36
39
|
# @return [Hash]
|
37
40
|
def initializer_kwargs
|
38
|
-
{ tasks: tasks }
|
41
|
+
{ tasks: tasks, timelines: timelines }
|
39
42
|
end
|
40
43
|
|
41
44
|
# @return [Asana::Resources::Task]
|
42
45
|
attr_reader :item
|
43
46
|
# @return [Checkoff::Tasks]
|
44
47
|
attr_reader :tasks
|
48
|
+
# @return [Checkoff::Timelines]
|
49
|
+
attr_reader :timelines
|
45
50
|
end
|
46
51
|
end
|
@@ -5,27 +5,62 @@ module Checkoff
|
|
5
5
|
# Utility methods for working with task dates and times
|
6
6
|
class TaskTiming
|
7
7
|
# @param time_class [Class<Time>]
|
8
|
-
|
8
|
+
# @param date_class [Class<Date>]
|
9
|
+
def initialize(time_class: Time, date_class: Date)
|
9
10
|
@time_class = time_class
|
11
|
+
@date_class = date_class
|
10
12
|
end
|
11
13
|
|
12
14
|
# @param task [Asana::Resources::Task]
|
13
15
|
# @return [Time, nil]
|
14
16
|
def start_time(task)
|
15
|
-
|
16
|
-
return @time_class.parse(task.start_on) if task.start_on
|
17
|
-
|
18
|
-
nil
|
17
|
+
date_or_time_field_by_name(task, :start)&.to_time
|
19
18
|
end
|
20
19
|
|
21
20
|
# @param task [Asana::Resources::Task]
|
22
21
|
# @return [Time, nil]
|
23
22
|
def due_time(task)
|
24
|
-
|
25
|
-
|
23
|
+
date_or_time_field_by_name(task, :due)&.to_time
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param task [Asana::Resources::Task]
|
27
|
+
# @param field_name [Symbol]
|
28
|
+
#
|
29
|
+
# @sg-ignore
|
30
|
+
# @return [Date, Time, nil]
|
31
|
+
def start_date_or_time(task)
|
32
|
+
return @time_class.parse(task.start_at) unless task.start_at.nil?
|
33
|
+
|
34
|
+
return @date_class.parse(task.start_on) unless task.start_on.nil?
|
26
35
|
|
27
36
|
nil
|
28
37
|
end
|
38
|
+
|
39
|
+
# @param task [Asana::Resources::Task]
|
40
|
+
# @param field_name [Symbol]
|
41
|
+
#
|
42
|
+
# @sg-ignore
|
43
|
+
# @return [Date, Time, nil]
|
44
|
+
def due_date_or_time(task)
|
45
|
+
return @time_class.parse(task.due_at) unless task.due_at.nil?
|
46
|
+
|
47
|
+
return @date_class.parse(task.due_on) unless task.due_on.nil?
|
48
|
+
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param task [Asana::Resources::Task]
|
53
|
+
# @param field_name [Symbol]
|
54
|
+
#
|
55
|
+
# @sg-ignore
|
56
|
+
# @return [Date, Time, nil]
|
57
|
+
def date_or_time_field_by_name(task, field_name)
|
58
|
+
return due_date_or_time(task) if field_name == :due
|
59
|
+
|
60
|
+
return start_date_or_time(task) if field_name == :start
|
61
|
+
|
62
|
+
raise "Teach me how to handle field #{field_name}"
|
63
|
+
end
|
29
64
|
end
|
30
65
|
end
|
31
66
|
end
|
data/lib/checkoff/sections.rb
CHANGED
@@ -56,13 +56,23 @@ module Checkoff
|
|
56
56
|
#
|
57
57
|
# @return [Enumerable<Asana::Resources::Section>]
|
58
58
|
def sections_or_raise(workspace_name, project_name, extra_fields: [])
|
59
|
-
fields = %w[name] + extra_fields
|
60
59
|
project = project_or_raise(workspace_name, project_name)
|
61
|
-
|
62
|
-
options: { fields: fields })
|
60
|
+
sections_by_project_gid(project.gid, extra_fields: extra_fields)
|
63
61
|
end
|
64
62
|
cache_method :sections_or_raise, SHORT_CACHE_TIME
|
65
63
|
|
64
|
+
# Returns a list of Asana API section objects for a given project GID
|
65
|
+
# @param project_gid [String]
|
66
|
+
# @param extra_fields [Array<String>]
|
67
|
+
#
|
68
|
+
# @return [Enumerable<Asana::Resources::Section>]
|
69
|
+
def sections_by_project_gid(project_gid, extra_fields: [])
|
70
|
+
fields = %w[name] + extra_fields
|
71
|
+
client.sections.get_sections_for_project(project_gid: project_gid,
|
72
|
+
options: { fields: fields })
|
73
|
+
end
|
74
|
+
cache_method :sections_by_project_gid, SHORT_CACHE_TIME
|
75
|
+
|
66
76
|
# Given a workspace name and project name, then provide a Hash of
|
67
77
|
# tasks with section name -> task list of the uncompleted tasks
|
68
78
|
# @param workspace_name [String]
|
@@ -85,6 +95,20 @@ module Checkoff
|
|
85
95
|
end
|
86
96
|
end
|
87
97
|
|
98
|
+
# @param section_gid [String]
|
99
|
+
# @param only_uncompleted [Boolean]
|
100
|
+
# @param extra_fields [Array<String>]
|
101
|
+
#
|
102
|
+
# @return [Enumerable<Asana::Resources::Task>]
|
103
|
+
def tasks_by_section_gid(section_gid,
|
104
|
+
only_uncompleted: true,
|
105
|
+
extra_fields: [])
|
106
|
+
options = projects.task_options
|
107
|
+
options[:options][:fields] += extra_fields
|
108
|
+
options[:completed_since] = '9999-12-01' if only_uncompleted
|
109
|
+
client.tasks.get_tasks(section: section_gid, **options)
|
110
|
+
end
|
111
|
+
|
88
112
|
# XXX: Rename to section_tasks
|
89
113
|
#
|
90
114
|
# Pulls task objects from a specified section
|
@@ -147,8 +171,45 @@ module Checkoff
|
|
147
171
|
name
|
148
172
|
end
|
149
173
|
|
174
|
+
# @param section [Asana::Resources::Section]
|
175
|
+
#
|
176
|
+
# @return [Asana::Resources::Section, nil]
|
177
|
+
def previous_section(section)
|
178
|
+
sections = sections_by_project_gid(section.project.fetch('gid'))
|
179
|
+
|
180
|
+
# @type [Array<Asana::Resources::Section>]
|
181
|
+
sections = sections.to_a
|
182
|
+
|
183
|
+
index = sections.find_index { |s| s.gid == section.gid }
|
184
|
+
return nil if index.nil? || index.zero?
|
185
|
+
|
186
|
+
sections[index - 1]
|
187
|
+
end
|
188
|
+
cache_method :previous_section, SHORT_CACHE_TIME
|
189
|
+
|
190
|
+
# @param gid [String]
|
191
|
+
#
|
192
|
+
# @return [Asana::Resources::Section]
|
193
|
+
def section_by_gid(gid)
|
194
|
+
options = {}
|
195
|
+
Asana::Resources::Section.new(parse(client.get("/sections/#{gid}", options: options)).first,
|
196
|
+
client: client)
|
197
|
+
end
|
198
|
+
|
150
199
|
private
|
151
200
|
|
201
|
+
# https://github.com/Asana/ruby-asana/blob/master/lib/asana/resource_includes/response_helper.rb#L7
|
202
|
+
# @param response [Faraday::Response]
|
203
|
+
#
|
204
|
+
# @return [Array<Hash, Hash>]
|
205
|
+
def parse(response)
|
206
|
+
data = response.body.fetch('data') do
|
207
|
+
raise("Unexpected response body: #{response.body}")
|
208
|
+
end
|
209
|
+
extra = response.body.except('data')
|
210
|
+
[data, extra]
|
211
|
+
end
|
212
|
+
|
152
213
|
# @return [Asana::Client]
|
153
214
|
attr_reader :client
|
154
215
|
|
@@ -188,6 +249,7 @@ module Checkoff
|
|
188
249
|
# @param project_gid [String]
|
189
250
|
# @return [void]
|
190
251
|
def file_task_by_section(by_section, task, project_gid)
|
252
|
+
# @sg-ignore
|
191
253
|
# @type [Array<Hash>]
|
192
254
|
membership = task.memberships.find { |m| m['project']['gid'] == project_gid }
|
193
255
|
raise "Could not find task in project_gid #{project_gid}: #{task}" if membership.nil?
|
@@ -23,12 +23,16 @@ module Checkoff
|
|
23
23
|
# @param [Hash] config
|
24
24
|
# @param [Asana::Client] client
|
25
25
|
# @param [Checkoff::Tasks] tasks
|
26
|
+
# @param [Checkoff::Timelines] timelines
|
26
27
|
def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
|
27
28
|
client: Checkoff::Clients.new(config: config).client,
|
28
29
|
tasks: Checkoff::Tasks.new(config: config,
|
29
|
-
client: client)
|
30
|
+
client: client),
|
31
|
+
timelines: Checkoff::Timelines.new(config: config,
|
32
|
+
client: client))
|
30
33
|
@config = config
|
31
34
|
@tasks = tasks
|
35
|
+
@timelines = timelines
|
32
36
|
end
|
33
37
|
|
34
38
|
# @param [Asana::Resources::Task] task
|
@@ -36,7 +40,7 @@ module Checkoff
|
|
36
40
|
# task details. Examples: [:tag, 'foo'] [:not, [:tag, 'foo']] [:tag, 'foo']
|
37
41
|
# @return [Boolean]
|
38
42
|
def filter_via_task_selector(task, task_selector)
|
39
|
-
evaluator = TaskSelectorEvaluator.new(task: task, tasks: tasks)
|
43
|
+
evaluator = TaskSelectorEvaluator.new(task: task, tasks: tasks, timelines: timelines)
|
40
44
|
evaluator.evaluate(task_selector)
|
41
45
|
end
|
42
46
|
|
@@ -45,6 +49,9 @@ module Checkoff
|
|
45
49
|
# @return [Checkoff::Tasks]
|
46
50
|
attr_reader :tasks
|
47
51
|
|
52
|
+
# @return [Checkoff::Timelines]
|
53
|
+
attr_reader :timelines
|
54
|
+
|
48
55
|
# bundle exec ./task_selectors.rb
|
49
56
|
# :nocov:
|
50
57
|
class << self
|
data/lib/checkoff/tasks.rb
CHANGED
@@ -27,6 +27,7 @@ module Checkoff
|
|
27
27
|
# @param workspaces [Checkoff::Workspaces]
|
28
28
|
# @param sections [Checkoff::Sections]
|
29
29
|
# @param time_class [Class<Time>]
|
30
|
+
# @param date_class [Class<Date>]
|
30
31
|
# @param asana_task [Class<Asana::Resources::Task>]
|
31
32
|
def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
|
32
33
|
client: Checkoff::Clients.new(config: config).client,
|
@@ -35,10 +36,12 @@ module Checkoff
|
|
35
36
|
sections: Checkoff::Sections.new(config: config,
|
36
37
|
client: client),
|
37
38
|
time_class: Time,
|
39
|
+
date_class: Date,
|
38
40
|
asana_task: Asana::Resources::Task)
|
39
41
|
@config = config
|
40
42
|
@sections = sections
|
41
43
|
@time_class = time_class
|
44
|
+
@date_class = date_class
|
42
45
|
@asana_task = asana_task
|
43
46
|
@client = client
|
44
47
|
@workspaces = workspaces
|
@@ -167,7 +170,7 @@ module Checkoff
|
|
167
170
|
|
168
171
|
# @return [Checkoff::Internal::TaskTiming]
|
169
172
|
def task_timing
|
170
|
-
@task_timing ||= Checkoff::Internal::TaskTiming.new(time_class: @time_class)
|
173
|
+
@task_timing ||= Checkoff::Internal::TaskTiming.new(time_class: @time_class, date_class: @date_class)
|
171
174
|
end
|
172
175
|
|
173
176
|
# @return [Checkoff::Internal::TaskHashes]
|
@@ -0,0 +1,121 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require 'forwardable'
|
6
|
+
require 'cache_method'
|
7
|
+
require_relative 'internal/config_loader'
|
8
|
+
require_relative 'workspaces'
|
9
|
+
require_relative 'clients'
|
10
|
+
|
11
|
+
# https://developers.asana.com/reference/timelines
|
12
|
+
|
13
|
+
module Checkoff
|
14
|
+
# Manages timelines of dependent tasks with dates and milestones
|
15
|
+
class Timelines
|
16
|
+
# @!parse
|
17
|
+
# extend CacheMethod::ClassMethods
|
18
|
+
|
19
|
+
MINUTE = 60
|
20
|
+
HOUR = MINUTE * 60
|
21
|
+
DAY = 24 * HOUR
|
22
|
+
REALLY_LONG_CACHE_TIME = HOUR * 1
|
23
|
+
LONG_CACHE_TIME = MINUTE * 15
|
24
|
+
SHORT_CACHE_TIME = MINUTE
|
25
|
+
|
26
|
+
# @param config [Hash]
|
27
|
+
# @param workspaces [Checkoff::Workspaces]
|
28
|
+
# @param sections [Checkoff::Sections]
|
29
|
+
# @param tasks [Checkoff::Tasks]
|
30
|
+
# @param clients [Checkoff::Clients]
|
31
|
+
# @param client [Asana::Client]
|
32
|
+
def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
|
33
|
+
workspaces: Checkoff::Workspaces.new(config: config),
|
34
|
+
sections: Checkoff::Sections.new(config: config),
|
35
|
+
tasks: Checkoff::Tasks.new(config: config),
|
36
|
+
clients: Checkoff::Clients.new(config: config),
|
37
|
+
client: clients.client)
|
38
|
+
@workspaces = workspaces
|
39
|
+
@sections = sections
|
40
|
+
@tasks = tasks
|
41
|
+
@client = client
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param task [Asana::Resources::Task]
|
45
|
+
# @param limit_to_portfolio_gid [String, nil]
|
46
|
+
# @param project_name [String]
|
47
|
+
def task_dependent_on_previous_section_last_milestone?(task, limit_to_portfolio_gid: nil)
|
48
|
+
task_data = @tasks.task_to_h(task)
|
49
|
+
# @sg-ignore
|
50
|
+
# @type [Array<Hash{String => Hash{String => String}}>]
|
51
|
+
memberships_data = task_data.fetch('memberships')
|
52
|
+
memberships_data.all? do |membership_data|
|
53
|
+
# @type [Hash{String => String}]
|
54
|
+
section_data = membership_data.fetch('section')
|
55
|
+
section_gid = section_data.fetch('gid')
|
56
|
+
section = @sections.section_by_gid(section_gid)
|
57
|
+
task_data_dependent_on_previous_section_last_milestone?(task_data, section)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param section_gid [String]
|
62
|
+
#
|
63
|
+
# @return [Asana::Resources::Task,nil]
|
64
|
+
def last_milestone_in_section(section_gid)
|
65
|
+
# @type [Array<Asana::Resources::Task>]
|
66
|
+
task_list = @sections.tasks_by_section_gid(section_gid).to_a
|
67
|
+
last_task = task_list.last
|
68
|
+
last_task&.resource_subtype == 'milestone' ? last_task : nil
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
# @param task_data [Hash]
|
74
|
+
# @param section [Asana::Resources::Section]
|
75
|
+
#
|
76
|
+
# @return [Boolean]
|
77
|
+
def task_data_dependent_on_previous_section_last_milestone?(task_data, section)
|
78
|
+
# @sg-ignore
|
79
|
+
# @type [Array<Hash{String => String}>]
|
80
|
+
dependencies = task_data.fetch('dependencies')
|
81
|
+
return false if dependencies.empty?
|
82
|
+
|
83
|
+
previous_section = @sections.previous_section(section)
|
84
|
+
return false if previous_section.nil?
|
85
|
+
|
86
|
+
previous_section_last_milestone = last_milestone_in_section(previous_section.gid)
|
87
|
+
return false if previous_section_last_milestone.nil?
|
88
|
+
|
89
|
+
dependencies.any? { |dependency| dependency.fetch('gid') == previous_section_last_milestone.gid }
|
90
|
+
end
|
91
|
+
|
92
|
+
# @return [Checkoff::Workspaces]
|
93
|
+
attr_reader :workspaces
|
94
|
+
|
95
|
+
# @return [Asana::Client]
|
96
|
+
attr_reader :client
|
97
|
+
|
98
|
+
# bundle exec ./timelines.rb
|
99
|
+
# :nocov:
|
100
|
+
class << self
|
101
|
+
# @return [void]
|
102
|
+
def run
|
103
|
+
# @sg-ignore
|
104
|
+
# @type [String]
|
105
|
+
# workspace_name = ARGV[0] || raise('Please pass workspace name as first argument')
|
106
|
+
# @sg-ignore
|
107
|
+
# @type [String]
|
108
|
+
# timeline_name = ARGV[1] || raise('Please pass timeline name as second argument')
|
109
|
+
# timelines = Checkoff::Timelines.new
|
110
|
+
# timeline = timelines.timeline_or_raise(workspace_name, timeline_name)
|
111
|
+
# puts "Results: #{timeline}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
# :nocov:
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# :nocov:
|
119
|
+
abs_program_name = File.expand_path($PROGRAM_NAME)
|
120
|
+
Checkoff::Timelines.run if abs_program_name == File.expand_path(__FILE__)
|
121
|
+
# :nocov:
|
data/lib/checkoff/version.rb
CHANGED
data/lib/checkoff.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.99.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-10-
|
11
|
+
date: 2023-10-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -166,6 +166,7 @@ files:
|
|
166
166
|
- lib/checkoff/task_searches.rb
|
167
167
|
- lib/checkoff/task_selectors.rb
|
168
168
|
- lib/checkoff/tasks.rb
|
169
|
+
- lib/checkoff/timelines.rb
|
169
170
|
- lib/checkoff/timing.rb
|
170
171
|
- lib/checkoff/version.rb
|
171
172
|
- lib/checkoff/workspaces.rb
|