checkoff 0.92.0 → 0.94.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9b750814d7176f6d66ffefaf0c99995dc1c2d6f44b0dcc99d68f777b84f44941
4
- data.tar.gz: bb0045530c18cf158a8d255818640a6130eb392f2218c49d2da8c2f70e8197de
3
+ metadata.gz: 3728a0da90fb49dedd232b05722fbb87f03789d3bf9bc15513e4fa4b88f9e6fc
4
+ data.tar.gz: 16ce81c8b51905620414ff21f9469ce83c100a61985f355b8a27de20b7234b3c
5
5
  SHA512:
6
- metadata.gz: 10dccc258a5a948f91cee8ff8ee0eef696dfb0a7e5ae92ae07116f26e65364dee2ebf1f89c6fcc0e17fc4ea7c83a89fda89cdab91586c747a54eef1864a7dc74
7
- data.tar.gz: 5aead20f2cf559538115b8f37c394245dd10906603d307a4f4c7879696cd8e03ef40f23b3da7b898f14ae06f48b55136834b6a3bb7420486f7c5915e01ba1a22
6
+ metadata.gz: d73466d69b7b116eb8c58c0053b7cf13cedf288477e7cc8862de091fdd183e5a84355e19841e09df003da9b44d2b87ac90a459845c9186737069002f5312e455
7
+ data.tar.gz: 207d7d34ecdc3435892afe3b96860df2373e36067f8c75866f7b87d5a6f0aac0ebe89b0889b45d6a31ddec1ac6211f592fce4b657aa70ffad5e5c504deeea773
data/Gemfile.lock CHANGED
@@ -12,7 +12,7 @@ GIT
12
12
  PATH
13
13
  remote: .
14
14
  specs:
15
- checkoff (0.92.0)
15
+ checkoff (0.94.0)
16
16
  activesupport
17
17
  asana (> 0.10.0)
18
18
  cache_method
@@ -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/docs/${kabob_case_plural_name}
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
- ${underscored_plural_name} = client.${underscored_plural_name}.get_${underscored_plural_name}_for_workspace(workspace_gid: workspace.gid)
53
- ${underscored_plural_name}.find { |${underscored_singular_name}| ${underscored_singular_name}.name == ${underscored_singular_name}_name }
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
- attr_reader :workspaces, :client
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)
@@ -72,7 +72,7 @@ module Checkoff
72
72
  end
73
73
 
74
74
  # :ready function
75
- class DuePFunctionEvaluator < FunctionEvaluator
75
+ class ReadyFunctionEvaluator < FunctionEvaluator
76
76
  def matches?
77
77
  fn?(selector, :ready)
78
78
  end
@@ -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:
@@ -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
- tasks = tasks_from_section(workspace_name,
83
- project_name,
84
- section_name: section_name,
85
- only_uncompleted: only_uncompleted,
86
- extra_fields: extra_fields)
87
- tasks.find { |task| task.name == task_name }
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, :unspecified]
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 tasks_from_section(workspace_name, project_name,
156
- section_name:,
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
@@ -3,5 +3,5 @@
3
3
  # Command-line and gem client for Asana (unofficial)
4
4
  module Checkoff
5
5
  # Version of library
6
- VERSION = '0.92.0'
6
+ VERSION = '0.94.0'
7
7
  end
data/lib/checkoff.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require 'checkoff/version'
4
4
  require 'checkoff/clients'
5
5
  require 'checkoff/workspaces'
6
+ require 'checkoff/portfolios'
6
7
  require 'checkoff/projects'
7
8
  require 'checkoff/sections'
8
9
  require 'checkoff/subtasks'
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.92.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-22 00:00:00.000000000 Z
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