checkoff 0.42.0 → 0.44.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 +2 -2
- data/lib/checkoff/internal/config_loader.rb +17 -1
- data/lib/checkoff/internal/task_selector_evaluator.rb +26 -1
- data/lib/checkoff/task_searches.rb +25 -1
- data/lib/checkoff/tasks.rb +26 -7
- data/lib/checkoff/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 413eeaac4e3e2a4784e5fca4930fed96beedd594b87f1c8d80460aabe4ee1dd3
|
4
|
+
data.tar.gz: 4127604e8d5c3605d49c2fda86fe0c0e1bed95aaeadfc493d8a2b02c19bed94d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 001c49dab8df9d273f2c395dd422509afa82c856712b7dfef9b7c65bf7f23c7d3ada832d32e888b188d38751716ee7e54c8d144b4575cd2de384946002ae6fc5
|
7
|
+
data.tar.gz: a10f6ee8a33870277503458dc19809e642d837899c31c1698ba04942356386fe09c713643db3f3694eca7d5495c43fe13c93d2ae7785e3ad383aeed88651e2f0
|
data/Gemfile.lock
CHANGED
@@ -12,7 +12,7 @@ GIT
|
|
12
12
|
PATH
|
13
13
|
remote: .
|
14
14
|
specs:
|
15
|
-
checkoff (0.
|
15
|
+
checkoff (0.44.0)
|
16
16
|
activesupport
|
17
17
|
asana (> 0.10.0)
|
18
18
|
cache_method
|
@@ -22,7 +22,7 @@ PATH
|
|
22
22
|
GEM
|
23
23
|
remote: https://rubygems.org/
|
24
24
|
specs:
|
25
|
-
activesupport (7.0.5)
|
25
|
+
activesupport (7.0.5.1)
|
26
26
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
27
27
|
i18n (>= 1.6, < 2)
|
28
28
|
minitest (>= 5.1)
|
@@ -8,19 +8,27 @@ module Checkoff
|
|
8
8
|
# Use the provided config from a YAML file, and fall back to env
|
9
9
|
# variable if it's not populated for a key'
|
10
10
|
class EnvFallbackConfigLoader
|
11
|
+
# @param config [Hash<Symbol, Object>]
|
12
|
+
# @param sym [Symbol]
|
13
|
+
# @param yaml_filename [String]
|
11
14
|
def initialize(config, sym, yaml_filename)
|
12
15
|
@config = config
|
13
16
|
@envvar_prefix = sym.upcase
|
14
17
|
@yaml_filename = yaml_filename
|
15
18
|
end
|
16
19
|
|
20
|
+
# @param key [Symbol]
|
21
|
+
# @return [Object]
|
17
22
|
def [](key)
|
18
23
|
config_value = @config[key]
|
19
24
|
return config_value unless config_value.nil?
|
20
25
|
|
26
|
+
# @sg-ignore
|
21
27
|
ENV.fetch(envvar_name(key), nil)
|
22
28
|
end
|
23
29
|
|
30
|
+
# @param key [Symbol]
|
31
|
+
# @return [Object]
|
24
32
|
def fetch(key)
|
25
33
|
out = self[key]
|
26
34
|
return out unless out.nil?
|
@@ -31,6 +39,8 @@ module Checkoff
|
|
31
39
|
|
32
40
|
private
|
33
41
|
|
42
|
+
# @param key [Symbol]
|
43
|
+
# @return [String]
|
34
44
|
def envvar_name(key)
|
35
45
|
"#{@envvar_prefix}__#{key.upcase}"
|
36
46
|
end
|
@@ -39,7 +49,8 @@ module Checkoff
|
|
39
49
|
# Load configuration file
|
40
50
|
class ConfigLoader
|
41
51
|
class << self
|
42
|
-
# @
|
52
|
+
# @sg-ignore
|
53
|
+
# @return [Hash<Symbol, Object>]
|
43
54
|
def load(sym)
|
44
55
|
yaml_result = load_yaml_file(sym)
|
45
56
|
EnvFallbackConfigLoader.new(yaml_result, sym, yaml_filename(sym))
|
@@ -47,13 +58,18 @@ module Checkoff
|
|
47
58
|
|
48
59
|
private
|
49
60
|
|
61
|
+
# @param sym [Symbol]
|
62
|
+
# @return [Hash<[String, Symbol], Object>]
|
50
63
|
def load_yaml_file(sym)
|
51
64
|
filename = yaml_filename(sym)
|
52
65
|
return {} unless File.exist?(filename)
|
53
66
|
|
67
|
+
# @sg-ignore
|
54
68
|
YAML.load_file(filename).with_indifferent_access
|
55
69
|
end
|
56
70
|
|
71
|
+
# @param sym [Symbol]
|
72
|
+
# @return [String]
|
57
73
|
def yaml_filename(sym)
|
58
74
|
file = "#{sym}.yml"
|
59
75
|
File.expand_path("~/.#{file}")
|
@@ -101,7 +101,7 @@ module Checkoff
|
|
101
101
|
def pull_custom_field_by_name(task, custom_field_name)
|
102
102
|
custom_fields = task.custom_fields
|
103
103
|
if custom_fields.nil?
|
104
|
-
raise "custom fields not found on task - did you add '
|
104
|
+
raise "custom fields not found on task - did you add 'custom_fields' in your extra_fields argument?"
|
105
105
|
end
|
106
106
|
|
107
107
|
# @sg-ignore
|
@@ -339,6 +339,30 @@ module Checkoff
|
|
339
339
|
end
|
340
340
|
end
|
341
341
|
|
342
|
+
# :custom_field_greater_than_or_equal_to_n_days_from_now function
|
343
|
+
class CustomFieldGreaterThanOrEqualToNDaysFromNowFunctionEvaluator < FunctionEvaluator
|
344
|
+
def matches?
|
345
|
+
fn?(task_selector, :custom_field_greater_than_or_equal_to_n_days_from_now)
|
346
|
+
end
|
347
|
+
|
348
|
+
def evaluate_arg?(_index)
|
349
|
+
false
|
350
|
+
end
|
351
|
+
|
352
|
+
# @param task [Asana::Resources::Task]
|
353
|
+
# @param custom_field_name [String]
|
354
|
+
# @param num_days [Integer]
|
355
|
+
# @return [Boolean]
|
356
|
+
def evaluate(task, custom_field_name, num_days)
|
357
|
+
custom_field = pull_custom_field_by_name_or_raise(task, custom_field_name)
|
358
|
+
|
359
|
+
time_str = custom_field.fetch('display_value')
|
360
|
+
time = Time.parse(time_str)
|
361
|
+
n_days_from_now = (Time.now + (num_days * 24 * 60 * 60))
|
362
|
+
time >= n_days_from_now
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
342
366
|
# String literals
|
343
367
|
class StringLiteralEvaluator < FunctionEvaluator
|
344
368
|
def matches?
|
@@ -376,6 +400,7 @@ module Checkoff
|
|
376
400
|
DuePFunctionEvaluator,
|
377
401
|
DueDateSetPFunctionEvaluator,
|
378
402
|
CustomFieldLessThanNDaysFromNowFunctionEvaluator,
|
403
|
+
CustomFieldGreaterThanOrEqualToNDaysFromNowFunctionEvaluator,
|
379
404
|
StringLiteralEvaluator,
|
380
405
|
].freeze
|
381
406
|
|
@@ -27,6 +27,17 @@ module Checkoff
|
|
27
27
|
|
28
28
|
include Asana::Resources::ResponseHelper
|
29
29
|
|
30
|
+
# @!parse
|
31
|
+
# extend CacheMethod::ClassMethods
|
32
|
+
|
33
|
+
# @param config [Hash<Symbol, Object>]
|
34
|
+
# @param workspaces [Checkoff::Workspaces]
|
35
|
+
# @param task_selectors [Checkoff::TaskSelectors]
|
36
|
+
# @param projects [Checkoff::Projects]
|
37
|
+
# @param clients [Checkoff::Clients]
|
38
|
+
# @param client [Asana::Client]
|
39
|
+
# @param search_url_parser [Checkoff::Internal::SearchUrl::Parser]
|
40
|
+
# @param asana_resources_collection_class [Class<Asana::Resources::Collection>]
|
30
41
|
def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
|
31
42
|
workspaces: Checkoff::Workspaces.new(config: config),
|
32
43
|
task_selectors: Checkoff::TaskSelectors.new(config: config),
|
@@ -49,6 +60,7 @@ module Checkoff
|
|
49
60
|
# @return [Array<Asana::Resources::Task>]
|
50
61
|
def task_search(workspace_name, url, extra_fields: [])
|
51
62
|
workspace = workspaces.workspace_or_raise(workspace_name)
|
63
|
+
# @sg-ignore
|
52
64
|
api_params, task_selector = @search_url_parser.convert_params(url)
|
53
65
|
path = "/workspaces/#{workspace.gid}/tasks/search"
|
54
66
|
options = calculate_api_options(extra_fields)
|
@@ -76,8 +88,13 @@ module Checkoff
|
|
76
88
|
# bundle exec ./task_searches.rb
|
77
89
|
# :nocov:
|
78
90
|
class << self
|
91
|
+
# @return [void]
|
79
92
|
def run
|
93
|
+
# @sg-ignore
|
94
|
+
# @type [String]
|
80
95
|
workspace_name = ARGV[0] || raise('Please pass workspace name as first argument')
|
96
|
+
# @sg-ignore
|
97
|
+
# @type [String]
|
81
98
|
url = ARGV[1] || raise('Please pass task search URL as second argument')
|
82
99
|
task_searches = Checkoff::TaskSearches.new
|
83
100
|
task_search = task_searches.task_search(workspace_name, url)
|
@@ -86,7 +103,14 @@ module Checkoff
|
|
86
103
|
end
|
87
104
|
# :nocov:
|
88
105
|
|
89
|
-
|
106
|
+
# @return [Checkoff::TaskSelectors]
|
107
|
+
attr_reader :task_selectors
|
108
|
+
# @return [Checkoff::Projects]
|
109
|
+
attr_reader :projects
|
110
|
+
# @return [Checkoff::Workspaces]
|
111
|
+
attr_reader :workspaces
|
112
|
+
# @return [Asana::Client]
|
113
|
+
attr_reader :client
|
90
114
|
end
|
91
115
|
end
|
92
116
|
|
data/lib/checkoff/tasks.rb
CHANGED
@@ -4,25 +4,25 @@
|
|
4
4
|
|
5
5
|
require_relative 'sections'
|
6
6
|
require_relative 'workspaces'
|
7
|
+
require_relative 'internal/config_loader'
|
7
8
|
require 'asana'
|
8
9
|
|
9
10
|
module Checkoff
|
10
11
|
# Pull tasks from Asana
|
11
12
|
class Tasks
|
12
13
|
MINUTE = 60
|
13
|
-
# @sg-ignore
|
14
14
|
HOUR = MINUTE * 60
|
15
15
|
DAY = 24 * HOUR
|
16
16
|
REALLY_LONG_CACHE_TIME = HOUR
|
17
|
-
# @sg-ignore
|
18
17
|
LONG_CACHE_TIME = MINUTE * 15
|
19
|
-
# @sg-ignore
|
20
18
|
SHORT_CACHE_TIME = MINUTE * 5
|
21
19
|
|
20
|
+
# @param config [Hash<Symbol, Object>]
|
22
21
|
# @param client [Asana::Client]
|
23
22
|
# @param workspaces [Checkoff::Workspaces]
|
24
|
-
# @param time_class [Class<Time>]
|
25
23
|
# @param sections [Checkoff::Sections]
|
24
|
+
# @param time_class [Class<Time>]
|
25
|
+
# @param asana_task [Class<Asana::Resources::Task>]
|
26
26
|
def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana),
|
27
27
|
client: Checkoff::Clients.new(config: config).client,
|
28
28
|
workspaces: Checkoff::Workspaces.new(config: config,
|
@@ -39,8 +39,10 @@ module Checkoff
|
|
39
39
|
@workspaces = workspaces
|
40
40
|
end
|
41
41
|
|
42
|
-
|
43
|
-
|
42
|
+
# @param task [Asana::Resources::Task]
|
43
|
+
# @param ignore_dependencies [Boolean]
|
44
|
+
def task_ready?(task, ignore_dependencies: false)
|
45
|
+
return false if !ignore_dependencies && incomplete_dependencies?(task)
|
44
46
|
|
45
47
|
due = due_time(task)
|
46
48
|
|
@@ -50,11 +52,19 @@ module Checkoff
|
|
50
52
|
end
|
51
53
|
|
52
54
|
# Pull a specific task by name
|
55
|
+
# @param workspace_name [String]
|
56
|
+
# @param project_name [String, Symbol]
|
57
|
+
# @param section_name [String, nil, Symbol]
|
58
|
+
# @param task_name [String]
|
59
|
+
# @param only_uncompleted [Boolean]
|
60
|
+
# @param extra_fields [Array<String>]
|
61
|
+
# @sg-ignore
|
53
62
|
# @return [Asana::Resources::Task, nil]
|
54
63
|
def task(workspace_name, project_name, task_name,
|
55
64
|
section_name: :unspecified,
|
56
65
|
only_uncompleted: true,
|
57
66
|
extra_fields: [])
|
67
|
+
# @sg-ignore
|
58
68
|
tasks = tasks_from_section(workspace_name,
|
59
69
|
project_name,
|
60
70
|
section_name: section_name,
|
@@ -74,6 +84,7 @@ module Checkoff
|
|
74
84
|
client.tasks.find_by_id(task_gid, options: options)
|
75
85
|
end
|
76
86
|
|
87
|
+
# @param name [String]
|
77
88
|
# @param workspace_gid [String]
|
78
89
|
# @param assignee_gid [String]
|
79
90
|
# @return [Asana::Resources::Task]
|
@@ -85,13 +96,19 @@ module Checkoff
|
|
85
96
|
workspace: workspace_gid, name: name)
|
86
97
|
end
|
87
98
|
|
88
|
-
#
|
99
|
+
# @param task [Asana::Resources::Task]
|
100
|
+
# @return [String] end-user URL to the task in question
|
89
101
|
def url_of_task(task)
|
90
102
|
"https://app.asana.com/0/0/#{task.gid}/f"
|
91
103
|
end
|
92
104
|
|
93
105
|
private
|
94
106
|
|
107
|
+
# @param workspace_name [String]
|
108
|
+
# @param project_name [String, Symbol]
|
109
|
+
# @param section_name [String, nil, :unspecified]
|
110
|
+
# @param only_uncompleted [Boolean]
|
111
|
+
# @param extra_fields [Array<String>]
|
95
112
|
# @return [Array<Asana::Resources::Task>]
|
96
113
|
def tasks_from_section(workspace_name, project_name,
|
97
114
|
section_name:,
|
@@ -109,6 +126,7 @@ module Checkoff
|
|
109
126
|
end
|
110
127
|
end
|
111
128
|
|
129
|
+
# @return [Asana::Client]
|
112
130
|
attr_reader :client
|
113
131
|
|
114
132
|
# @return [Checkoff::Projects]
|
@@ -131,6 +149,7 @@ module Checkoff
|
|
131
149
|
nil
|
132
150
|
end
|
133
151
|
|
152
|
+
# @param task [Asana::Resources::Task]
|
134
153
|
def incomplete_dependencies?(task)
|
135
154
|
# Avoid a redundant fetch. Unfortunately, Ruby SDK allows
|
136
155
|
# dependencies to be fetched along with other attributes--but
|
data/lib/checkoff/version.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.44.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-06-
|
11
|
+
date: 2023-06-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|