checkoff 0.14.0 → 0.15.2
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 +4 -4
- data/Makefile +7 -3
- data/README.md +32 -10
- data/coverage/.last_run.json +2 -2
- data/fix.sh +31 -2
- data/lib/checkoff/cli.rb +1 -1
- data/lib/checkoff/clients.rb +55 -0
- data/lib/checkoff/create-entity.sh +85 -0
- data/lib/checkoff/custom_fields.rb +68 -0
- data/lib/checkoff/projects.rb +10 -11
- data/lib/checkoff/sections.rb +9 -3
- data/lib/checkoff/subtasks.rb +2 -0
- data/lib/checkoff/tags.rb +68 -0
- data/lib/checkoff/tasks.rb +32 -3
- data/lib/checkoff/version.rb +1 -1
- data/lib/checkoff/workspaces.rb +28 -13
- data/lib/checkoff.rb +2 -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: 10f4779f3bcd067c2bbb80ed9ddea16121a5d26a2a9ee9f95ea6ca43c1931c9c
|
4
|
+
data.tar.gz: 3ab265436a93926f0903aa97f792034f773b81c2e28fea7e4f5a49e4746aa62c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6870f522f4b5b3a0dedd97126f784e38e4dc86bb0de81c466ec353db66d4be4cf06c3a0f24d9f33790711322faf8440f4d783d3d4759083191579ce2970e8e47
|
7
|
+
data.tar.gz: 382a46c341315009e55ab0f3606c51bf8eaa2dcaf9c2a475c831a9a216cdb7fec4c5d2c1d7bbff95408ce17f512b2481dcc56417749ad246d057a05b2041cab1
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
GIT
|
2
2
|
remote: https://github.com/apiology/ruby-asana
|
3
|
-
revision:
|
3
|
+
revision: ab9393136d9ca59b75e42a661fdddc1c65c8f7c3
|
4
4
|
branch: checkoff_fixes
|
5
5
|
specs:
|
6
|
-
asana (0.10.
|
6
|
+
asana (0.10.6)
|
7
7
|
faraday (~> 1.0)
|
8
8
|
faraday_middleware (~> 1.0)
|
9
9
|
faraday_middleware-multi_json (~> 0.0)
|
@@ -12,7 +12,7 @@ GIT
|
|
12
12
|
PATH
|
13
13
|
remote: .
|
14
14
|
specs:
|
15
|
-
checkoff (0.
|
15
|
+
checkoff (0.15.2)
|
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 (6.1.4)
|
25
|
+
activesupport (6.1.4.1)
|
26
26
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
27
27
|
i18n (>= 1.6, < 2)
|
28
28
|
minitest (>= 5.1)
|
data/Makefile
CHANGED
@@ -23,9 +23,11 @@ requirements_dev.txt.installed: requirements_dev.txt
|
|
23
23
|
|
24
24
|
pip_install: requirements_dev.txt.installed ## Install Python dependencies
|
25
25
|
|
26
|
-
|
27
|
-
|
26
|
+
# bundle install doesn't get run here so that we can catch it below in
|
27
|
+
# fresh-checkout and fresh-rbenv cases
|
28
|
+
Gemfile.lock: Gemfile
|
28
29
|
|
30
|
+
# Ensure any Gemfile.lock changes ensure a bundle is installed.
|
29
31
|
Gemfile.lock.installed: Gemfile.lock
|
30
32
|
bundle install
|
31
33
|
touch Gemfile.lock.installed
|
@@ -37,9 +39,11 @@ clear_metrics: ## remove or reset result artifacts created by tests and quality
|
|
37
39
|
|
38
40
|
clean: clear_metrics ## remove all built artifacts
|
39
41
|
|
40
|
-
|
42
|
+
overcommit: ## run precommit quality checks
|
41
43
|
bundle exec overcommit --run
|
42
44
|
|
45
|
+
quality: overcommit ## run precommit quality checks
|
46
|
+
|
43
47
|
test: ## Run lower-level tests
|
44
48
|
@bundle exec rake test
|
45
49
|
|
data/README.md
CHANGED
@@ -4,12 +4,28 @@ Command-line and gem client for Asana (unofficial)
|
|
4
4
|
|
5
5
|
## Using
|
6
6
|
|
7
|
-
```
|
7
|
+
```sh
|
8
8
|
$ checkoff --help
|
9
|
-
|
10
|
-
|
9
|
+
NAME
|
10
|
+
checkoff - Command-line client for Asana (unofficial)
|
11
|
+
|
12
|
+
|
13
|
+
SYNOPSIS
|
14
|
+
checkoff [global options] command [command options] [arguments...]
|
15
|
+
|
11
16
|
|
12
|
-
|
17
|
+
|
18
|
+
GLOBAL OPTIONS
|
19
|
+
--help - Show this message
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
COMMANDS
|
24
|
+
help - Shows a list of commands or help for one command
|
25
|
+
mv - Move tasks from one section to another within a project
|
26
|
+
quickadd - Add a short task to Asana
|
27
|
+
view - Output representation of Asana tasks
|
28
|
+
$
|
13
29
|
```
|
14
30
|
|
15
31
|
Let's say we have a project like this:
|
@@ -18,7 +34,7 @@ Let's say we have a project like this:
|
|
18
34
|
|
19
35
|
Checkoff outputs things in JSON. 'jq' is a great tool to use in combination:
|
20
36
|
|
21
|
-
```
|
37
|
+
```sh
|
22
38
|
$ checkoff view 'Personal Projects' 'Create demo'
|
23
39
|
{"":[{"name":"This is a task that doesn't belong to any sections."}],"Write it:":[{"name":"Write something"}],"Publish to github:":[{"name":"git push!"}],"Make sure it looks OK!:":[{"name":"Looks it up in your browser..."}]}
|
24
40
|
$
|
@@ -52,7 +68,7 @@ $ checkoff view 'Personal Projects' 'Create demo' | jq
|
|
52
68
|
|
53
69
|
You can drill down into a section on the command line:
|
54
70
|
|
55
|
-
```
|
71
|
+
```sh
|
56
72
|
$ checkoff view 'Personal Projects' 'Create demo' 'Publish to github:'
|
57
73
|
[{"name":"git push!"}]
|
58
74
|
```
|
@@ -71,12 +87,17 @@ $ checkoff view 'Personal Projects' 'Create demo' | jq 'to_entries | map(.value)
|
|
71
87
|
|
72
88
|
And even gather counts for project metrics:
|
73
89
|
|
74
|
-
```
|
90
|
+
```sh
|
75
91
|
$ checkoff view 'Personal Projects' 'Create demo' | jq 'to_entries | map(.value) | flatten | map(.name) | length'
|
76
92
|
4
|
77
93
|
$
|
78
94
|
```
|
79
95
|
|
96
|
+
## Naming things
|
97
|
+
|
98
|
+
Since checkoff looks up things by their name, if you have two things
|
99
|
+
with the same name, you're probably going to have a bad time.
|
100
|
+
|
80
101
|
## Caching
|
81
102
|
|
82
103
|
Note that I don't know of a way through the Asana API to target
|
@@ -94,7 +115,7 @@ clear the cache.
|
|
94
115
|
|
95
116
|
This will work under OS X:
|
96
117
|
|
97
|
-
```
|
118
|
+
```sh
|
98
119
|
brew install memcached
|
99
120
|
ln -sfv /usr/local/opt/memcached/*.plist ~/Library/LaunchAgents
|
100
121
|
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.memcached.plist
|
@@ -103,7 +124,8 @@ gem install checkoff
|
|
103
124
|
|
104
125
|
## Configuring
|
105
126
|
|
106
|
-
You'll need to create a file called `.asana.yml` in your home
|
127
|
+
You'll need to create a file called `.asana.yml` in your home
|
128
|
+
directory, that looks like this:
|
107
129
|
|
108
130
|
```yaml
|
109
131
|
---
|
@@ -129,7 +151,7 @@ Alternately you can set environment variables to match - e.g., `ASANA__PERSONAL_
|
|
129
151
|
|
130
152
|
## Developing
|
131
153
|
|
132
|
-
```
|
154
|
+
```sh
|
133
155
|
bundle install
|
134
156
|
bundle exec exe/checkoff --help
|
135
157
|
```
|
data/coverage/.last_run.json
CHANGED
data/fix.sh
CHANGED
@@ -127,10 +127,33 @@ ensure_bundle() {
|
|
127
127
|
bundler_version=$(bundle --version | cut -d ' ' -f3)
|
128
128
|
bundler_version_major=$(cut -d. -f1 <<< "${bundler_version}")
|
129
129
|
bundler_version_minor=$(cut -d. -f2 <<< "${bundler_version}")
|
130
|
+
bundler_version_patch=$(cut -d. -f3 <<< "${bundler_version}")
|
130
131
|
# Version 2.1 of bundler seems to have some issues with nokogiri:
|
131
132
|
#
|
132
133
|
# https://app.asana.com/0/1107901397356088/1199504270687298
|
133
|
-
|
134
|
+
|
135
|
+
# Version 2.2.22 of bundler comes with a fix to ensure the 'bundle
|
136
|
+
# update --conservative' flag works as expected - important when
|
137
|
+
# doing a 'bundle update' on a about-to-be-published gem after
|
138
|
+
# bumping a gem version.
|
139
|
+
need_better_bundler=false
|
140
|
+
if [ "${bundler_version_major}" -lt 2 ]
|
141
|
+
then
|
142
|
+
need_better_bundler=true
|
143
|
+
elif [ "${bundler_version_major}" -eq 2 ]
|
144
|
+
then
|
145
|
+
if [ "${bundler_version_minor}" -lt 2 ]
|
146
|
+
then
|
147
|
+
need_better_bundler=true
|
148
|
+
elif [ "${bundler_version_minor}" -eq 2 ]
|
149
|
+
then
|
150
|
+
if [ "${bundler_version_patch}" -lt 22 ]
|
151
|
+
then
|
152
|
+
need_better_bundler=true
|
153
|
+
fi
|
154
|
+
fi
|
155
|
+
fi
|
156
|
+
if [ "${need_better_bundler}" = true ]
|
134
157
|
then
|
135
158
|
gem install --no-document bundler
|
136
159
|
fi
|
@@ -143,7 +166,13 @@ ensure_bundle() {
|
|
143
166
|
# re-resolve and consider the new platform when picking gems, all
|
144
167
|
# without needing to have a machine that matches PLATFORM handy to
|
145
168
|
# install those platform-specific gems on.'
|
146
|
-
|
169
|
+
#
|
170
|
+
# This affects nokogiri, which will try to reinstall itself in
|
171
|
+
# Docker builds where it's already installed if this is not run.
|
172
|
+
for platform in x86_64-darwin-20 x86_64-linux
|
173
|
+
do
|
174
|
+
grep "${platform:?}" Gemfile.lock >/dev/null 2>&1 || bundle lock --add-platform "${platform:?}"
|
175
|
+
done
|
147
176
|
}
|
148
177
|
|
149
178
|
set_ruby_local_version() {
|
data/lib/checkoff/cli.rb
CHANGED
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require 'forwardable'
|
6
|
+
require_relative 'config_loader'
|
7
|
+
require 'asana'
|
8
|
+
|
9
|
+
# https://developers.asana.com/docs/clients
|
10
|
+
|
11
|
+
module Checkoff
|
12
|
+
# Pulls a configured Asana client object which can be used to access the API
|
13
|
+
class Clients
|
14
|
+
MINUTE = 60
|
15
|
+
HOUR = MINUTE * 60
|
16
|
+
DAY = 24 * HOUR
|
17
|
+
REALLY_LONG_CACHE_TIME = HOUR * 1
|
18
|
+
LONG_CACHE_TIME = MINUTE * 15
|
19
|
+
SHORT_CACHE_TIME = MINUTE
|
20
|
+
|
21
|
+
def initialize(config: Checkoff::ConfigLoader.load(:asana),
|
22
|
+
asana_client_class: Asana::Client)
|
23
|
+
@config = config
|
24
|
+
@asana_client_class = asana_client_class
|
25
|
+
end
|
26
|
+
|
27
|
+
def client
|
28
|
+
@client ||= @asana_client_class.new do |c|
|
29
|
+
c.authentication :access_token, @config.fetch(:personal_access_token)
|
30
|
+
c.default_headers 'asana-enable' => 'string_ids,new_sections'
|
31
|
+
c.default_headers 'asana-disable' => 'new_user_task_lists'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_reader :workspaces
|
38
|
+
|
39
|
+
# bundle exec ./clients.rb
|
40
|
+
# :nocov:
|
41
|
+
class << self
|
42
|
+
def run
|
43
|
+
clients = Checkoff::Clients.new
|
44
|
+
client = clients.client
|
45
|
+
puts "Results: #{client}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
# :nocov:
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# :nocov:
|
53
|
+
abs_program_name = File.expand_path($PROGRAM_NAME)
|
54
|
+
Checkoff::Clients.run if abs_program_name == __FILE__
|
55
|
+
# :nocov:
|
@@ -0,0 +1,85 @@
|
|
1
|
+
#!/bin/bash -eu
|
2
|
+
|
3
|
+
set -o pipefail
|
4
|
+
|
5
|
+
underscored_plural_name="${1:?underscored plural name of entities minus .rb}"
|
6
|
+
# Sorry, shellcheck, I can't express 'end of line' in a simple variable search and replace
|
7
|
+
# shellcheck disable=SC2001
|
8
|
+
underscored_singular_name=$(sed -e 's/s$//g' <<< "${underscored_plural_name}")
|
9
|
+
kabob_case_plural_name=${underscored_plural_name/_/-}
|
10
|
+
class_name="${2:?class name without Checkoff:: prefix}"
|
11
|
+
|
12
|
+
cat > "${underscored_plural_name}.rb" << EOF
|
13
|
+
#!/usr/bin/env ruby
|
14
|
+
|
15
|
+
# frozen_string_literal: true
|
16
|
+
|
17
|
+
require 'forwardable'
|
18
|
+
require 'cache_method'
|
19
|
+
require_relative 'config_loader'
|
20
|
+
require_relative 'workspaces'
|
21
|
+
require_relative 'clients'
|
22
|
+
|
23
|
+
# https://developers.asana.com/docs/${kabob_case_plural_name}
|
24
|
+
|
25
|
+
module Checkoff
|
26
|
+
class ${class_name}
|
27
|
+
MINUTE = 60
|
28
|
+
HOUR = MINUTE * 60
|
29
|
+
DAY = 24 * HOUR
|
30
|
+
REALLY_LONG_CACHE_TIME = HOUR * 1
|
31
|
+
LONG_CACHE_TIME = MINUTE * 15
|
32
|
+
SHORT_CACHE_TIME = MINUTE
|
33
|
+
|
34
|
+
def initialize(config: Checkoff::ConfigLoader.load(:asana),
|
35
|
+
workspaces: Checkoff::Workspaces.new(config: config),
|
36
|
+
clients: Checkoff::Clients.new(config: config),
|
37
|
+
client: clients.client)
|
38
|
+
@workspaces = workspaces
|
39
|
+
@client = client
|
40
|
+
end
|
41
|
+
|
42
|
+
def ${underscored_singular_name}_or_raise(workspace_name, ${underscored_singular_name}_name)
|
43
|
+
${underscored_singular_name} = ${underscored_singular_name}(workspace_name, ${underscored_singular_name}_name)
|
44
|
+
raise "Could not find ${underscored_singular_name} #{${underscored_singular_name}_name} under workspace #{workspace_name}." if ${underscored_singular_name}.nil?
|
45
|
+
|
46
|
+
${underscored_singular_name}
|
47
|
+
end
|
48
|
+
cache_method :${underscored_singular_name}_or_raise, LONG_CACHE_TIME
|
49
|
+
|
50
|
+
def ${underscored_singular_name}(workspace_name, ${underscored_singular_name}_name)
|
51
|
+
workspace = workspaces.workspace_or_raise(workspace_name)
|
52
|
+
${underscored_plural_name} = client.${underscored_plural_name}.get_${underscored_plural_name}_for_workspace(workspace_gid: workspace.gid)
|
53
|
+
${underscored_plural_name}.find { |${underscored_singular_name}| ${underscored_singular_name}.name == ${underscored_singular_name}_name }
|
54
|
+
end
|
55
|
+
cache_method :${underscored_singular_name}, LONG_CACHE_TIME
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
attr_reader :workspaces, :client
|
60
|
+
|
61
|
+
# bundle exec ./${underscored_plural_name}.rb
|
62
|
+
# :nocov:
|
63
|
+
class << self
|
64
|
+
def run
|
65
|
+
workspace_name = ARGV[0] || raise('Please pass workspace name as first argument')
|
66
|
+
${underscored_singular_name}_name = ARGV[1] || raise('Please pass ${underscored_singular_name} name as second argument')
|
67
|
+
${underscored_plural_name} = Checkoff::${class_name}.new
|
68
|
+
${underscored_singular_name} = ${underscored_plural_name}.${underscored_singular_name}_or_raise(workspace_name, ${underscored_singular_name}_name)
|
69
|
+
puts "Results: #{${underscored_singular_name}}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
# :nocov:
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# :nocov:
|
77
|
+
abs_program_name = File.expand_path(\$PROGRAM_NAME)
|
78
|
+
Checkoff::${class_name}.run if abs_program_name == __FILE__
|
79
|
+
# :nocov:
|
80
|
+
EOF
|
81
|
+
|
82
|
+
chmod +x "${underscored_plural_name}.rb"
|
83
|
+
git add "${underscored_plural_name}.rb"
|
84
|
+
|
85
|
+
echo "You can start by running 'bundle exec ./${underscored_plural_name}.rb' and tweaking from there"
|
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require 'forwardable'
|
6
|
+
require 'cache_method'
|
7
|
+
require_relative 'config_loader'
|
8
|
+
require_relative 'workspaces'
|
9
|
+
require_relative 'clients'
|
10
|
+
|
11
|
+
# https://developers.asana.com/docs/custom-fields
|
12
|
+
|
13
|
+
module Checkoff
|
14
|
+
# Work with custom fields in Asana
|
15
|
+
class CustomFields
|
16
|
+
MINUTE = 60
|
17
|
+
HOUR = MINUTE * 60
|
18
|
+
DAY = 24 * HOUR
|
19
|
+
REALLY_LONG_CACHE_TIME = HOUR * 1
|
20
|
+
LONG_CACHE_TIME = MINUTE * 15
|
21
|
+
SHORT_CACHE_TIME = MINUTE
|
22
|
+
|
23
|
+
def initialize(config: Checkoff::ConfigLoader.load(:asana),
|
24
|
+
workspaces: Checkoff::Workspaces.new(config: config),
|
25
|
+
clients: Checkoff::Clients.new(config: config),
|
26
|
+
client: clients.client)
|
27
|
+
@workspaces = workspaces
|
28
|
+
@client = client
|
29
|
+
end
|
30
|
+
|
31
|
+
def custom_field_or_raise(workspace_name, custom_field_name)
|
32
|
+
custom_field = custom_field(workspace_name, custom_field_name)
|
33
|
+
raise "Could not find custom_field #{custom_field_name} under workspace #{workspace_name}." if custom_field.nil?
|
34
|
+
|
35
|
+
custom_field
|
36
|
+
end
|
37
|
+
cache_method :custom_field_or_raise, LONG_CACHE_TIME
|
38
|
+
|
39
|
+
def custom_field(workspace_name, custom_field_name)
|
40
|
+
workspace = workspaces.workspace_or_raise(workspace_name)
|
41
|
+
custom_fields = client.custom_fields.get_custom_fields_for_workspace(workspace_gid: workspace.gid)
|
42
|
+
custom_fields.find { |custom_field| custom_field.name == custom_field_name }
|
43
|
+
end
|
44
|
+
cache_method :custom_field, LONG_CACHE_TIME
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
attr_reader :workspaces, :client
|
49
|
+
|
50
|
+
# bundle exec ./custom_fields.rb
|
51
|
+
# :nocov:
|
52
|
+
class << self
|
53
|
+
def run
|
54
|
+
workspace_name = ARGV[0] || raise('Please pass workspace name as first argument')
|
55
|
+
custom_field_name = ARGV[1] || raise('Please pass custom_field name as second argument')
|
56
|
+
custom_fields = Checkoff::CustomFields.new
|
57
|
+
custom_field = custom_fields.custom_field_or_raise(workspace_name, custom_field_name)
|
58
|
+
puts "Results: #{custom_field}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
# :nocov:
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# :nocov:
|
66
|
+
abs_program_name = File.expand_path($PROGRAM_NAME)
|
67
|
+
Checkoff::CustomFields.run if abs_program_name == __FILE__
|
68
|
+
# :nocov:
|
data/lib/checkoff/projects.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'config_loader'
|
4
|
-
|
4
|
+
require_relative 'workspaces'
|
5
|
+
require_relative 'clients'
|
5
6
|
require 'cache_method'
|
6
7
|
require 'asana'
|
7
8
|
|
@@ -23,16 +24,12 @@ module Checkoff
|
|
23
24
|
SHORT_CACHE_TIME = MINUTE * 5
|
24
25
|
|
25
26
|
def initialize(config: Checkoff::ConfigLoader.load(:asana),
|
26
|
-
|
27
|
-
|
27
|
+
workspaces: Checkoff::Workspaces.new(config: config),
|
28
|
+
clients: Checkoff::Clients.new(config: config),
|
29
|
+
client: clients.client)
|
28
30
|
@config = config
|
29
|
-
@asana_client = asana_client
|
30
31
|
@workspaces = workspaces
|
31
|
-
|
32
|
-
|
33
|
-
# Returns Asana Ruby API Client object
|
34
|
-
def client
|
35
|
-
@workspaces.client
|
32
|
+
@client = client
|
36
33
|
end
|
37
34
|
|
38
35
|
# Default options used in Asana API to pull taskso
|
@@ -84,20 +81,22 @@ module Checkoff
|
|
84
81
|
|
85
82
|
private
|
86
83
|
|
84
|
+
attr_reader :client
|
85
|
+
|
87
86
|
def projects
|
88
87
|
client.projects
|
89
88
|
end
|
90
89
|
cache_method :projects, LONG_CACHE_TIME
|
91
90
|
|
92
91
|
def projects_by_workspace_name(workspace_name)
|
93
|
-
workspace = @workspaces.
|
92
|
+
workspace = @workspaces.workspace_or_raise(workspace_name)
|
94
93
|
raise "Could not find workspace named #{workspace_name}" unless workspace
|
95
94
|
|
96
95
|
projects.find_by_workspace(workspace: workspace.gid)
|
97
96
|
end
|
98
97
|
|
99
98
|
def my_tasks(workspace_name)
|
100
|
-
workspace = @workspaces.
|
99
|
+
workspace = @workspaces.workspace_or_raise(workspace_name)
|
101
100
|
result = client.user_task_lists.get_user_task_list_for_user(user_gid: 'me',
|
102
101
|
workspace: workspace.gid)
|
103
102
|
gid = result.gid
|
data/lib/checkoff/sections.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'forwardable'
|
4
|
+
require_relative 'projects'
|
5
|
+
require_relative 'workspaces'
|
6
|
+
require_relative 'clients'
|
4
7
|
|
5
8
|
module Checkoff
|
6
9
|
# Query different sections of Asana projects
|
@@ -16,10 +19,13 @@ module Checkoff
|
|
16
19
|
def initialize(config: Checkoff::ConfigLoader.load(:asana),
|
17
20
|
projects: Checkoff::Projects.new(config: config),
|
18
21
|
workspaces: Checkoff::Workspaces.new(config: config),
|
22
|
+
clients: Checkoff::Clients.new(config: config),
|
23
|
+
client: clients.client,
|
19
24
|
time: Time)
|
20
25
|
@projects = projects
|
21
26
|
@workspaces = workspaces
|
22
27
|
@time = time
|
28
|
+
@client = client
|
23
29
|
end
|
24
30
|
|
25
31
|
# Returns a list of Asana API section objects for a given project
|
@@ -72,6 +78,8 @@ module Checkoff
|
|
72
78
|
|
73
79
|
private
|
74
80
|
|
81
|
+
attr_reader :client
|
82
|
+
|
75
83
|
# Given a project object, pull all tasks, then provide a Hash of
|
76
84
|
# tasks with section name -> task list of the uncompleted tasks
|
77
85
|
def tasks_by_section_for_project(project)
|
@@ -80,8 +88,6 @@ module Checkoff
|
|
80
88
|
by_section(active_tasks, project.gid)
|
81
89
|
end
|
82
90
|
|
83
|
-
def_delegators :@projects, :client
|
84
|
-
|
85
91
|
# Given a list of tasks, pull a Hash of tasks with section name -> task list
|
86
92
|
def by_section(tasks, project_gid)
|
87
93
|
by_section = {}
|
@@ -97,7 +103,7 @@ module Checkoff
|
|
97
103
|
raise "Could not find task in project_gid #{project_gid}: #{task}" if membership.nil?
|
98
104
|
|
99
105
|
current_section = membership['section']['name']
|
100
|
-
current_section = nil if
|
106
|
+
current_section = nil if ['(no section)', 'Untitled section'].include?(current_section)
|
101
107
|
by_section[current_section] ||= []
|
102
108
|
by_section[current_section] << task
|
103
109
|
end
|
data/lib/checkoff/subtasks.rb
CHANGED
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
require 'forwardable'
|
6
|
+
require 'cache_method'
|
7
|
+
require_relative 'config_loader'
|
8
|
+
require_relative 'workspaces'
|
9
|
+
require_relative 'clients'
|
10
|
+
|
11
|
+
# https://developers.asana.com/docs/tags
|
12
|
+
|
13
|
+
module Checkoff
|
14
|
+
# Work with tags in Asana
|
15
|
+
class Tags
|
16
|
+
MINUTE = 60
|
17
|
+
HOUR = MINUTE * 60
|
18
|
+
DAY = 24 * HOUR
|
19
|
+
REALLY_LONG_CACHE_TIME = HOUR * 1
|
20
|
+
LONG_CACHE_TIME = MINUTE * 15
|
21
|
+
SHORT_CACHE_TIME = MINUTE
|
22
|
+
|
23
|
+
def initialize(config: Checkoff::ConfigLoader.load(:asana),
|
24
|
+
workspaces: Checkoff::Workspaces.new(config: config),
|
25
|
+
clients: Checkoff::Clients.new(config: config),
|
26
|
+
client: clients.client)
|
27
|
+
@workspaces = workspaces
|
28
|
+
@client = client
|
29
|
+
end
|
30
|
+
|
31
|
+
def tag_or_raise(workspace_name, tag_name)
|
32
|
+
tag = tag(workspace_name, tag_name)
|
33
|
+
raise "Could not find tag #{tag_name} under workspace #{workspace_name}." if tag.nil?
|
34
|
+
|
35
|
+
tag
|
36
|
+
end
|
37
|
+
cache_method :tag_or_raise, LONG_CACHE_TIME
|
38
|
+
|
39
|
+
def tag(workspace_name, tag_name)
|
40
|
+
workspace = workspaces.workspace_or_raise(workspace_name)
|
41
|
+
tags = client.tags.get_tags_for_workspace(workspace_gid: workspace.gid)
|
42
|
+
tags.find { |tag| tag.name == tag_name }
|
43
|
+
end
|
44
|
+
cache_method :tag, LONG_CACHE_TIME
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
attr_reader :workspaces, :client
|
49
|
+
|
50
|
+
# bundle exec ./tags.rb
|
51
|
+
# :nocov:
|
52
|
+
class << self
|
53
|
+
def run
|
54
|
+
workspace_name = ARGV[0] || raise('Please pass workspace name as first argument')
|
55
|
+
tag_name = ARGV[1] || raise('Please pass tag name as second argument')
|
56
|
+
tags = Checkoff::Tags.new
|
57
|
+
tag = tags.tag_or_raise(workspace_name, tag_name)
|
58
|
+
puts "Results: #{tag}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
# :nocov:
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# :nocov:
|
66
|
+
abs_program_name = File.expand_path($PROGRAM_NAME)
|
67
|
+
Checkoff::Tags.run if abs_program_name == __FILE__
|
68
|
+
# :nocov:
|
data/lib/checkoff/tasks.rb
CHANGED
@@ -16,10 +16,25 @@ module Checkoff
|
|
16
16
|
|
17
17
|
def initialize(config: Checkoff::ConfigLoader.load(:asana),
|
18
18
|
sections: Checkoff::Sections.new(config: config),
|
19
|
+
clients: Checkoff::Clients.new(config: config),
|
20
|
+
client: clients.client,
|
21
|
+
time_class: Time,
|
19
22
|
asana_task: Asana::Resources::Task)
|
20
23
|
@config = config
|
21
24
|
@sections = sections
|
25
|
+
@time_class = time_class
|
22
26
|
@asana_task = asana_task
|
27
|
+
@client = client
|
28
|
+
end
|
29
|
+
|
30
|
+
def task_ready?(task)
|
31
|
+
return false if incomplete_dependencies?(task)
|
32
|
+
|
33
|
+
due = due_time(task)
|
34
|
+
|
35
|
+
return true if due.nil?
|
36
|
+
|
37
|
+
due < @time_class.now
|
23
38
|
end
|
24
39
|
|
25
40
|
# Pull a specific task by name
|
@@ -52,9 +67,7 @@ module Checkoff
|
|
52
67
|
|
53
68
|
private
|
54
69
|
|
55
|
-
|
56
|
-
@sections.client
|
57
|
-
end
|
70
|
+
attr_reader :client
|
58
71
|
|
59
72
|
def projects
|
60
73
|
@projects ||= @sections.projects
|
@@ -63,5 +76,21 @@ module Checkoff
|
|
63
76
|
def default_assignee_gid
|
64
77
|
@config.fetch(:default_assignee_gid)
|
65
78
|
end
|
79
|
+
|
80
|
+
def due_time(task)
|
81
|
+
return @time_class.parse(task.due_at) if task.due_at
|
82
|
+
return @time_class.parse(task.due_on) if task.due_on
|
83
|
+
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
|
87
|
+
def incomplete_dependencies?(task)
|
88
|
+
task.dependencies.any? do |parent_task_info|
|
89
|
+
parent_task_gid = parent_task_info.gid
|
90
|
+
parent_task = @asana_task.find_by_id(client, parent_task_gid,
|
91
|
+
options: { fields: ['completed'] })
|
92
|
+
!parent_task.completed
|
93
|
+
end
|
94
|
+
end
|
66
95
|
end
|
67
96
|
end
|
data/lib/checkoff/version.rb
CHANGED
data/lib/checkoff/workspaces.rb
CHANGED
@@ -1,32 +1,47 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'forwardable'
|
4
|
+
require 'cache_method'
|
5
|
+
require_relative 'config_loader'
|
6
|
+
require_relative 'clients'
|
7
|
+
|
8
|
+
# https://developers.asana.com/docs/workspaces
|
9
|
+
|
3
10
|
module Checkoff
|
4
11
|
# Query different workspaces of Asana projects
|
5
12
|
class Workspaces
|
13
|
+
MINUTE = 60
|
14
|
+
HOUR = MINUTE * 60
|
15
|
+
DAY = 24 * HOUR
|
16
|
+
REALLY_LONG_CACHE_TIME = HOUR * 1
|
17
|
+
LONG_CACHE_TIME = MINUTE * 15
|
18
|
+
SHORT_CACHE_TIME = MINUTE
|
19
|
+
|
6
20
|
def initialize(config: Checkoff::ConfigLoader.load(:asana),
|
7
|
-
|
21
|
+
clients: Checkoff::Clients.new(config: config),
|
22
|
+
client: clients.client)
|
8
23
|
@config = config
|
9
|
-
@
|
10
|
-
end
|
11
|
-
|
12
|
-
# Returns Asana Ruby API Client object
|
13
|
-
def client
|
14
|
-
@client ||= @asana_client.new do |c|
|
15
|
-
c.authentication :access_token, @config.fetch(:personal_access_token)
|
16
|
-
c.default_headers 'asana-enable' => 'string_ids,new_sections'
|
17
|
-
c.default_headers 'asana-disable' => 'new_user_task_lists'
|
18
|
-
end
|
24
|
+
@client = client
|
19
25
|
end
|
20
26
|
|
21
27
|
# Pulls an Asana workspace object
|
22
|
-
def
|
28
|
+
def workspace(workspace_name)
|
23
29
|
client.workspaces.find_all.find do |workspace|
|
24
30
|
workspace.name == workspace_name
|
25
|
-
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def workspace_or_raise(workspace_name)
|
35
|
+
workspace = workspace(workspace_name)
|
36
|
+
raise "Could not find workspace #{workspace_name}" if workspace.nil?
|
37
|
+
|
38
|
+
workspace
|
26
39
|
end
|
27
40
|
|
28
41
|
private
|
29
42
|
|
43
|
+
attr_reader :client
|
44
|
+
|
30
45
|
def default_workspace_gid
|
31
46
|
@config.fetch(:default_workspace_gid)
|
32
47
|
end
|
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.15.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vince Broz
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-09-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -299,10 +299,14 @@ files:
|
|
299
299
|
- fix.sh
|
300
300
|
- lib/checkoff.rb
|
301
301
|
- lib/checkoff/cli.rb
|
302
|
+
- lib/checkoff/clients.rb
|
302
303
|
- lib/checkoff/config_loader.rb
|
304
|
+
- lib/checkoff/create-entity.sh
|
305
|
+
- lib/checkoff/custom_fields.rb
|
303
306
|
- lib/checkoff/projects.rb
|
304
307
|
- lib/checkoff/sections.rb
|
305
308
|
- lib/checkoff/subtasks.rb
|
309
|
+
- lib/checkoff/tags.rb
|
306
310
|
- lib/checkoff/tasks.rb
|
307
311
|
- lib/checkoff/version.rb
|
308
312
|
- lib/checkoff/workspaces.rb
|