checkoff 0.128.0 → 0.130.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bdeff27a19c7a24911c5864a8f376979b78e19b17d56992cb1e9ef1c33c815f5
4
- data.tar.gz: 0e7ad84caa524a58699903a35b08b0865c6c32b5d494ab654b9df5c68506c97c
3
+ metadata.gz: 671e124a1d07f8449c10381cdaf97619ae58cd56dda7adcf2672396c8cae1c07
4
+ data.tar.gz: fd7c3e0b5f1dd48e7a6281a807c92a7f6d7614d8d71e27bdf45895e1f98246d7
5
5
  SHA512:
6
- metadata.gz: 34d9d43590df498315a1ee700c812de55e014e2b9f67a41b725baa8da89e907f93c6af5486275743e9bfb52e4a80f95089026dd5feb82f13e470f58f477cf037
7
- data.tar.gz: f6c41733167ddc1966d12fba1b6a00ab634eba365748531e4e9dde5096becb6e8dc43cc0fa07ffb330e5cfd18b00dbe2c81ee10d6c1c6f22837bb0273025908c
6
+ metadata.gz: ee30df69be122ddcc9cfe7cfe5a72bab8ab53df71ddc0abbd81cab05767f25cea01047f2da2c6652831c4b5558ca23e126f02a0343c6f9963166c8047b93a634
7
+ data.tar.gz: 85e37bfc18623f783b70ab4724d1b19e3bfdf27c67c130e9c3a23d8b85faf938bdac54f15210d9b073641ec2d7fdcdca0b0a41630e4b660edb3b973c7c2a75ea
data/GLOSSARY.md CHANGED
@@ -2,10 +2,15 @@
2
2
 
3
3
  * ready: See tasks.rb#task_ready?. Indicates a task is ready for
4
4
  a person to work on it. This is subtly different than what is used
5
- by Asana to mark a date as red/green! A task is ready if it is not
6
- dependent on an incomplete task and one of these is true:
5
+ by Asana to mark a date as red/green!
6
+
7
+ A task is ready if it is not dependent on an incomplete task and one
8
+ of these is true:
7
9
 
8
10
  * start is null and due on is today
9
11
  * start is null and due at is before now
10
12
  * start on is today
11
13
  * start at is before now
14
+
15
+ A project is ready if there is no start date, or if the start date
16
+ is today or in the past.
data/Gemfile.lock CHANGED
@@ -12,7 +12,7 @@ GIT
12
12
  PATH
13
13
  remote: .
14
14
  specs:
15
- checkoff (0.128.0)
15
+ checkoff (0.130.0)
16
16
  activesupport
17
17
  asana (> 0.10.0)
18
18
  cache_method
@@ -209,6 +209,13 @@
209
209
  #
210
210
  # # @return [Asana::Resources::Portfolio,nil]
211
211
  # def find_by_id(client, id, options: {}); end
212
+ # # Get portfolio items
213
+ # #
214
+ # # @param portfolio_gid [String] (required) Globally unique identifier for the portfolio.
215
+ # # @param options [Hash] the request I/O options
216
+ # #
217
+ # # @return [Enumerable<Asana::Resources::Project>]
218
+ # def get_items_for_portfolio(portfolio_gid: required("portfolio_gid"), options: {}); end
212
219
  # end
213
220
  # class User
