checkoff 0.42.0 → 0.44.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|