checkoff 0.10.0 → 0.12.1
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/.circleci/config.yml +4 -3
- data/.envrc +1 -1
- data/.gitignore +1 -0
- data/Gemfile.lock +24 -11
- data/Makefile +7 -3
- data/README.md +1 -1
- data/checkoff.gemspec +6 -1
- data/coverage/.last_run.json +2 -2
- data/exe/checkoff +1 -2
- data/fix.sh +42 -7
- data/lib/checkoff/cli.rb +98 -85
- data/lib/checkoff/config_loader.rb +22 -16
- data/lib/checkoff/projects.rb +35 -30
- data/lib/checkoff/sections.rb +59 -121
- data/lib/checkoff/subtasks.rb +14 -11
- data/lib/checkoff/tasks.rb +22 -16
- data/lib/checkoff/version.rb +2 -1
- data/lib/checkoff/workspaces.rb +8 -4
- data/rakelib/{ci.rake → citest.rake} +1 -1
- data/rakelib/clear_metrics.rake +10 -1
- metadata +34 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c9229d2827676a7888a6bb60059d06729ec2ba27912e029ac452827c6c2ce4e2
|
|
4
|
+
data.tar.gz: f15c045ba7aa21087c5c3b179866c17632757ef7a1923c7c9a1c4185d4828043
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d711b94cb4ae8b0933ed9c0e20104123e7707b898616335ddcb73e0f30e09a2bd427889f554a6422b7071ebbf304c58832446783288f1b4d1171a1b31a61dd51
|
|
7
|
+
data.tar.gz: cc6e96a3371f0870777228394ca085db4a7786badf3173b49a864c79866af3ad4ca71a3482920b1de8f8e9f5b6683d9f846ffd7cd819bc81de20a01cfdc83a3c
|
data/.circleci/config.yml
CHANGED
|
@@ -19,7 +19,7 @@ commands:
|
|
|
19
19
|
export PATH="${HOME}/.pyenv/bin:${PATH}"
|
|
20
20
|
export PATH="${HOME}/.rbenv/bin:${HOME}/.rbenv/shims:${PATH}"
|
|
21
21
|
export PATH="${HOME}/project/node_modules/.bin:${PATH}"
|
|
22
|
-
eval "$(pyenv init
|
|
22
|
+
eval "$(pyenv init --path)"
|
|
23
23
|
eval "$(pyenv virtualenv-init -)"
|
|
24
24
|
eval "$(rbenv init -)"
|
|
25
25
|
export BUNDLE_PATH=vendor/bundle
|
|
@@ -97,11 +97,12 @@ jobs:
|
|
|
97
97
|
steps:
|
|
98
98
|
- set_up_environment
|
|
99
99
|
- run_with_languages:
|
|
100
|
-
label:
|
|
100
|
+
label: Test
|
|
101
101
|
command: |
|
|
102
|
-
|
|
102
|
+
bundle exec rake citest
|
|
103
103
|
# https://github.com/bluelabsio/records-mover/blob/master/Makefile#L25
|
|
104
104
|
git status --porcelain coverage/.last_run.json
|
|
105
|
+
git diff coverage/.last_run.json
|
|
105
106
|
test -z "$(git status --porcelain coverage/.last_run.json)"
|
|
106
107
|
|
|
107
108
|
workflows:
|
data/.envrc
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
PATH_add bin
|
|
2
|
-
eval $(with-op op-env sh -e ASANA__PERSONAL_ACCESS_TOKEN)
|
|
2
|
+
eval $(with-op op-env sh -e ASANA__PERSONAL_ACCESS_TOKEN -e ASANA__DEFAULT_ASSIGNEE_GID)
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
checkoff (0.
|
|
4
|
+
checkoff (0.12.1)
|
|
5
5
|
activesupport
|
|
6
6
|
asana (> 0.10.0)
|
|
7
7
|
cache_method
|
|
8
8
|
dalli
|
|
9
|
+
gli
|
|
9
10
|
|
|
10
11
|
GEM
|
|
11
12
|
remote: https://rubygems.org/
|
|
12
13
|
specs:
|
|
13
|
-
activesupport (6.1.
|
|
14
|
+
activesupport (6.1.4)
|
|
14
15
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
15
16
|
i18n (>= 1.6, < 2)
|
|
16
17
|
minitest (>= 5.1)
|
|
@@ -27,23 +28,32 @@ GEM
|
|
|
27
28
|
cache_method (0.2.7)
|
|
28
29
|
cache (>= 0.2.1)
|
|
29
30
|
childprocess (4.0.0)
|
|
30
|
-
concurrent-ruby (1.1.
|
|
31
|
+
concurrent-ruby (1.1.9)
|
|
31
32
|
dalli (2.7.11)
|
|
32
|
-
docile (1.
|
|
33
|
-
faraday (1.
|
|
33
|
+
docile (1.4.0)
|
|
34
|
+
faraday (1.5.1)
|
|
35
|
+
faraday-em_http (~> 1.0)
|
|
36
|
+
faraday-em_synchrony (~> 1.0)
|
|
34
37
|
faraday-excon (~> 1.1)
|
|
38
|
+
faraday-httpclient (~> 1.0.1)
|
|
35
39
|
faraday-net_http (~> 1.0)
|
|
36
40
|
faraday-net_http_persistent (~> 1.1)
|
|
41
|
+
faraday-patron (~> 1.0)
|
|
37
42
|
multipart-post (>= 1.2, < 3)
|
|
38
43
|
ruby2_keywords (>= 0.0.4)
|
|
44
|
+
faraday-em_http (1.0.0)
|
|
45
|
+
faraday-em_synchrony (1.0.0)
|
|
39
46
|
faraday-excon (1.1.0)
|
|
47
|
+
faraday-httpclient (1.0.1)
|
|
40
48
|
faraday-net_http (1.0.1)
|
|
41
|
-
faraday-net_http_persistent (1.
|
|
49
|
+
faraday-net_http_persistent (1.2.0)
|
|
50
|
+
faraday-patron (1.0.0)
|
|
42
51
|
faraday_middleware (1.0.0)
|
|
43
52
|
faraday (~> 1.0)
|
|
44
53
|
faraday_middleware-multi_json (0.0.6)
|
|
45
54
|
faraday_middleware
|
|
46
55
|
multi_json
|
|
56
|
+
gli (2.20.1)
|
|
47
57
|
i18n (1.8.10)
|
|
48
58
|
concurrent-ruby (~> 1.0)
|
|
49
59
|
imagen (0.1.8)
|
|
@@ -62,9 +72,10 @@ GEM
|
|
|
62
72
|
multi_json (~> 1.3)
|
|
63
73
|
multi_xml (~> 0.5)
|
|
64
74
|
rack (>= 1.2, < 3)
|
|
65
|
-
overcommit (0.
|
|
75
|
+
overcommit (0.58.0)
|
|
66
76
|
childprocess (>= 0.6.3, < 5)
|
|
67
77
|
iniparse (~> 1.4)
|
|
78
|
+
rexml (~> 3.2)
|
|
68
79
|
parallel (1.20.1)
|
|
69
80
|
parser (3.0.1.1)
|
|
70
81
|
ast (~> 2.4.1)
|
|
@@ -73,7 +84,7 @@ GEM
|
|
|
73
84
|
rake (13.0.3)
|
|
74
85
|
regexp_parser (2.1.1)
|
|
75
86
|
rexml (3.2.5)
|
|
76
|
-
rubocop (1.
|
|
87
|
+
rubocop (1.15.0)
|
|
77
88
|
parallel (~> 1.10)
|
|
78
89
|
parser (>= 3.0.0.0)
|
|
79
90
|
rainbow (>= 2.2.2, < 4.0)
|
|
@@ -89,13 +100,14 @@ GEM
|
|
|
89
100
|
rubocop-rake (0.5.1)
|
|
90
101
|
rubocop
|
|
91
102
|
ruby-progressbar (1.11.0)
|
|
92
|
-
ruby2_keywords (0.0.
|
|
103
|
+
ruby2_keywords (0.0.5)
|
|
93
104
|
rugged (1.1.0)
|
|
94
105
|
simplecov (0.21.2)
|
|
95
106
|
docile (~> 1.1)
|
|
96
107
|
simplecov-html (~> 0.11)
|
|
97
108
|
simplecov_json_formatter (~> 0.1)
|
|
98
109
|
simplecov-html (0.12.3)
|
|
110
|
+
simplecov-lcov (0.8.0)
|
|
99
111
|
simplecov_json_formatter (0.1.3)
|
|
100
112
|
tzinfo (2.0.4)
|
|
101
113
|
concurrent-ruby (~> 1.0)
|
|
@@ -118,13 +130,14 @@ DEPENDENCIES
|
|
|
118
130
|
checkoff!
|
|
119
131
|
minitest-profile
|
|
120
132
|
mocha
|
|
121
|
-
overcommit
|
|
133
|
+
overcommit (>= 0.58.0)
|
|
122
134
|
rake (~> 13.0)
|
|
123
135
|
rubocop
|
|
124
136
|
rubocop-minitest
|
|
125
137
|
rubocop-rake
|
|
126
138
|
simplecov
|
|
139
|
+
simplecov-lcov
|
|
127
140
|
undercover
|
|
128
141
|
|
|
129
142
|
BUNDLED WITH
|
|
130
|
-
2.2.
|
|
143
|
+
2.2.24
|
data/Makefile
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.PHONY: clean test help quality localtest
|
|
1
|
+
.PHONY: clean test help quality localtest
|
|
2
2
|
.DEFAULT_GOAL := default
|
|
3
3
|
|
|
4
4
|
define PRINT_HELP_PYSCRIPT
|
|
@@ -32,7 +32,10 @@ Gemfile.lock.installed: Gemfile.lock
|
|
|
32
32
|
|
|
33
33
|
bundle_install: Gemfile.lock.installed ## Install Ruby dependencies
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
clear_metrics: ## remove or reset result artifacts created by tests and quality tools
|
|
36
|
+
bundle exec rake clear_metrics
|
|
37
|
+
|
|
38
|
+
clean: clear_metrics ## remove all built artifacts
|
|
36
39
|
|
|
37
40
|
quality: ## run precommit quality checks
|
|
38
41
|
bundle exec overcommit --run
|
|
@@ -40,7 +43,8 @@ quality: ## run precommit quality checks
|
|
|
40
43
|
test: ## Run lower-level tests
|
|
41
44
|
@bundle exec rake test
|
|
42
45
|
|
|
43
|
-
localtest:
|
|
46
|
+
localtest: ## run default local actions
|
|
47
|
+
@bundle exec rake localtest
|
|
44
48
|
|
|
45
49
|
update_from_cookiecutter: ## Bring in changes from template project used to create this repo
|
|
46
50
|
bundle exec overcommit --uninstall
|
data/README.md
CHANGED
|
@@ -12,7 +12,7 @@ $ checkoff --help
|
|
|
12
12
|
View tasks:
|
|
13
13
|
checkoff view workspace project [section]
|
|
14
14
|
|
|
15
|
-
'project' can be set to a project name, or :my_tasks
|
|
15
|
+
'project' can be set to a project name, or :my_tasks for your user task list.
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
Let's say we have a project like this:
|
data/checkoff.gemspec
CHANGED
|
@@ -26,16 +26,21 @@ Gem::Specification.new do |spec|
|
|
|
26
26
|
spec.add_runtime_dependency 'asana', '>0.10.0'
|
|
27
27
|
spec.add_runtime_dependency 'cache_method'
|
|
28
28
|
spec.add_runtime_dependency 'dalli'
|
|
29
|
+
spec.add_runtime_dependency 'gli'
|
|
29
30
|
|
|
30
31
|
spec.add_development_dependency 'bump'
|
|
31
32
|
spec.add_development_dependency 'bundler'
|
|
32
33
|
spec.add_development_dependency 'minitest-profile'
|
|
33
34
|
spec.add_development_dependency 'mocha'
|
|
34
|
-
|
|
35
|
+
# 0.58.0 and 0.57.0 don't seem super compatible with signatures, and
|
|
36
|
+
# magit doesn't seem to want to use the bundled version at the moment,
|
|
37
|
+
# so let's favor the more recent version...
|
|
38
|
+
spec.add_development_dependency 'overcommit', ['>=0.58.0']
|
|
35
39
|
spec.add_development_dependency 'rake', '~> 13.0'
|
|
36
40
|
spec.add_development_dependency 'rubocop'
|
|
37
41
|
spec.add_development_dependency 'rubocop-minitest'
|
|
38
42
|
spec.add_development_dependency 'rubocop-rake'
|
|
39
43
|
spec.add_development_dependency 'simplecov'
|
|
44
|
+
spec.add_development_dependency 'simplecov-lcov'
|
|
40
45
|
spec.add_development_dependency 'undercover'
|
|
41
46
|
end
|
data/coverage/.last_run.json
CHANGED
data/exe/checkoff
CHANGED
data/fix.sh
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
set -o pipefail
|
|
4
4
|
|
|
5
|
+
apt_upgraded=0
|
|
6
|
+
|
|
7
|
+
update_apt() {
|
|
8
|
+
if [ "${apt_upgraded}" = 0 ]
|
|
9
|
+
then
|
|
10
|
+
sudo apt-get update -y
|
|
11
|
+
apt_upgraded=1
|
|
12
|
+
fi
|
|
13
|
+
}
|
|
14
|
+
|
|
5
15
|
install_rbenv() {
|
|
6
16
|
if [ "$(uname)" == "Darwin" ]
|
|
7
17
|
then
|
|
@@ -77,6 +87,8 @@ ensure_dev_library() {
|
|
|
77
87
|
|
|
78
88
|
ensure_ruby_build_requirements() {
|
|
79
89
|
ensure_dev_library readline/readline.h readline libreadline-dev
|
|
90
|
+
ensure_dev_library zlib.h zlib zlib1g-dev
|
|
91
|
+
ensure_dev_library openssl/ssl.h openssl libssl-dev
|
|
80
92
|
}
|
|
81
93
|
|
|
82
94
|
# You can find out which feature versions are still supported / have
|
|
@@ -92,7 +104,17 @@ ensure_ruby_versions() {
|
|
|
92
104
|
|
|
93
105
|
for ver in $ruby_versions
|
|
94
106
|
do
|
|
95
|
-
|
|
107
|
+
# These CFLAGS can be retired once 2.6.7 is no longer needed :
|
|
108
|
+
#
|
|
109
|
+
# https://github.com/rbenv/ruby-build/issues/1747
|
|
110
|
+
# https://github.com/rbenv/ruby-build/issues/1489
|
|
111
|
+
# https://bugs.ruby-lang.org/issues/17777
|
|
112
|
+
if [ "${ver}" == 2.6.7 ]
|
|
113
|
+
then
|
|
114
|
+
CFLAGS="-Wno-error=implicit-function-declaration" rbenv install -s "${ver}"
|
|
115
|
+
else
|
|
116
|
+
rbenv install -s "${ver}"
|
|
117
|
+
fi
|
|
96
118
|
done
|
|
97
119
|
}
|
|
98
120
|
|
|
@@ -101,7 +123,17 @@ ensure_bundle() {
|
|
|
101
123
|
#
|
|
102
124
|
# https://app.circleci.com/pipelines/github/apiology/source_finder/21/workflows/88db659f-a4f4-4751-abc0-46f5929d8e58/jobs/107
|
|
103
125
|
set_rbenv_env_variables
|
|
104
|
-
bundle --version >/dev/null 2>&1 || gem install bundler
|
|
126
|
+
bundle --version >/dev/null 2>&1 || gem install --no-document bundler
|
|
127
|
+
bundler_version=$(bundle --version | cut -d ' ' -f3)
|
|
128
|
+
bundler_version_major=$(cut -d. -f1 <<< "${bundler_version}")
|
|
129
|
+
bundler_version_minor=$(cut -d. -f2 <<< "${bundler_version}")
|
|
130
|
+
# Version 2.1 of bundler seems to have some issues with nokogiri:
|
|
131
|
+
#
|
|
132
|
+
# https://app.asana.com/0/1107901397356088/1199504270687298
|
|
133
|
+
if [ "${bundler_version_major}" == 2 ] && [ "${bundler_version_minor}" -lt 2 ]
|
|
134
|
+
then
|
|
135
|
+
gem install --no-document bundler
|
|
136
|
+
fi
|
|
105
137
|
make bundle_install
|
|
106
138
|
# https://bundler.io/v2.0/bundle_lock.html#SUPPORTING-OTHER-PLATFORMS
|
|
107
139
|
#
|
|
@@ -138,8 +170,9 @@ WARNING: seems you still have not added 'pyenv' to the load path.
|
|
|
138
170
|
# Load pyenv automatically by adding
|
|
139
171
|
# the following to ~/.bashrc:
|
|
140
172
|
|
|
141
|
-
export
|
|
142
|
-
|
|
173
|
+
export PYENV_ROOT="${HOME}/.pyenv"
|
|
174
|
+
export PATH="${PYENV_ROOT}/bin:$PATH"
|
|
175
|
+
eval "$(pyenv init --path)"
|
|
143
176
|
eval "$(pyenv virtualenv-init -)"
|
|
144
177
|
EOF
|
|
145
178
|
fi
|
|
@@ -153,8 +186,9 @@ set_pyenv_env_variables() {
|
|
|
153
186
|
#
|
|
154
187
|
# https://app.circleci.com/pipelines/github/apiology/cookiecutter-pypackage/15/workflows/10506069-7662-46bd-b915-2992db3f795b/jobs/15
|
|
155
188
|
set +u
|
|
156
|
-
export
|
|
157
|
-
|
|
189
|
+
export PYENV_ROOT="${HOME}/.pyenv"
|
|
190
|
+
export PATH="${PYENV_ROOT}/bin:$PATH"
|
|
191
|
+
eval "$(pyenv init --path)"
|
|
158
192
|
eval "$(pyenv virtualenv-init -)"
|
|
159
193
|
set -u
|
|
160
194
|
}
|
|
@@ -179,7 +213,7 @@ install_package() {
|
|
|
179
213
|
HOMEBREW_NO_AUTO_UPDATE=1 brew install "${homebrew_package}"
|
|
180
214
|
elif type apt-get >/dev/null 2>&1
|
|
181
215
|
then
|
|
182
|
-
|
|
216
|
+
update_apt
|
|
183
217
|
sudo apt-get install -y "${apt_package}"
|
|
184
218
|
else
|
|
185
219
|
>&2 echo "Teach me how to install packages on this plaform"
|
|
@@ -192,6 +226,7 @@ ensure_python_build_requirements() {
|
|
|
192
226
|
ensure_dev_library bzlib.h bzip2 libbz2-dev
|
|
193
227
|
ensure_dev_library openssl/ssl.h openssl libssl-dev
|
|
194
228
|
ensure_dev_library ffi.h libffi libffi-dev
|
|
229
|
+
ensure_dev_library sqlite3.h sqlite3 libsqlite3-dev
|
|
195
230
|
}
|
|
196
231
|
|
|
197
232
|
# You can find out which feature versions are still supported / have
|
data/lib/checkoff/cli.rb
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
require 'ostruct'
|
|
6
6
|
require 'dalli'
|
|
7
|
+
require 'gli'
|
|
7
8
|
require 'cache_method'
|
|
8
9
|
require_relative 'workspaces'
|
|
9
10
|
require_relative 'projects'
|
|
@@ -11,41 +12,44 @@ require_relative 'tasks'
|
|
|
11
12
|
require_relative 'sections'
|
|
12
13
|
|
|
13
14
|
module Checkoff
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def initialize(config: Checkoff::ConfigLoader.load(:asana),
|
|
20
|
-
workspaces: Checkoff::Workspaces.new(config: config),
|
|
15
|
+
# CLI subcommand that shows tasks in JSON form
|
|
16
|
+
class ViewSubcommand
|
|
17
|
+
def initialize(workspace_name, project_name, section_name,
|
|
18
|
+
task_name,
|
|
19
|
+
config: Checkoff::ConfigLoader.load(:asana),
|
|
21
20
|
projects: Checkoff::Projects.new(config: config),
|
|
22
21
|
sections: Checkoff::Sections.new(config: config,
|
|
23
22
|
projects: projects),
|
|
24
|
-
tasks: Checkoff::Tasks.new(config: config
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@
|
|
28
|
-
@
|
|
23
|
+
tasks: Checkoff::Tasks.new(config: config,
|
|
24
|
+
sections: sections),
|
|
25
|
+
stderr: $stderr)
|
|
26
|
+
@workspace_name = workspace_name
|
|
27
|
+
@stderr = stderr
|
|
28
|
+
validate_and_assign_project_name(project_name)
|
|
29
|
+
@section_name = section_name
|
|
30
|
+
@task_name = task_name
|
|
29
31
|
@sections = sections
|
|
30
32
|
@tasks = tasks
|
|
31
|
-
@kernel = kernel
|
|
32
|
-
@stderr = stderr
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
task_out[:due] = task.due_at
|
|
35
|
+
def run
|
|
36
|
+
if section_name.nil?
|
|
37
|
+
run_on_project(workspace_name, project_name)
|
|
38
|
+
elsif task_name.nil?
|
|
39
|
+
run_on_section(workspace_name, project_name, section_name)
|
|
40
|
+
else
|
|
41
|
+
run_on_task(workspace_name, project_name, section_name, task_name)
|
|
43
42
|
end
|
|
44
|
-
task_out
|
|
45
43
|
end
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def validate_and_assign_project_name(project_name)
|
|
48
|
+
@project_name = if project_name.start_with? ':'
|
|
49
|
+
project_name[1..].to_sym
|
|
50
|
+
else
|
|
51
|
+
project_name
|
|
52
|
+
end
|
|
49
53
|
end
|
|
50
54
|
|
|
51
55
|
def run_on_project(workspace, project)
|
|
@@ -63,80 +67,89 @@ module Checkoff
|
|
|
63
67
|
tasks_to_hash(tasks).to_json
|
|
64
68
|
end
|
|
65
69
|
|
|
66
|
-
def
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
def run_on_task(workspace, project, section, task_name)
|
|
71
|
+
section = nil if section == ''
|
|
72
|
+
task = tasks.task(workspace, project, task_name, section_name: section)
|
|
73
|
+
task_to_hash(task).to_json
|
|
70
74
|
end
|
|
71
75
|
|
|
72
|
-
def
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
def task_to_hash(task)
|
|
77
|
+
task_out = {
|
|
78
|
+
name: task.name,
|
|
79
|
+
}
|
|
80
|
+
if task.due_on
|
|
81
|
+
task_out[:due] = task.due_on
|
|
82
|
+
elsif task.due_at
|
|
83
|
+
task_out[:due] = task.due_at
|
|
84
|
+
end
|
|
85
|
+
task_out
|
|
77
86
|
end
|
|
78
87
|
|
|
79
|
-
def
|
|
80
|
-
|
|
81
|
-
subargs.project = args[2]
|
|
82
|
-
subargs.section = args[3]
|
|
88
|
+
def tasks_to_hash(tasks)
|
|
89
|
+
tasks.map { |task| task_to_hash(task) }
|
|
83
90
|
end
|
|
84
91
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
subargs.task_name = args[2]
|
|
88
|
-
end
|
|
92
|
+
attr_reader :workspace_name, :project_name, :section_name, :task_name, :sections, :tasks, :stderr
|
|
93
|
+
end
|
|
89
94
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
raise
|
|
101
|
-
end
|
|
102
|
-
[mode, subargs]
|
|
95
|
+
# CLI subcommand that creates a task
|
|
96
|
+
class QuickaddSubcommand
|
|
97
|
+
def initialize(workspace_name, task_name,
|
|
98
|
+
config: Checkoff::ConfigLoader.load(:asana),
|
|
99
|
+
workspaces: Checkoff::Workspaces.new(config: config),
|
|
100
|
+
tasks: Checkoff::Tasks.new(config: config))
|
|
101
|
+
@workspace_name = workspace_name
|
|
102
|
+
@task_name = task_name
|
|
103
|
+
@workspaces = workspaces
|
|
104
|
+
@tasks = tasks
|
|
103
105
|
end
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
stderr.puts " #{$PROGRAM_NAME} quickadd workspace task_name"
|
|
110
|
-
stderr.puts
|
|
111
|
-
stderr.puts "'project' can be set to a project name, or :my_tasks, " \
|
|
112
|
-
":my_tasks_upcoming, :my_tasks_new, or :my_tasks_today"
|
|
106
|
+
|
|
107
|
+
def run
|
|
108
|
+
workspace = @workspaces.workspace_by_name(workspace_name)
|
|
109
|
+
@tasks.add_task(task_name,
|
|
110
|
+
workspace_gid: workspace.gid)
|
|
113
111
|
end
|
|
114
112
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
attr_reader :workspace_name, :task_name
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Provide ability for CLI to pull Asana items
|
|
119
|
+
class CheckoffGLIApp
|
|
120
|
+
extend GLI::App
|
|
121
|
+
|
|
122
|
+
program_desc 'Command-line client for Asana (unofficial)'
|
|
123
|
+
|
|
124
|
+
subcommand_option_handling :normal
|
|
125
|
+
arguments :strict
|
|
126
|
+
|
|
127
|
+
desc 'Add a short task to Asana'
|
|
128
|
+
arg 'workspace'
|
|
129
|
+
arg 'task_name'
|
|
130
|
+
command :quickadd do |c|
|
|
131
|
+
c.action do |_global_options, _options, args|
|
|
132
|
+
workspace_name = args.fetch(0)
|
|
133
|
+
task_name = args.fetch(1)
|
|
134
|
+
|
|
135
|
+
QuickaddSubcommand.new(workspace_name, task_name).run
|
|
125
136
|
end
|
|
126
137
|
end
|
|
127
138
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
desc 'Output representation of Asana tasks'
|
|
140
|
+
arg 'workspace'
|
|
141
|
+
arg 'project'
|
|
142
|
+
arg 'section', :optional
|
|
143
|
+
arg 'task_name', :optional
|
|
144
|
+
command :view do |c|
|
|
145
|
+
c.action do |_global_options, _options, args|
|
|
146
|
+
workspace_name = args.fetch(0)
|
|
147
|
+
project_name = args.fetch(1)
|
|
148
|
+
section_name = args[2]
|
|
149
|
+
task_name = args[3]
|
|
150
|
+
|
|
151
|
+
puts ViewSubcommand.new(workspace_name, project_name, section_name, task_name).run
|
|
138
152
|
end
|
|
139
153
|
end
|
|
140
154
|
end
|
|
141
|
-
# rubocop:enable Metrics/ClassLength
|
|
142
155
|
end
|
|
@@ -13,10 +13,6 @@ module Checkoff
|
|
|
13
13
|
@yaml_filename = yaml_filename
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def envvar_name(key)
|
|
17
|
-
"#{@envvar_prefix}__#{key.upcase}"
|
|
18
|
-
end
|
|
19
|
-
|
|
20
16
|
def [](key)
|
|
21
17
|
config_value = @config[key]
|
|
22
18
|
return config_value unless config_value.nil?
|
|
@@ -31,25 +27,35 @@ module Checkoff
|
|
|
31
27
|
raise KeyError,
|
|
32
28
|
"Please configure either the #{key} key in #{@yaml_filename} or set #{envvar_name(key)}"
|
|
33
29
|
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def envvar_name(key)
|
|
34
|
+
"#{@envvar_prefix}__#{key.upcase}"
|
|
35
|
+
end
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
# Load configuration file
|
|
37
39
|
class ConfigLoader
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
class << self
|
|
41
|
+
def load(sym)
|
|
42
|
+
yaml_result = load_yaml_file(sym)
|
|
43
|
+
EnvFallbackConfigLoader.new(yaml_result, sym, yaml_filename(sym))
|
|
44
|
+
end
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
filename = yaml_filename(sym)
|
|
45
|
-
return {} unless File.exist?(filename)
|
|
46
|
+
private
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
def load_yaml_file(sym)
|
|
49
|
+
filename = yaml_filename(sym)
|
|
50
|
+
return {} unless File.exist?(filename)
|
|
51
|
+
|
|
52
|
+
YAML.load_file(filename).with_indifferent_access
|
|
53
|
+
end
|
|
49
54
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
def yaml_filename(sym)
|
|
56
|
+
file = "#{sym}.yml"
|
|
57
|
+
File.expand_path("~/.#{file}")
|
|
58
|
+
end
|
|
53
59
|
end
|
|
54
60
|
end
|
|
55
61
|
end
|
data/lib/checkoff/projects.rb
CHANGED
|
@@ -22,8 +22,6 @@ module Checkoff
|
|
|
22
22
|
LONG_CACHE_TIME = MINUTE * 15
|
|
23
23
|
SHORT_CACHE_TIME = MINUTE * 5
|
|
24
24
|
|
|
25
|
-
# XXX: Move low-level functions private
|
|
26
|
-
|
|
27
25
|
def initialize(config: Checkoff::ConfigLoader.load(:asana),
|
|
28
26
|
asana_client: Asana::Client,
|
|
29
27
|
workspaces: Checkoff::Workspaces.new(config: config))
|
|
@@ -32,30 +30,23 @@ module Checkoff
|
|
|
32
30
|
@workspaces = workspaces
|
|
33
31
|
end
|
|
34
32
|
|
|
33
|
+
# Returns Asana Ruby API Client object
|
|
35
34
|
def client
|
|
36
35
|
@workspaces.client
|
|
37
36
|
end
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
projects.find_by_workspace(workspace: workspace.gid)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def my_tasks(workspace_name)
|
|
52
|
-
workspace = @workspaces.workspace_by_name(workspace_name)
|
|
53
|
-
result = client.user_task_lists.get_user_task_list_for_user(user_gid: 'me',
|
|
54
|
-
workspace: workspace.gid)
|
|
55
|
-
gid = result.gid
|
|
56
|
-
projects.find_by_id(gid)
|
|
38
|
+
# Default options used in Asana API to pull taskso
|
|
39
|
+
def task_options
|
|
40
|
+
{
|
|
41
|
+
per_page: 100,
|
|
42
|
+
options: {
|
|
43
|
+
fields: %w[name completed_at due_at due_on tags
|
|
44
|
+
memberships.project.gid memberships.section.name dependencies],
|
|
45
|
+
},
|
|
46
|
+
}
|
|
57
47
|
end
|
|
58
48
|
|
|
49
|
+
# pulls an Asana API project class given a name
|
|
59
50
|
def project(workspace_name, project_name)
|
|
60
51
|
if project_name.is_a?(Symbol) && project_name.to_s.start_with?('my_tasks')
|
|
61
52
|
my_tasks(workspace_name)
|
|
@@ -68,20 +59,12 @@ module Checkoff
|
|
|
68
59
|
end
|
|
69
60
|
cache_method :project, LONG_CACHE_TIME
|
|
70
61
|
|
|
62
|
+
# find uncompleted tasks in a list
|
|
71
63
|
def active_tasks(tasks)
|
|
72
64
|
tasks.select { |task| task.completed_at.nil? }
|
|
73
65
|
end
|
|
74
66
|
|
|
75
|
-
|
|
76
|
-
{
|
|
77
|
-
per_page: 100,
|
|
78
|
-
options: {
|
|
79
|
-
fields: %w[name completed_at due_at due_on assignee_status tags
|
|
80
|
-
memberships.project.gid memberships.section.name dependencies],
|
|
81
|
-
},
|
|
82
|
-
}
|
|
83
|
-
end
|
|
84
|
-
|
|
67
|
+
# pull task objects from a named project
|
|
85
68
|
def tasks_from_project(project, only_uncompleted: true, extra_fields: [])
|
|
86
69
|
options = task_options
|
|
87
70
|
options[:completed_since] = '9999-12-01' if only_uncompleted
|
|
@@ -90,5 +73,27 @@ module Checkoff
|
|
|
90
73
|
client.tasks.find_all(**options).to_a
|
|
91
74
|
end
|
|
92
75
|
cache_method :tasks_from_project, SHORT_CACHE_TIME
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def projects
|
|
80
|
+
client.projects
|
|
81
|
+
end
|
|
82
|
+
cache_method :projects, LONG_CACHE_TIME
|
|
83
|
+
|
|
84
|
+
def projects_by_workspace_name(workspace_name)
|
|
85
|
+
workspace = @workspaces.workspace_by_name(workspace_name)
|
|
86
|
+
raise "Could not find workspace named #{workspace_name}" unless workspace
|
|
87
|
+
|
|
88
|
+
projects.find_by_workspace(workspace: workspace.gid)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def my_tasks(workspace_name)
|
|
92
|
+
workspace = @workspaces.workspace_by_name(workspace_name)
|
|
93
|
+
result = client.user_task_lists.get_user_task_list_for_user(user_gid: 'me',
|
|
94
|
+
workspace: workspace.gid)
|
|
95
|
+
gid = result.gid
|
|
96
|
+
projects.find_by_id(gid)
|
|
97
|
+
end
|
|
93
98
|
end
|
|
94
99
|
end
|
data/lib/checkoff/sections.rb
CHANGED
|
@@ -4,7 +4,6 @@ require 'forwardable'
|
|
|
4
4
|
|
|
5
5
|
module Checkoff
|
|
6
6
|
# Query different sections of Asana projects
|
|
7
|
-
# rubocop:disable Metrics/ClassLength
|
|
8
7
|
class Sections
|
|
9
8
|
MINUTE = 60
|
|
10
9
|
LONG_CACHE_TIME = MINUTE * 15
|
|
@@ -23,68 +22,75 @@ module Checkoff
|
|
|
23
22
|
@time = time
|
|
24
23
|
end
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
# Returns a list of Asana API section objects for a given project
|
|
26
|
+
def sections_or_raise(workspace_name, project_name)
|
|
27
|
+
project = project_or_raise(workspace_name, project_name)
|
|
28
|
+
client.sections.get_sections_for_project(project_gid: project.gid)
|
|
29
|
+
end
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
# Given a workspace name and project name, then provide a Hash of
|
|
32
|
+
# tasks with section name -> task list of the uncompleted tasks
|
|
33
|
+
def tasks_by_section(workspace_name, project_name)
|
|
34
|
+
project = project_or_raise(workspace_name, project_name)
|
|
35
|
+
tasks_by_section_for_project(project)
|
|
36
36
|
end
|
|
37
|
+
cache_method :tasks_by_section, SHORT_CACHE_TIME
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
# XXX: Rename to section_tasks
|
|
40
|
+
#
|
|
41
|
+
# Pulls task objects from a specified section
|
|
42
|
+
def tasks(workspace_name, project_name, section_name,
|
|
43
|
+
only_uncompleted: true,
|
|
44
|
+
extra_fields: [])
|
|
45
|
+
section = section_or_raise(workspace_name, project_name, section_name)
|
|
46
|
+
options = projects.task_options
|
|
47
|
+
# asana-0.10.3 gem doesn't support per_page - not sure if API
|
|
48
|
+
# itself does
|
|
49
|
+
options.delete(:per_page)
|
|
50
|
+
options[:options][:fields] += extra_fields
|
|
51
|
+
options[:completed_since] = '9999-12-01' if only_uncompleted
|
|
52
|
+
client.tasks.get_tasks_for_section(section_gid: section.gid,
|
|
53
|
+
**options).to_a
|
|
44
54
|
end
|
|
45
|
-
cache_method :
|
|
55
|
+
cache_method :tasks, SHORT_CACHE_TIME
|
|
46
56
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
# Pulls just names of tasks from a given section.
|
|
58
|
+
def section_task_names(workspace_name, project_name, section_name)
|
|
59
|
+
tasks = tasks(workspace_name, project_name, section_name)
|
|
60
|
+
tasks.map(&:name)
|
|
51
61
|
end
|
|
62
|
+
cache_method :section_task_names, SHORT_CACHE_TIME
|
|
63
|
+
|
|
64
|
+
private
|
|
52
65
|
|
|
66
|
+
# Given a project object, pull all tasks, then provide a Hash of
|
|
67
|
+
# tasks with section name -> task list of the uncompleted tasks
|
|
53
68
|
def tasks_by_section_for_project(project)
|
|
54
69
|
raw_tasks = projects.tasks_from_project(project)
|
|
55
70
|
active_tasks = projects.active_tasks(raw_tasks)
|
|
56
71
|
by_section(active_tasks, project.gid)
|
|
57
72
|
end
|
|
58
73
|
|
|
59
|
-
|
|
60
|
-
if task.name =~ /:$/
|
|
61
|
-
current_section = task.name
|
|
62
|
-
by_section[current_section] = []
|
|
63
|
-
else
|
|
64
|
-
by_section[current_section] ||= []
|
|
65
|
-
by_section[current_section] << task
|
|
66
|
-
end
|
|
67
|
-
[current_section, by_section]
|
|
68
|
-
end
|
|
74
|
+
def_delegators :@projects, :client
|
|
69
75
|
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
# Given a list of tasks, pull a Hash of tasks with section name -> task list
|
|
77
|
+
def by_section(tasks, project_gid)
|
|
72
78
|
by_section = {}
|
|
73
79
|
tasks.each do |task|
|
|
74
|
-
|
|
75
|
-
legacy_file_task_by_section(current_section, by_section, task)
|
|
80
|
+
file_task_by_section(by_section, task, project_gid)
|
|
76
81
|
end
|
|
77
82
|
by_section
|
|
78
83
|
end
|
|
84
|
+
cache_method :by_section, LONG_CACHE_TIME
|
|
79
85
|
|
|
80
|
-
def
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
def file_task_by_section(by_section, task, project_gid)
|
|
87
|
+
membership = task.memberships.find { |m| m['project']['gid'] == project_gid }
|
|
88
|
+
raise "Could not find task in project_gid #{project_gid}: #{task}" if membership.nil?
|
|
89
|
+
|
|
90
|
+
current_section = membership['section']['name']
|
|
91
|
+
current_section = nil if current_section == '(no section)'
|
|
92
|
+
by_section[current_section] ||= []
|
|
93
|
+
by_section[current_section] << task
|
|
88
94
|
end
|
|
89
95
|
|
|
90
96
|
def project_or_raise(workspace_name, project_name)
|
|
@@ -96,88 +102,20 @@ module Checkoff
|
|
|
96
102
|
project
|
|
97
103
|
end
|
|
98
104
|
|
|
99
|
-
def
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
raise NotImplementedError, 'Section-based user task lists not yet supported'
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
ASSIGNEE_STATUS_BY_PROJECT_NAME = {
|
|
106
|
-
my_tasks_new: 'inbox',
|
|
107
|
-
my_tasks_today: 'today',
|
|
108
|
-
my_tasks_upcoming: 'upcoming',
|
|
109
|
-
}.freeze
|
|
110
|
-
|
|
111
|
-
def user_task_list_by_section(workspace_name, project_name)
|
|
112
|
-
verify_legacy_user_task_list!(workspace_name)
|
|
113
|
-
|
|
114
|
-
project = projects.project(workspace_name, project_name)
|
|
115
|
-
if project_name == :my_tasks
|
|
116
|
-
legacy_tasks_by_section_for_project(project)
|
|
117
|
-
else
|
|
118
|
-
legacy_tasks_by_section_for_project_and_assignee_status(project,
|
|
119
|
-
ASSIGNEE_STATUS_BY_PROJECT_NAME.fetch(project_name))
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def tasks_by_section(workspace_name, project_name)
|
|
124
|
-
project = project_or_raise(workspace_name, project_name)
|
|
125
|
-
case project_name
|
|
126
|
-
when :my_tasks, :my_tasks_new, :my_tasks_today, :my_tasks_upcoming
|
|
127
|
-
user_task_list_by_section(workspace_name, project_name)
|
|
128
|
-
else
|
|
129
|
-
tasks_by_section_for_project(project)
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
cache_method :tasks_by_section, SHORT_CACHE_TIME
|
|
133
|
-
|
|
134
|
-
# XXX: Rename to section_tasks
|
|
135
|
-
def tasks(workspace_name, project_name, section_name)
|
|
136
|
-
tasks_by_section(workspace_name, project_name).fetch(section_name, [])
|
|
137
|
-
end
|
|
138
|
-
cache_method :tasks, SHORT_CACHE_TIME
|
|
139
|
-
|
|
140
|
-
def section_task_names(workspace_name, project_name, section_name)
|
|
141
|
-
tasks = tasks(workspace_name, project_name, section_name)
|
|
142
|
-
if tasks.nil?
|
|
143
|
-
by_section = tasks_by_section(workspace_name, project_name)
|
|
144
|
-
desc = "#{workspace_name} | #{project_name} | #{section_name}"
|
|
145
|
-
raise "Could not find task names for #{desc}. " \
|
|
146
|
-
"Valid sections: #{by_section.keys}"
|
|
147
|
-
end
|
|
148
|
-
tasks.map(&:name)
|
|
149
|
-
end
|
|
150
|
-
cache_method :section_task_names, SHORT_CACHE_TIME
|
|
151
|
-
|
|
152
|
-
def user_task_list_migrated_to_real_sections?(workspace_name)
|
|
153
|
-
workspace = workspaces.workspace_by_name(workspace_name)
|
|
154
|
-
result = client.user_task_lists.get_user_task_list_for_user(user_gid: 'me',
|
|
155
|
-
workspace: workspace.gid)
|
|
156
|
-
result.migration_status != 'not_migrated'
|
|
105
|
+
def section(workspace_name, project_name, section_name)
|
|
106
|
+
sections = sections_or_raise(workspace_name, project_name)
|
|
107
|
+
sections.find { |section| section.name.chomp(':') == section_name.chomp(':') }
|
|
157
108
|
end
|
|
158
109
|
|
|
159
|
-
def
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
Date.parse(task.due_on) <= time.today
|
|
164
|
-
else
|
|
165
|
-
true # set a due date if you don't want to do this now
|
|
166
|
-
end
|
|
167
|
-
end
|
|
110
|
+
def section_or_raise(workspace_name, project_name, section_name)
|
|
111
|
+
section = section(workspace_name, project_name, section_name)
|
|
112
|
+
if section.nil?
|
|
113
|
+
valid_sections = sections_or_raise(workspace_name, project_name).map(&:name)
|
|
168
114
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
by_section.flat_map do |section_name, tasks|
|
|
172
|
-
task_names = tasks.map(&:name)
|
|
173
|
-
if section_name.nil?
|
|
174
|
-
task_names
|
|
175
|
-
else
|
|
176
|
-
[section_name, task_names]
|
|
177
|
-
end
|
|
115
|
+
raise "Could not find section #{section_name} under project #{project_name} " \
|
|
116
|
+
"under workspace #{workspace_name}. Valid sections: #{valid_sections}"
|
|
178
117
|
end
|
|
118
|
+
section
|
|
179
119
|
end
|
|
180
|
-
cache_method :project_task_names, SHORT_CACHE_TIME
|
|
181
120
|
end
|
|
182
|
-
# rubocop:enable Metrics/ClassLength
|
|
183
121
|
end
|
data/lib/checkoff/subtasks.rb
CHANGED
|
@@ -11,17 +11,7 @@ module Checkoff
|
|
|
11
11
|
|
|
12
12
|
extend Forwardable
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
if task.name =~ /:$/
|
|
16
|
-
current_section = task.name
|
|
17
|
-
by_section[current_section] = []
|
|
18
|
-
else
|
|
19
|
-
by_section[current_section] ||= []
|
|
20
|
-
by_section[current_section] << task
|
|
21
|
-
end
|
|
22
|
-
[current_section, by_section]
|
|
23
|
-
end
|
|
24
|
-
|
|
14
|
+
# pulls a Hash of subtasks broken out by section
|
|
25
15
|
def by_section(tasks)
|
|
26
16
|
current_section = nil
|
|
27
17
|
by_section = {}
|
|
@@ -37,5 +27,18 @@ module Checkoff
|
|
|
37
27
|
task.subtasks(projects.task_options)
|
|
38
28
|
end
|
|
39
29
|
cache_method :raw_subtasks, LONG_CACHE_TIME
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def file_task_by_section(current_section, by_section, task)
|
|
34
|
+
if task.name =~ /:$/
|
|
35
|
+
current_section = task.name
|
|
36
|
+
by_section[current_section] = []
|
|
37
|
+
else
|
|
38
|
+
by_section[current_section] ||= []
|
|
39
|
+
by_section[current_section] << task
|
|
40
|
+
end
|
|
41
|
+
[current_section, by_section]
|
|
42
|
+
end
|
|
40
43
|
end
|
|
41
44
|
end
|
data/lib/checkoff/tasks.rb
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
require_relative 'sections'
|
|
6
6
|
|
|
7
7
|
module Checkoff
|
|
8
|
-
# Pull
|
|
8
|
+
# Pull tasks from Asana
|
|
9
9
|
class Tasks
|
|
10
10
|
MINUTE = 60
|
|
11
11
|
HOUR = MINUTE * 60
|
|
@@ -22,25 +22,21 @@ module Checkoff
|
|
|
22
22
|
@asana_task = asana_task
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def projects
|
|
30
|
-
@projects ||= @sections.projects
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def task(workspace_name, project_name, task_name, only_uncompleted: true)
|
|
25
|
+
# Pull a specific task by name
|
|
26
|
+
def task(workspace_name, project_name, task_name,
|
|
27
|
+
section_name: :unspecified,
|
|
28
|
+
only_uncompleted: true)
|
|
34
29
|
project = projects.project(workspace_name, project_name)
|
|
35
|
-
tasks =
|
|
36
|
-
|
|
30
|
+
tasks = if section_name == :unspecified
|
|
31
|
+
projects.tasks_from_project(project,
|
|
32
|
+
only_uncompleted: only_uncompleted)
|
|
33
|
+
else
|
|
34
|
+
@sections.tasks(workspace_name, project_name, section_name,
|
|
35
|
+
only_uncompleted: only_uncompleted)
|
|
36
|
+
end
|
|
37
37
|
tasks.find { |task| task.name == task_name }
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
def tasks_minus_sections(tasks)
|
|
41
|
-
@sections.by_section(tasks).values.flatten
|
|
42
|
-
end
|
|
43
|
-
|
|
44
40
|
def add_task(name,
|
|
45
41
|
workspace_gid: default_workspace_gid,
|
|
46
42
|
assignee_gid: default_assignee_gid)
|
|
@@ -49,6 +45,16 @@ module Checkoff
|
|
|
49
45
|
workspace: workspace_gid, name: name)
|
|
50
46
|
end
|
|
51
47
|
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def client
|
|
51
|
+
@sections.client
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def projects
|
|
55
|
+
@projects ||= @sections.projects
|
|
56
|
+
end
|
|
57
|
+
|
|
52
58
|
def default_assignee_gid
|
|
53
59
|
@config.fetch(:default_assignee_gid)
|
|
54
60
|
end
|
data/lib/checkoff/version.rb
CHANGED
data/lib/checkoff/workspaces.rb
CHANGED
|
@@ -9,6 +9,7 @@ module Checkoff
|
|
|
9
9
|
@asana_client = asana_client
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
+
# Returns Asana Ruby API Client object
|
|
12
13
|
def client
|
|
13
14
|
@client ||= @asana_client.new do |c|
|
|
14
15
|
c.authentication :access_token, @config.fetch(:personal_access_token)
|
|
@@ -17,14 +18,17 @@ module Checkoff
|
|
|
17
18
|
end
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
@config.fetch(:default_workspace_gid)
|
|
22
|
-
end
|
|
23
|
-
|
|
21
|
+
# Pulls an Asana workspace object
|
|
24
22
|
def workspace_by_name(workspace_name)
|
|
25
23
|
client.workspaces.find_all.find do |workspace|
|
|
26
24
|
workspace.name == workspace_name
|
|
27
25
|
end || raise("Could not find workspace named [#{workspace_name}]")
|
|
28
26
|
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def default_workspace_gid
|
|
31
|
+
@config.fetch(:default_workspace_gid)
|
|
32
|
+
end
|
|
29
33
|
end
|
|
30
34
|
end
|
data/rakelib/clear_metrics.rake
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
desc '
|
|
3
|
+
desc 'Ensure that any locally ratcheted coverage metrics are cleared back ' \
|
|
4
|
+
'to git baseline'
|
|
4
5
|
task :clear_metrics do |_t|
|
|
5
6
|
ret =
|
|
6
7
|
system('git checkout coverage/.last_run.json')
|
|
7
8
|
raise unless ret
|
|
9
|
+
|
|
10
|
+
# Without this old lines which are removed are still counted,
|
|
11
|
+
# leading to inconsistent coverage percentages between runs.
|
|
12
|
+
#
|
|
13
|
+
# need to save coverage/.last_run.json
|
|
14
|
+
ret =
|
|
15
|
+
system('rm -fr coverage/assets coverage/.*.json.lock coverage/lcov/* coverage/index.html coverage/.resultset.json')
|
|
16
|
+
raise unless ret
|
|
8
17
|
end
|
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.12.1
|
|
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-07-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -66,6 +66,20 @@ dependencies:
|
|
|
66
66
|
- - ">="
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: gli
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
69
83
|
- !ruby/object:Gem::Dependency
|
|
70
84
|
name: bump
|
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -128,14 +142,14 @@ dependencies:
|
|
|
128
142
|
requirements:
|
|
129
143
|
- - ">="
|
|
130
144
|
- !ruby/object:Gem::Version
|
|
131
|
-
version:
|
|
145
|
+
version: 0.58.0
|
|
132
146
|
type: :development
|
|
133
147
|
prerelease: false
|
|
134
148
|
version_requirements: !ruby/object:Gem::Requirement
|
|
135
149
|
requirements:
|
|
136
150
|
- - ">="
|
|
137
151
|
- !ruby/object:Gem::Version
|
|
138
|
-
version:
|
|
152
|
+
version: 0.58.0
|
|
139
153
|
- !ruby/object:Gem::Dependency
|
|
140
154
|
name: rake
|
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -206,6 +220,20 @@ dependencies:
|
|
|
206
220
|
- - ">="
|
|
207
221
|
- !ruby/object:Gem::Version
|
|
208
222
|
version: '0'
|
|
223
|
+
- !ruby/object:Gem::Dependency
|
|
224
|
+
name: simplecov-lcov
|
|
225
|
+
requirement: !ruby/object:Gem::Requirement
|
|
226
|
+
requirements:
|
|
227
|
+
- - ">="
|
|
228
|
+
- !ruby/object:Gem::Version
|
|
229
|
+
version: '0'
|
|
230
|
+
type: :development
|
|
231
|
+
prerelease: false
|
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
233
|
+
requirements:
|
|
234
|
+
- - ">="
|
|
235
|
+
- !ruby/object:Gem::Version
|
|
236
|
+
version: '0'
|
|
209
237
|
- !ruby/object:Gem::Dependency
|
|
210
238
|
name: undercover
|
|
211
239
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -281,7 +309,7 @@ files:
|
|
|
281
309
|
- metrics/rubocop_high_water_mark
|
|
282
310
|
- metrics/scalastyle_high_water_mark
|
|
283
311
|
- metrics/shellcheck_high_water_mark
|
|
284
|
-
- rakelib/
|
|
312
|
+
- rakelib/citest.rake
|
|
285
313
|
- rakelib/clear_metrics.rake
|
|
286
314
|
- rakelib/default.rake
|
|
287
315
|
- rakelib/gem_tasks.rake
|
|
@@ -310,7 +338,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
310
338
|
- !ruby/object:Gem::Version
|
|
311
339
|
version: '0'
|
|
312
340
|
requirements: []
|
|
313
|
-
rubygems_version: 3.1
|
|
341
|
+
rubygems_version: 3.0.3.1
|
|
314
342
|
signing_key:
|
|
315
343
|
specification_version: 4
|
|
316
344
|
summary: Command-line and gem client for Asana (unofficial)
|