checkoff 0.65.0 → 0.67.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: 4505355c1c82cd6052ef5fd0996ecf25a415df8846ce7e21f4d80f5a9b1c788f
4
- data.tar.gz: 2837a88035c53d49ef8ee4bef2cbd2b1c2007f1b2d5441d4640a8680785ef438
3
+ metadata.gz: 651d344e64e8f9b999d1c8d1f235c6657428e11164844e85ea3f53f31dca581b
4
+ data.tar.gz: bd94b2f08680af330190beb09c6659787e500d7d561a15a80a363a8c4675d158
5
5
  SHA512:
6
- metadata.gz: 999cc4babf24a98ab16cbed5349e536e4e7d9a9b08b16c50a73df63dac135dde8740e799c2ea7630d0682685775a9eff984830a0bfcca884900ee604a62f443b
7
- data.tar.gz: df41ae1e0e59e3efa637c06114a9b4370c2e093c932ff022be61f773af3a617c6eecc65ec5f147987bb229701aeea88fcf60163923309e92a19ba0b8b4e3db3e
6
+ metadata.gz: '086458eef1a121b9cf27c79d78d18a65289cb7658d37126647a363d784ecaec3251ca05aba3b4f22512a07a35ee012d7fdf58b585950845be0742560e3d0dd50'
7
+ data.tar.gz: 05e1b5637f49ffd9c0dea9110705a43e79f875cedc699df023e1cc1d68480a7b9b68eb974faf201bc4e0c9efce1ba5333adbeca8bcf84ff0f7c076d57fb07753
data/Gemfile.lock CHANGED
@@ -12,7 +12,7 @@ GIT
12
12
  PATH
13
13
  remote: .
14
14
  specs:
15
- checkoff (0.65.0)
15
+ checkoff (0.67.0)
16
16
  activesupport
17
17
  asana (> 0.10.0)
18
18
  cache_method
@@ -51,6 +51,7 @@ module Checkoff
51
51
  'is_not' => CustomFieldVariant::IsNot,
52
52
  'less_than' => CustomFieldVariant::LessThan,
53
53
  'greater_than' => CustomFieldVariant::GreaterThan,
54
+ 'equals' => CustomFieldVariant::Equals,
54
55
  'doesnt_contain_any' => CustomFieldVariant::DoesntContainAny,
55
56
  'contains_any' => CustomFieldVariant::ContainsAny,
56
57
  'contains_all' => CustomFieldVariant::ContainsAll,
@@ -7,6 +7,8 @@ module Checkoff
7
7
  module CustomFieldVariant
8
8
  # base class for handling different custom_field_#{gid}.variant params
9
9
  class CustomFieldVariant
10
+ # @param [String] gid
11
+ # @param [Hash] remaining_params
10
12
  def initialize(gid, remaining_params)
11
13
  @gid = gid
12
14
  @remaining_params = remaining_params
@@ -14,17 +16,27 @@ module Checkoff
14
16
 
15
17
  private
16
18
 
17
- attr_reader :gid, :remaining_params
19
+ # @return [String]
20
+ attr_reader :gid
18
21
 
22
+ # @return [Hash]
23
+ attr_reader :remaining_params
24
+
25
+ # @return [void]
19
26
  def ensure_no_remaining_params!
20
27
  return if remaining_params.empty?
21
28
 
22
29
  raise "Teach me how to handle these remaining keys: #{remaining_params}"
23
30
  end
24
31
 
32
+ # @param [String] param_name
33
+ #
34
+ # @return [String]
25
35
  def fetch_solo_param(param_name)
26
36
  case remaining_params.keys
27
37
  when [param_name]
38
+ # @sg-ignore
39
+ # @type [Array<String>]
28
40
  param_values = remaining_params.fetch(param_name)
29
41
  unless param_values.length == 1
30
42
  raise "Teach me how to handle these remaining keys for #{param_name}: #{remaining_params}"
@@ -39,6 +51,7 @@ module Checkoff
39
51
 