214
221
  # # Returns the full user record for the currently authenticated user.
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Checkoff
4
+ module Internal
5
+ # Utility methods for working with project dates and times
6
+ class ProjectTiming
7
+ # @param time_class [Class<Time>]
8
+ # @param date_class [Class<Date>]
9
+ # @param client [Asana::Client]
10
+ # @param custom_fields [Checkoff::CustomFields]
11
+ def initialize(time_class: Time, date_class: Date,
12
+ client: Checkoff::Clients.new.client,
13
+ custom_fields: Checkoff::CustomFields.new(client: client))
14
+ @time_class = time_class
15
+ @date_class = date_class
16
+ @custom_fields = custom_fields
17
+ end
18
+
19
+ # @param project [Asana::Resources::Project]
20
+ # @param field_name [Symbol]
21
+ #
22
+ # @sg-ignore
23
+ # @return [Date, nil]
24
+ def start_date(project)
25
+ return @date_class.parse(project.start_on) unless project.start_on.nil?
26
+
27
+ nil
28
+ end
29
+
30
+ # @param project [Asana::Resources::Project]
31
+ # @param field_name [Symbol]
32
+ #
33
+ # @sg-ignore
34
+ # @return [Date, nil]
35
+ def due_date(project)
36
+ return @date_class.parse(project.due_on) unless project.due_on.nil?
37
+
38
+ nil
39
+ end
40
+
41
+ # @param project [Asana::Resources::Project]
42
+ # @param custom_field_name [String]
43
+ #
44
+ # @return [Time, Date, nil]
45
+ def custom_field(project, custom_field_name)
46
+ custom_field = @custom_fields.resource_custom_field_by_name_or_raise(project, custom_field_name)
47
+ # @sg-ignore
48
+ # @type [String, nil]
49
+ time_str = custom_field.fetch('display_value')
50
+ return nil if time_str.nil?
51
+
52
+ Time.parse(time_str)
53
+ end
54
+
55
+ # @param project [Asana::Resources::Project]
56
+ # @param field_name [Symbol,Array]
57
+ #
58
+ # @sg-ignore
59
+ # @return [Date, Time, nil]
60
+ def date_or_time_field_by_name(project, field_name)
61
+ return due_date(project) if field_name == :due
62
+
63
+ return start_date(project) if field_name == :start
64
+
65
+ return start_date(project) if field_name == :ready
66
+
67
+ if field_name.is_a?(Array)
68
+ # @sg-ignore
69
+ # @type [Symbol]
70
+ actual_field_name = field_name.first
71
+ args = field_name[1..]
72
+
73
+ return custom_field(project, *args) if actual_field_name == :custom_field
74
+ end
75
+
76
+ raise "Teach me how to handle field #{field_name}"
77
+ end
78
+ end
79
+ end
80
+ end
@@ -20,6 +20,22 @@ module Checkoff
20
20
  resource.due_date
21
21
  end
22
22
  end
23
+
24
+ # :ready? function
25
+ class ReadyPFunctionEvaluator < FunctionEvaluator
26
+ FUNCTION_NAME = :ready?
27
+
28
+ def matches?
29
+ fn?(selector, FUNCTION_NAME)
30
+ end
31
+
32
+ # @param project [Asana::Resources::Project]
33
+ # @param period [Symbol<:now_or_before,:this_week>]
34
+ # @return [Boolean]
35
+ def evaluate(project, period = :now_or_before)
36
+ @projects.project_ready?(project, period: period)
37
+ end
38
+ end
23
39
  end
24
40
  end
25
41
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'asana'
4
+
5
+ # Monkeypatches Asana::Resources::Resource so that Ruby marshalling and
6
+ # unmarshalling works on Asana resource classes. Currently, it will
7
+ # work unless you call an accessor method, which triggers Asana's
8
+ # client library Resource class' method_missing() to "cache" the
9
+ # result by creating a singleton method. Unfortunately, singleton
10
+ # methods break marshalling, which is not smart enough to know that it
11
+ # is not necessary to marshall them as they will simply be recreated
12
+ # when needed.
13
+
14
+ module Asana
15
+ # Monkeypatches:
16
+ #
17
+ # https://github.com/Asana/ruby-asana/blob/master/lib/asana
18
+ module Resources
19
+ # Public: The base resource class which provides some sugar over common
20
+ # resource functionality.
21
+ class Resource
22
+ # @return [Hash]
23
+ def marshal_dump
24
+ { 'data' => @_data }
25
+ end
26
+
27
+ # @param data [Hash]
28
+ #
29
+ # @return [void]
30
+ def marshal_load(data)
31
+ # @sg-ignore
32
+ # @type [Hash]
33
+ @_data = data.fetch('data')
34
+ @_data.each do |k, v|
35
+ if respond_to?(k)
36
+ variable = :"@#{k}"
37
+ instance_variable_set(variable, v)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -99,7 +99,7 @@ module Checkoff
99
99
  fields: ['name'],
100
100
  }
101
101
  options[:fields] += extra_project_fields
102
- portfolio.get_items(options: options)
102
+ client.portfolios.get_items_for_portfolio(portfolio_gid: portfolio.gid, options: options)
103
103
  end
104
104
 
105
105
  private
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'internal/config_loader'
4
4
  require_relative 'internal/project_hashes'
5
+ require_relative 'internal/project_timing'
5
6
  require_relative 'workspaces'
6
7
  require_relative 'clients'
7
8
  require 'cache_method'
