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 +4 -4
- data/Gemfile.lock +1 -1
- data/lib/checkoff/internal/search_url/custom_field_param_converter.rb +1 -0
- data/lib/checkoff/internal/search_url/custom_field_variant.rb +32 -1
- data/lib/checkoff/internal/section_selector_evaluator.rb +56 -0
- data/lib/checkoff/internal/selector_classes/section/function_evaluator.rb +33 -0
- data/lib/checkoff/internal/selector_classes/section.rb +31 -0
- data/lib/checkoff/my_tasks.rb +2 -1
- data/lib/checkoff/project_selectors.rb +0 -2
- data/lib/checkoff/section_selectors.rb +77 -0
- data/lib/checkoff/sections.rb +7 -4
- data/lib/checkoff/task_selectors.rb +0 -2
- data/lib/checkoff/version.rb +1 -1
- data/lib/checkoff.rb +1 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 651d344e64e8f9b999d1c8d1f235c6657428e11164844e85ea3f53f31dca581b
|
4
|
+
data.tar.gz: bd94b2f08680af330190beb09c6659787e500d7d561a15a80a363a8c4675d158
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '086458eef1a121b9cf27c79d78d18a65289cb7658d37126647a363d784ecaec3251ca05aba3b4f22512a07a35ee012d7fdf58b585950845be0742560e3d0dd50'
|
7
|
+
data.tar.gz: 05e1b5637f49ffd9c0dea9110705a43e79f875cedc699df023e1cc1d68480a7b9b68eb974faf201bc4e0c9efce1ba5333adbeca8bcf84ff0f7c076d57fb07753
|
data/Gemfile.lock
CHANGED
@@ -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
|
-
|
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
|
data/lib/checkoff/my_tasks.rb
CHANGED
@@ -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:
|
data/lib/checkoff/sections.rb
CHANGED
@@ -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
|
-
|
60
|
-
|
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
|
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.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-
|
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
|