40
52
  # custom_field_#{gid}.variant = 'less_than'
41
53
  class LessThan < CustomFieldVariant
54
+ # @return [Array<(Hash, Array)>]
42
55
  def convert
43
56
  max_value = fetch_solo_param("custom_field_#{gid}.max")
44
57
  empty_task_selector = []
@@ -48,6 +61,7 @@ module Checkoff
48
61
 
49
62
  # custom_field_#{gid}.variant = 'greater_than'
50
63
  class GreaterThan < CustomFieldVariant
64
+ # @return [Array<(Hash, Array)>]
51
65
  def convert
52
66
  max_value = fetch_solo_param("custom_field_#{gid}.min")
53
67
  empty_task_selector = []
@@ -55,10 +69,21 @@ module Checkoff
55
69
  end
56
70
  end
57
71
 
72
+ # custom_field_#{gid}.variant = 'equals'
73
+ class Equals < CustomFieldVariant
74
+ # @return [Array<(Hash, Array)>]
75
+ def convert
76
+ value = fetch_solo_param("custom_field_#{gid}.value")
77
+ empty_task_selector = []
78
+ [{ "custom_fields.#{gid}.value" => value }, empty_task_selector]
79
+ end
80
+ end
81
+
58
82
  # This is used in the UI for select fields
59
83
  #
60
84
  # custom_field_#{gid}.variant = 'is_not'
61
85
  class IsNot < CustomFieldVariant
86
+ # @return [Array<(Hash, Array)>]
62
87
  def convert
63
88
  selected_options = fetch_solo_param("custom_field_#{gid}.selected_options").split('~')
64
89
 
@@ -75,6 +100,7 @@ module Checkoff
75
100
  #
76
101
  # custom_field_#{gid}.variant = 'doesnt_contain_any'
77
102
  class DoesntContainAny < CustomFieldVariant
103
+ # @return [Array<(Hash, Array)>]
78
104
  def convert
79
105
  selected_options = fetch_solo_param("custom_field_#{gid}.selected_options").split('~')
80
106
 
@@ -90,6 +116,7 @@ module Checkoff
90
116
  #
91
117
  # custom_field_#{gid}.variant = 'contains_any'
92
118
  class ContainsAny < CustomFieldVariant
119
+ # @return [Array<(Hash, Array)>]
93
120
  def convert
94
121
  selected_options = fetch_solo_param("custom_field_#{gid}.selected_options").split('~')
95
122
 
@@ -104,6 +131,7 @@ module Checkoff
104
131
  #
105
132
  # custom_field_#{gid}.variant = 'contains_all'
106
133
  class ContainsAll < CustomFieldVariant
134
+ # @return [Array<(Hash, Array)>]
107
135
  def convert
108
136
  selected_options = fetch_solo_param("custom_field_#{gid}.selected_options").split('~')
109
137
 
@@ -116,6 +144,7 @@ module Checkoff
116
144
 
117
145
  # custom_field_#{gid}.variant = 'no_value'
118
146
  class NoValue < CustomFieldVariant
147
+ # @return [Array<(Hash, Array)>]
119
148
  def convert
120
149
  ensure_no_remaining_params!
121
150
 
@@ -135,6 +164,7 @@ module Checkoff
135
164
  #
136
165
  # Not used for multi-select fields
137
166
  class AnyValue < CustomFieldVariant
167
+ # @return [Array<(Hash, Array)>]
138
168
  def convert
139
169
  ensure_no_remaining_params!
140
170
 
@@ -146,6 +176,7 @@ module Checkoff
146
176
 
147
177
  # custom_field_#{gid}.variant = 'is'
148
178
  class Is < CustomFieldVariant
179
+ # @return [Array<(Hash, Array)>]
149
180
  def convert
150
181
  selected_options = fetch_solo_param("custom_field_#{gid}.selected_options").split('~')