@@ -32,15 +33,21 @@ module Checkoff
32
33
  # @param client [Asana::Client]
33
34
  # @param workspaces [Checkoff::Workspaces]
34
35
  # @param project_hashes [Checkoff::Internal::ProjectHashes]
36
+ # @param project_timing [Checkoff::Internal::ProjectTiming]
37
+ # @param timing [Checkoff::Timing]
35
38
  def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
36
39
  client: Checkoff::Clients.new(config: config).client,
37
40
  workspaces: Checkoff::Workspaces.new(config: config,
38
41
  client: client),
39
- project_hashes: Checkoff::Internal::ProjectHashes.new)
42
+ project_hashes: Checkoff::Internal::ProjectHashes.new,
43
+ project_timing: Checkoff::Internal::ProjectTiming.new(client: client),
44
+ timing: Checkoff::Timing.new)
40
45
  @config = config
41
46
  @workspaces = workspaces
42
47
  @client = client
43
48
  @project_hashes = project_hashes
49
+ @project_timing = project_timing
50
+ @timing = timing
44
51
  end
45
52
 
46
53
  # Default options used in Asana API to pull tasks
@@ -139,8 +146,37 @@ module Checkoff
139
146
  project_hashes.project_to_h(project_obj, project: project)
140
147
  end
141
148
 
149
+ # Indicates a project is ready for a person to work on it. This
150
+ # is subtly different than what is used by Asana to mark a date as
151
+ # red/green!
152
+ #
153
+ # A project is ready if there is no start date, or if the start
154
+ # date is today or in the past.
155
+ #
156
+ # @param project [Asana::Resources::Project]
157
+ # @param period [Symbol<:now_or_before,:this_week>]
158
+ def project_ready?(project, period: :now_or_before)
159
+ in_period?(project, :ready, period)
160
+ end
161
+
162
+ # @param project [Asana::Resources::Project]
163
+ # @param field_name [Symbol,Array]
164
+ # @param period [Symbol<:now_or_before,:this_week>,Array] See Checkoff::Timing#in_period?
165
+ def in_period?(project, field_name, period)
166
+ # @type [Date,Time,nil]
167
+ project_date = project_timing.date_or_time_field_by_name(project, field_name)
168
+
169
+ timing.in_period?(project_date, period)
170
+ end
171
+
142
172
  private
143
173
 
174
+ # @return [Checkoff::Timing]
175
+ attr_reader :timing
176
+
177
+ # @return [Checkoff::Internal::ProjectTiming]
178
+ attr_reader :project_timing
179
+
144
180
  # @return [Checkoff::Internal::ProjectHashes]
145
181
  attr_reader :project_hashes
146
182
 
@@ -54,7 +54,7 @@ module Checkoff
54
54
  @portfolios = portfolios
55
55
  @custom_fields = custom_fields
56
56
  @workspaces = workspaces
57
- @timing = Timing.new(today_getter: date_class, now_getter: time_class)
57
+ @timing = Checkoff::Timing.new(today_getter: date_class, now_getter: time_class)
58
58
  end
59
59
 
60
60
  # Indicates a task is ready for a person to work on it. This is
@@ -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.128.0'
6
+ VERSION = '0.130.0'
7
7
  end
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.128.0
4
+ version: 0.130.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-11-04 00:00:00.000000000 Z
11
+ date: 2023-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -135,6 +135,7 @@ files:
135
135
  - lib/checkoff/internal/create-class.sh
136
136
  - lib/checkoff/internal/project_hashes.rb
137
137
  - lib/checkoff/internal/project_selector_evaluator.rb
138
+ - lib/checkoff/internal/project_timing.rb
138
139
  - lib/checkoff/internal/search_url.rb
139
140
  - lib/checkoff/internal/search_url/custom_field_param_converter.rb
140
141
  - lib/checkoff/internal/search_url/custom_field_variant.rb
@@ -156,6 +157,7 @@ files:
156
157
  - lib/checkoff/internal/task_hashes.rb
157
158
  - lib/checkoff/internal/task_selector_evaluator.rb
158
159
  - lib/checkoff/internal/task_timing.rb
160
+ - lib/checkoff/monkeypatches/resource_marshalling.rb
159
161
  - lib/checkoff/my_tasks.rb
160
162
  - lib/checkoff/portfolios.rb
161
163
  - lib/checkoff/project_selectors.rb