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