151
182
 
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'selector_classes/common'
4
+ require_relative 'selector_classes/section'
5
+ require_relative 'selector_evaluator'
6
+
7
+ module Checkoff
8
+ # Evaluates section selectors against a section
9
+ class SectionSelectorEvaluator < SelectorEvaluator
10
+ # @param section [Asana::Resources::Project]
11
+ # @param client [Asana::Client]
12
+ # @param projects [Checkoff::Projects]
13
+ # @param sections [Checkoff::Sections]
14
+ def initialize(section:,
15
+ client:,
16
+ projects: Checkoff::Projects.new(client: client),
17
+ sections: Checkoff::Sections.new)
18
+ @item = section
19
+ @client = client
20
+ @projects = projects
21
+ @sections = sections
22
+ super()
23
+ end
24
+
25
+ private
26
+
27
+ COMMON_FUNCTION_EVALUATORS = (Checkoff::SelectorClasses::Common.constants.map do |const|
28
+ Checkoff::SelectorClasses::Common.const_get(const)
29
+ end - [Checkoff::SelectorClasses::Common::FunctionEvaluator]).freeze
30
+
31
+ SECTION_FUNCTION_EVALUATORS = (Checkoff::SelectorClasses::Section.constants.map do |const|
32
+ Checkoff::SelectorClasses::Section.const_get(const)
33
+ end - [Checkoff::SelectorClasses::Section::FunctionEvaluator]).freeze
34
+
35
+ FUNCTION_EVALUTORS = (COMMON_FUNCTION_EVALUATORS + SECTION_FUNCTION_EVALUATORS).freeze
36
+
37
+ # @return [Array<Class<ProjectSelectorClasses::FunctionEvaluator>>]
38
+ def function_evaluators
39
+ FUNCTION_EVALUTORS
40
+ end
41
+
42
+ # @return [Hash]
43
+ def initializer_kwargs
44
+ { sections: sections, projects: projects, client: client }
45
+ end
46
+
47
+ # @return [Asana::Resources::Project]
48
+ attr_reader :item
49
+ # @return [Checkoff::Sections]
50
+ attr_reader :sections
51
+ # @return [Checkoff::Projects]
52
+ attr_reader :projects
53
+ # @return [Asana::Client]
54
+ attr_reader :client
55
+ end
56
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../function_evaluator'
4
+
5
+ module Checkoff
6
+ module SelectorClasses
7
+ module Section
8
+ # Base class to evaluate a project selector function given fully evaluated arguments
9
+ class FunctionEvaluator < ::Checkoff::SelectorClasses::FunctionEvaluator
10
+ # @param selector [Array<(Symbol, Array)>,String]
11
+ # @param client [Asana::Client]
12
+ # @param sections [Checkoff::Sections]
13
+ def initialize(selector:,
14
+ sections:,
15
+ client:,
16
+ **)
17
+ @selector = selector
18
+ @sections = sections
19
+ @client = client
20
+ super()
21
+ end
22
+
23
+ private
24
+
25
+ # @return [Array<(Symbol, Array)>]
26
+ attr_reader :selector
27
+
28
+ # @return [Asana::Client]
29
+ attr_reader :client
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'section/function_evaluator'
4
+
5
+ module Checkoff
6
+ module SelectorClasses
7
+ # Section selector classes
8
+ module Section
9
+ # :ends_with_milestone function
10
+ class DueDateFunctionEvaluator < FunctionEvaluator
11
+ FUNCTION_NAME = :ends_with_milestone
12
+
13
+ def matches?
14
+ fn?(selector, FUNCTION_NAME)
15
+ end
16
+
17
+ # @param section [Asana::Resources::Section]
18
+ #
19
+ # @sg-ignore
20
+ # @return [Boolean]
21
+ def evaluate(section)
22
+ tasks = client.tasks.get_tasks(section: section.gid,
23
+ per_page: 100,
24
+ options: { fields: ['resource_subtype'] })
25
+ # @sg-ignore
26
+ tasks.last&.resource_subtype == 'milestone'
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -61,7 +61,8 @@ module Checkoff
61
61
  # @return [Hash<String, Enumerable<Asana::Resources::Task>>]
