checkoff 0.92.0 → 0.94.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/config/definitions.rb +33 -0
- data/lib/checkoff/create-entity.sh +32 -7
- data/lib/checkoff/internal/selector_classes/task.rb +1 -1
- data/lib/checkoff/internal/task_timing.rb +31 -0
- data/lib/checkoff/portfolios.rb +112 -0
- data/lib/checkoff/tasks.rb +18 -31
- data/lib/checkoff/version.rb +1 -1
- data/lib/checkoff.rb +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3728a0da90fb49dedd232b05722fbb87f03789d3bf9bc15513e4fa4b88f9e6fc
|
4
|
+
data.tar.gz: 16ce81c8b51905620414ff21f9469ce83c100a61985f355b8a27de20b7234b3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d73466d69b7b116eb8c58c0053b7cf13cedf288477e7cc8862de091fdd183e5a84355e19841e09df003da9b44d2b87ac90a459845c9186737069002f5312e455
|
7
|
+
data.tar.gz: 207d7d34ecdc3435892afe3b96860df2373e36067f8c75866f7b87d5a6f0aac0ebe89b0889b45d6a31ddec1ac6211f592fce4b657aa70ffad5e5c504deeea773
|
data/Gemfile.lock
CHANGED
data/config/definitions.rb
CHANGED
@@ -33,6 +33,10 @@
|
|
33
33
|
# def projects; end
|
34
34
|
# # @return [Asana::ProxiedResourceClasses::UserTaskList]
|
35
35
|
# def user_task_lists; end
|
36
|
+
# # @return [Asana::ProxiedResourceClasses::Portfolio]
|
37
|
+
# def portfolios; end
|
38
|
+
# # @return [Asana::ProxiedResourceClasses::User]
|
39
|
+
# def users; end
|
36
40
|
# end
|
37
41
|
# class Collection < Asana::Resources::Collection; end
|
38
42
|
# module Resources
|
@@ -160,6 +164,35 @@
|
|
160
164
|
# def get_user_task_list_for_user(client, user_gid:,
|
161
165
|
# workspace: nil, options: {}); end
|
162
166
|
# end
|
167
|
+
# class Portfolio
|
168
|
+
# # Returns a list of the portfolios in compact representation that are owned
|
169
|
+
# # by the current API user.
|
170
|
+
# #
|
171
|
+
# # @param workspace [Gid] The workspace or organization to filter portfolios on.
|
172
|
+
# # @param owner [String] The user who owns the portfolio. Currently, API users can only get a
|
173
|
+
# # list of portfolios that they themselves own.
|
174
|
+
# #
|
175
|
+
# # @param per_page [Integer] the number of records to fetch per page.
|
176
|
+
# # @param options [Hash] the request I/O options.
|
177
|
+
# #
|
178
|
+
# # @return [Enumerable<Asana::Resources::Portfolio>]
|
179
|
+
# def find_all(workspace: required("workspace"), owner: required("owner"), per_page: 20, options: {}); end
|
180
|
+
# # Returns the complete record for a single portfolio.
|
181
|
+
# #
|
182
|
+
# # @param id [Gid] The portfolio to get.
|
183
|
+
# # @param options [Hash] the request I/O options.
|
184
|
+
#
|
185
|
+
# # @return [Asana::Resources::Portfolio,nil]
|
186
|
+
# def find_by_id(client, id, options: {}); end
|
187
|
+
# end
|
188
|
+
# class User
|
189
|
+
# # Returns the full user record for the currently authenticated user.
|
190
|
+
# #
|
191
|
+
# # @param options [Hash] the request I/O options.
|
192
|
+
# #
|
193
|
+
# # @return [Asana::Resources::User]
|
194
|
+
# def me(options: {}); end
|
195
|
+
# end
|
163
196
|
# end
|
164
197
|
# end
|
165
198
|
# rubocop:enable Layout/LineLength
|
@@ -8,6 +8,8 @@ underscored_plural_name="${1:?underscored plural name of entities minus .rb}"
|
|
8
8
|
underscored_singular_name=$(sed -e 's/s$//g' <<< "${underscored_plural_name}")
|
9
9
|
kabob_case_plural_name=${underscored_plural_name/_/-}
|
10
10
|
class_name="${2:?class name without Checkoff:: prefix}"
|
11
|
+
# shellcheck disable=SC2001
|
12
|
+
asana_resource_class_name=Asana::Resources::$(sed -e 's/s$//g' <<< "${class_name}")
|
11
13
|
|
12
14
|
cat > "${underscored_plural_name}.rb" << EOF
|
13
15
|
#!/usr/bin/env ruby
|
@@ -20,10 +22,13 @@ require_relative 'internal/config_loader'
|
|
20
22
|
require_relative 'workspaces'
|
21
23
|
require_relative 'clients'
|
22
24
|
|
23
|
-
# https://developers.asana.com/
|
25
|
+
# https://developers.asana.com/reference/${kabob_case_plural_name}
|
24
26
|
|
25
27
|
module Checkoff
|
26
28
|
class ${class_name}
|
29
|
+
# @!parse
|
30
|
+
# extend CacheMethod::ClassMethods
|
31
|
+
|
27
32
|
MINUTE = 60
|
28
33
|
HOUR = MINUTE * 60
|
29
34
|
DAY = 24 * HOUR
|
@@ -31,6 +36,10 @@ module Checkoff
|
|
31
36
|
LONG_CACHE_TIME = MINUTE * 15
|
32
37
|
SHORT_CACHE_TIME = MINUTE
|
33
38
|
|
39
|
+
# @param config [Hash]
|
40
|
+
# @param workspaces [Checkoff::Workspaces]
|
41
|
+
# @param clients [Checkoff::Clients]
|
42
|
+
# @param client [Asana::Client]
|
34
43
|
def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
|
35
44
|
workspaces: Checkoff::Workspaces.new(config: config),
|
36
45
|
clients: Checkoff::Clients.new(config: config),
|
@@ -39,31 +48,47 @@ module Checkoff
|
|
39
48
|
@client = client
|
40
49
|
end
|
41
50
|
|
51
|
+
# @param workspace_name [String]
|
52
|
+
# @param ${underscored_singular_name}_name [String]
|
53
|
+
#
|
54
|
+
# @return [${asana_resource_class_name}]
|
42
55
|
def ${underscored_singular_name}_or_raise(workspace_name, ${underscored_singular_name}_name)
|
43
|
-
${underscored_singular_name} = ${underscored_singular_name}(workspace_name, ${underscored_singular_name}_name)
|
44
|
-
raise "Could not find ${underscored_singular_name} #{${underscored_singular_name}_name} under workspace #{workspace_name}." if ${underscored_singular_name}.nil?
|
56
|
+
${underscored_singular_name}_obj = ${underscored_singular_name}(workspace_name, ${underscored_singular_name}_name)
|
57
|
+
raise "Could not find ${underscored_singular_name} #{${underscored_singular_name}_name} under workspace #{workspace_name}." if ${underscored_singular_name}_obj.nil?
|
45
58
|
|
46
|
-
${underscored_singular_name}
|
59
|
+
${underscored_singular_name}_obj
|
47
60
|
end
|
48
61
|
cache_method :${underscored_singular_name}_or_raise, LONG_CACHE_TIME
|
49
62
|
|
63
|
+
# @param workspace_name [String]
|
64
|
+
# @param ${underscored_singular_name}_name [String]
|
65
|
+
#
|
66
|
+
# @return [${asana_resource_class_name},nil]
|
50
67
|
def ${underscored_singular_name}(workspace_name, ${underscored_singular_name}_name)
|
51
68
|
workspace = workspaces.workspace_or_raise(workspace_name)
|
52
|
-
${
|
53
|
-
${
|
69
|
+
${underscored_singular_name}_objs = client.${underscored_plural_name}.get_${underscored_plural_name}_for_workspace(workspace_gid: workspace.gid)
|
70
|
+
${underscored_singular_name}_objs.find { |${underscored_singular_name}_obj| ${underscored_singular_name}_obj.name == ${underscored_singular_name}_name }
|
54
71
|
end
|
55
72
|
cache_method :${underscored_singular_name}, LONG_CACHE_TIME
|
56
73
|
|
57
74
|
private
|
58
75
|
|
59
|
-
|
76
|
+
# @return [Checkoff::Workspaces]
|
77
|
+
attr_reader :workspaces
|
78
|
+
|
79
|
+
# @return [Asana::Client]
|
80
|
+
attr_reader :client
|
60
81
|
|
61
82
|
# bundle exec ./${underscored_plural_name}.rb
|
62
83
|
# :nocov:
|
63
84
|
class << self
|
64
85
|
# @return [void]
|
65
86
|
def run
|
87
|
+
# @sg-ignore
|
88
|
+
# @type [String]
|
66
89
|
workspace_name = ARGV[0] || raise('Please pass workspace name as first argument')
|
90
|
+
# @sg-ignore
|
91
|
+
# @type [String]
|
67
92
|
${underscored_singular_name}_name = ARGV[1] || raise('Please pass ${underscored_singular_name} name as second argument')
|
68
93
|
${underscored_plural_name} = Checkoff::${class_name}.new
|
69
94
|
${underscored_singular_name} = ${underscored_plural_name}.${underscored_singular_name}_or_raise(workspace_name, ${underscored_singular_name}_name)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Checkoff
|
4
|
+
module Internal
|
5
|
+
# Utility methods for working with task dates and times
|
6
|
+
class TaskTiming
|
7
|
+
# @param time_class [Class<Time>]
|
8
|
+
def initialize(time_class: Time)
|
9
|
+
@time_class = time_class
|
10
|
+
end
|
11
|
+
|
12
|
+
# @param task [Asana::Resources::Task]
|
13
|
+
# @return [Time, nil]
|
14
|
+
def start_time(task)
|
15
|
+
return @time_class.parse(task.start_at) if task.start_at
|
16
|
+
return @time_class.parse(task.start_on) if task.start_on
|
17
|
+
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param task [Asana::Resources::Task]
|
22
|
+
# @return [Time, nil]
|
23
|
+
def due_time(task)
|
24
|
+
return @time_class.parse(task.due_at) if task.due_at
|
25
|
+
return @time_class.parse(task.due_on) if task.due_on
|
26
|
+
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,112 @@
|
|
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/portfolios
|
12
|
+
|
13
|
+
module Checkoff
|
14
|
+
# Pull portfolios from Asana
|
15
|
+
class Portfolios
|
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 clients [Checkoff::Clients]
|
29
|
+
# @param client [Asana::Client]
|
30
|
+
def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
|
31
|
+
workspaces: Checkoff::Workspaces.new(config: config),
|
32
|
+
clients: Checkoff::Clients.new(config: config),
|
33
|
+
client: clients.client)
|
34
|
+
@workspaces = workspaces
|
35
|
+
@client = client
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param workspace_name [String]
|
39
|
+
# @param portfolio_name [String]
|
40
|
+
#
|
41
|
+
# @return [Asana::Resources::Portfolio]
|
42
|
+
def portfolio_or_raise(workspace_name, portfolio_name)
|
43
|
+
portfolio_obj = portfolio(workspace_name, portfolio_name)
|
44
|
+
raise "Could not find portfolio #{portfolio_name} under workspace #{workspace_name}." if portfolio_obj.nil?
|
45
|
+
|
46
|
+
portfolio_obj
|
47
|
+
end
|
48
|
+
cache_method :portfolio_or_raise, LONG_CACHE_TIME
|
49
|
+
|
50
|
+
# @param workspace_name [String]
|
51
|
+
# @param portfolio_name [String]
|
52
|
+
#
|
53
|
+
# @sg-ignore
|
54
|
+
# @return [Asana::Resources::Portfolio,nil]
|
55
|
+
def portfolio(workspace_name, portfolio_name)
|
56
|
+
workspace = workspaces.workspace_or_raise(workspace_name)
|
57
|
+
me = client.users.me
|
58
|
+
portfolio_objs = client.portfolios.find_all(workspace: workspace.gid,
|
59
|
+
owner: me.gid)
|
60
|
+
# @type [Asana::Resources::Portfolio, nil]
|
61
|
+
portfolio_objs.find { |portfolio_obj| portfolio_obj.name == portfolio_name }
|
62
|
+
end
|
63
|
+
cache_method :portfolio, LONG_CACHE_TIME
|
64
|
+
|
65
|
+
# Pull a specific portfolio by gid
|
66
|
+
#
|
67
|
+
# @param portfolio_gid [String]
|
68
|
+
# @param extra_fields [Array<String>]
|
69
|
+
#
|
70
|
+
# @return [Asana::Resources::Portfolio, nil]
|
71
|
+
def portfolio_by_gid(portfolio_gid,
|
72
|
+
extra_fields: [])
|
73
|
+
options = {
|
74
|
+
fields: ['name'],
|
75
|
+
}
|
76
|
+
options[:fields] += extra_fields
|
77
|
+
client.portfolios.find_by_id(portfolio_gid, options: options)
|
78
|
+
end
|
79
|
+
cache_method :portfolio_by_gid, SHORT_CACHE_TIME
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
# @return [Checkoff::Workspaces]
|
84
|
+
attr_reader :workspaces
|
85
|
+
|
86
|
+
# @return [Asana::Client]
|
87
|
+
attr_reader :client
|
88
|
+
|
89
|
+
# bundle exec ./portfolios.rb
|
90
|
+
# :nocov:
|
91
|
+
class << self
|
92
|
+
# @return [void]
|
93
|
+
def run
|
94
|
+
# @sg-ignore
|
95
|
+
# @type [String]
|
96
|
+
workspace_name = ARGV[0] || raise('Please pass workspace name as first argument')
|
97
|
+
# @sg-ignore
|
98
|
+
# @type [String]
|
99
|
+
portfolio_name = ARGV[1] || raise('Please pass portfolio name as second argument')
|
100
|
+
portfolios = Checkoff::Portfolios.new
|
101
|
+
portfolio = portfolios.portfolio_or_raise(workspace_name, portfolio_name)
|
102
|
+
puts "Results: #{portfolio}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
# :nocov:
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# :nocov:
|
110
|
+
abs_program_name = File.expand_path($PROGRAM_NAME)
|
111
|
+
Checkoff::Portfolios.run if abs_program_name == File.expand_path(__FILE__)
|
112
|
+
# :nocov:
|
data/lib/checkoff/tasks.rb
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
require_relative 'sections'
|
6
6
|
require_relative 'workspaces'
|
7
7
|
require_relative 'internal/config_loader'
|
8
|
+
require_relative 'internal/task_timing'
|
8
9
|
require 'asana'
|
9
10
|
|
10
11
|
module Checkoff
|
@@ -57,8 +58,8 @@ module Checkoff
|
|
57
58
|
def task_ready?(task, ignore_dependencies: false)
|
58
59
|
return false if !ignore_dependencies && incomplete_dependencies?(task)
|
59
60
|
|
60
|
-
start = start_time(task)
|
61
|
-
due = due_time(task)
|
61
|
+
start = task_timing.start_time(task)
|
62
|
+
due = task_timing.due_time(task)
|
62
63
|
|
63
64
|
return true if start.nil? && due.nil?
|
64
65
|
|
@@ -79,12 +80,12 @@ module Checkoff
|
|
79
80
|
only_uncompleted: true,
|
80
81
|
extra_fields: [])
|
81
82
|
# @sg-ignore
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
83
|
+
t = tasks(workspace_name,
|
84
|
+
project_name,
|
85
|
+
section_name: section_name,
|
86
|
+
only_uncompleted: only_uncompleted,
|
87
|
+
extra_fields: extra_fields)
|
88
|
+
t.find { |task| task.name == task_name }
|
88
89
|
end
|
89
90
|
cache_method :task, SHORT_CACHE_TIME
|
90
91
|
|
@@ -146,16 +147,20 @@ module Checkoff
|
|
146
147
|
|
147
148
|
private
|
148
149
|
|
150
|
+
# @return [Checkoff::Internal::TaskTiming]
|
151
|
+
def task_timing
|
152
|
+
@task_timing ||= Checkoff::Internal::TaskTiming.new(time_class: @time_class)
|
153
|
+
end
|
154
|
+
|
149
155
|
# @param workspace_name [String]
|
150
156
|
# @param project_name [String, Symbol]
|
151
|
-
# @param section_name [String, nil,
|
157
|
+
# @param section_name [String, nil, Symbol<:unspecified>]
|
152
158
|
# @param only_uncompleted [Boolean]
|
153
159
|
# @param extra_fields [Array<String>]
|
160
|
+
#
|
154
161
|
# @return [Enumerable<Asana::Resources::Task>]
|
155
|
-
def
|
156
|
-
|
157
|
-
only_uncompleted:,
|
158
|
-
extra_fields:)
|
162
|
+
def tasks(workspace_name, project_name,
|
163
|
+
only_uncompleted:, extra_fields:, section_name: :unspecified)
|
159
164
|
if section_name == :unspecified
|
160
165
|
project = projects.project_or_raise(workspace_name, project_name)
|
161
166
|
projects.tasks_from_project(project,
|
@@ -182,23 +187,5 @@ module Checkoff
|
|
182
187
|
def default_assignee_gid
|
183
188
|
@config.fetch(:default_assignee_gid)
|
184
189
|
end
|
185
|
-
|
186
|
-
# @param task [Asana::Resources::Task]
|
187
|
-
# @return [Time, nil]
|
188
|
-
def start_time(task)
|
189
|
-
return @time_class.parse(task.start_at) if task.start_at
|
190
|
-
return @time_class.parse(task.start_on) if task.start_on
|
191
|
-
|
192
|
-
nil
|
193
|
-
end
|
194
|
-
|
195
|
-
# @param task [Asana::Resources::Task]
|
196
|
-
# @return [Time, nil]
|
197
|
-
def due_time(task)
|
198
|
-
return @time_class.parse(task.due_at) if task.due_at
|
199
|
-
return @time_class.parse(task.due_on) if task.due_on
|
200
|
-
|
201
|
-
nil
|
202
|
-
end
|
203
190
|
end
|
204
191
|
end
|
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.94.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-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -153,7 +153,9 @@ files:
|
|
153
153
|
- lib/checkoff/internal/selector_classes/task/function_evaluator.rb
|
154
154
|
- lib/checkoff/internal/selector_evaluator.rb
|
155
155
|
- lib/checkoff/internal/task_selector_evaluator.rb
|
156
|
+
- lib/checkoff/internal/task_timing.rb
|
156
157
|
- lib/checkoff/my_tasks.rb
|
158
|
+
- lib/checkoff/portfolios.rb
|
157
159
|
- lib/checkoff/project_selectors.rb
|
158
160
|
- lib/checkoff/projects.rb
|
159
161
|
- lib/checkoff/section_selectors.rb
|