62
62
  def by_my_tasks_section(tasks, project_gid)
63
63
  by_section = {}
64
- sections = client.sections.get_sections_for_project(project_gid: project_gid)
64
+ sections = client.sections.get_sections_for_project(project_gid: project_gid,
65
+ options: { fields: ['name'] })
65
66
  sections.each_entry { |section| by_section[section_key(section.name)] = [] }
66
67
  tasks.each do |task|
67
68
  assignee_section = task.assignee_section
@@ -9,8 +9,6 @@ require_relative 'internal/project_selector_evaluator'
9
9
  require_relative 'workspaces'
10
10
  require_relative 'clients'
11
11
 
12
- # https://developers.asana.com/docs/project-selectors
13
-
14
12
  module Checkoff
15
13
  # Filter lists of projects using declarative selectors.
16
14
  class ProjectSelectors
@@ -0,0 +1,77 @@
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 'internal/section_selector_evaluator'
9
+ require_relative 'workspaces'
10
+ require_relative 'clients'
11
+
12
+ module Checkoff
13
+ # Filter lists of sections using declarative selectors.
14
+ class SectionSelectors
15
+ MINUTE = 60
16
+ HOUR = MINUTE * 60
17
+ DAY = 24 * HOUR
18
+ REALLY_LONG_CACHE_TIME = HOUR * 1
19
+ LONG_CACHE_TIME = MINUTE * 15
20
+ SHORT_CACHE_TIME = MINUTE
21
+
22
+ # @param config [Hash<Symbol, Object>]
23
+ # @param workspaces [Checkoff::Workspaces]
24
+ # @param sections [Checkoff::Sections]
25
+ # @param clients [Checkoff::Clients]
26
+ # @param client [Asana::Client]
27
+ def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
28
+ workspaces: Checkoff::Workspaces.new(config: config),
29
+ sections: Checkoff::Sections.new(config: config),
30
+ clients: Checkoff::Clients.new(config: config),
31
+ client: clients.client)
32
+ @workspaces = workspaces
33
+ @sections = sections
34
+ @client = client
35
+ end
36
+
37
+ # @param [Asana::Resources::Section] section
38
+ # @param [Array<(Symbol, Array)>] section_selector Filter based on
39
+ # section details. Examples: [:tag, 'foo'] [:not, [:tag, 'foo']] [:tag, 'foo']
40
+ # @return [Boolean]
41
+ def filter_via_section_selector(section, section_selector)
42
+ # @sg-ignore
43
+ evaluator = SectionSelectorEvaluator.new(section: section, sections: sections, client: client)
44
+ evaluator.evaluate(section_selector)
45
+ end
46
+
47
+ private
48
+
49
+ # @return [Checkoff::Workspaces]
50
+ attr_reader :workspaces
51
+
52
+ # @return [Checkoff::Sections]
53
+ attr_reader :sections
54
+
55
+ # @return [Asana::Client]
56
+ attr_reader :client
57
+
58
+ # bundle exec ./section_selectors.rb
59
+ # :nocov:
60
+ class << self
61
+ # @return [void]
62
+ def run
63
+ # workspace_name = ARGV[0] || raise('Please pass workspace name as first argument')
64
+ # section_selector_name = ARGV[1] || raise('Please pass section_selector name as second argument')
65
+ # section_selectors = Checkoff::SectionSelectors.new
66
+ # section_selector = section_selectors.section_selector_or_raise(workspace_name, section_selector_name)
67
+ # puts "Results: #{section_selector}"
68
+ end
69
+ end
70
+ # :nocov:
71
+ end
72
+ end
73
+
74
+ # :nocov:
75
+ abs_program_name = File.expand_path($PROGRAM_NAME)
76
+ Checkoff::SectionSelectors.run if abs_program_name == File.expand_path(__FILE__)
77
+ # :nocov:
@@ -52,12 +52,14 @@ module Checkoff
52
52
  # Returns a list of Asana API section objects for a given project
53
53
  # @param workspace_name [String]
54
54
  # @param project_name [String, Symbol]
55
+ # @param extra_fields [Array<String>]
55
56
  #
56
57
  # @return [Enumerable<Asana::Resources::Section>]
57
- def sections_or_raise(workspace_name, project_name)
58
+ def sections_or_raise(workspace_name, project_name, extra_fields: [])
59
+ fields = %w[name] + extra_fields
58
60
  project = project_or_raise(workspace_name, project_name)
59
- # @sg-ignore
60
- client.sections.get_sections_for_project(project_gid: project.gid)
61
+ client.sections.get_sections_for_project(project_gid: project.gid,
62
+ options: { fields: fields })
61
63
  end
62
64
  cache_method :sections_or_raise, SHORT_CACHE_TIME
63
65
 
@@ -173,7 +175,8 @@ module Checkoff
173
175
  def by_section(tasks, project_gid)
174
176
  by_section = {}
175
177
  # @sg-ignore
176
- sections = client.sections.get_sections_for_project(project_gid: project_gid)
178
+ sections = client.sections.get_sections_for_project(project_gid: project_gid,
179
+ options: { fields: ['name'] })
177
180
  sections.each_entry { |section| by_section[section_key(section.name)] = [] }
178
181
  tasks.each { |task| file_task_by_section(by_section, task, project_gid) }
179
182
  by_section
@@ -9,8 +9,6 @@ require_relative 'internal/config_loader'
9
9
  require_relative 'internal/task_selector_evaluator'
10
10
  require_relative 'tasks'
11
11
 
12
- # https://developers.asana.com/docs/task-selectors
13
-
14
12
  module Checkoff
15
13
  # Filter lists of tasks using declarative selectors.
16
14
  class TaskSelectors
@@ -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.65.0'
6
+ VERSION = '0.67.0'
7
7
  end
data/lib/checkoff.rb CHANGED
@@ -11,4 +11,5 @@ require 'checkoff/custom_fields'
11
11
  require 'checkoff/tags'
12
12
  require 'checkoff/task_selectors'
13
13
  require 'checkoff/project_selectors'
14
+ require 'checkoff/section_selectors'
14
15
  require 'checkoff/task_searches'
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.65.0
4
+ version: 0.67.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-18 00:00:00.000000000 Z
11
+ date: 2023-10-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -140,11 +140,14 @@ files:
140
140
  - lib/checkoff/internal/search_url/parser.rb
141
141
  - lib/checkoff/internal/search_url/results_merger.rb
142
142
  - lib/checkoff/internal/search_url/simple_param_converter.rb
143
+ - lib/checkoff/internal/section_selector_evaluator.rb
143
144
  - lib/checkoff/internal/selector_classes/common.rb
144
145
  - lib/checkoff/internal/selector_classes/common/function_evaluator.rb
145
146
  - lib/checkoff/internal/selector_classes/function_evaluator.rb
146
147
  - lib/checkoff/internal/selector_classes/project.rb
147
148
  - lib/checkoff/internal/selector_classes/project/function_evaluator.rb
149
+ - lib/checkoff/internal/selector_classes/section.rb
150
+ - lib/checkoff/internal/selector_classes/section/function_evaluator.rb
148
151
  - lib/checkoff/internal/selector_classes/task.rb
149
152
  - lib/checkoff/internal/selector_classes/task/function_evaluator.rb
150
153
  - lib/checkoff/internal/selector_evaluator.rb
@@ -152,6 +155,7 @@ files:
152
155
  - lib/checkoff/my_tasks.rb
153
156
  - lib/checkoff/project_selectors.rb
154
157
  - lib/checkoff/projects.rb
158
+ - lib/checkoff/section_selectors.rb
155
159
  - lib/checkoff/sections.rb
156
160
  - lib/checkoff/subtasks.rb
157
161
  - lib/checkoff/tags.rb