checkoff 0.11.1 → 0.13.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 +11 -4
- data/DEVELOPMENT.md +2 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +30 -13
- data/README.md +2 -5
- data/checkoff.gemspec +7 -3
- data/coverage/.last_run.json +2 -2
- data/fix.sh +8 -4
- data/lib/checkoff/cli.rb +151 -0
- data/lib/checkoff/projects.rb +20 -10
- data/lib/checkoff/sections.rb +43 -118
- data/lib/checkoff/subtasks.rb +7 -0
- data/lib/checkoff/tasks.rb +2 -5
- data/lib/checkoff/version.rb +1 -1
- data/rakelib/console.rake +6 -0
- data/rakelib/default.rake +1 -0
- data/rakelib/repl.rake +4 -0
- metadata +20 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8a996aad5215c1625924296ca8c851cb3293968c74e6bc3da78dce7996fa8041
|
|
4
|
+
data.tar.gz: 574da8911c1c542f6334ed2baa670dd69a3e09828ec6a76d3d3fe78b5c3e9d7a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 04d3b2b21b3409665f86035caf13ea1f6662c233f698c88df09cdfc7672abdc797945ba66dcc09076b79fc02e15523aab872b90ce116501cab4f23901427bd63
|
|
7
|
+
data.tar.gz: 8f34cc1b54f99aadae356a5be75871ccb8c2e0307c0d40bcc9c496f6b4103a7666e2daaff5db2dfdff3103939dd36cc3810692707f43263da64c94e3488c2842
|
data/.circleci/config.yml
CHANGED
|
@@ -37,7 +37,9 @@ commands:
|
|
|
37
37
|
sed -E -e 's/checkoff \([[:digit:]]+.[[:digit:]]+.[[:digit:]]+\)/checkoff (0.1.0)/g' \
|
|
38
38
|
Gemfile.lock > Gemfile.lock.deversioned
|
|
39
39
|
- restore_cache:
|
|
40
|
-
key: gems-
|
|
40
|
+
key: gems-v1-{{ checksum "Gemfile.lock.deversioned" }}
|
|
41
|
+
- restore_cache:
|
|
42
|
+
key: wheels-v1-{{ checksum "requirements_dev.txt" }}
|
|
41
43
|
- run:
|
|
42
44
|
name: Initialize packages
|
|
43
45
|
command: |
|
|
@@ -52,9 +54,13 @@ commands:
|
|
|
52
54
|
exit 1
|
|
53
55
|
fi
|
|
54
56
|
- save_cache:
|
|
55
|
-
key: gems-
|
|
57
|
+
key: gems-v1-{{ checksum "Gemfile.lock.deversioned" }}
|
|
56
58
|
paths:
|
|
57
59
|
- "vendor/bundle"
|
|
60
|
+
- save_cache:
|
|
61
|
+
key: wheels-v1-{{ checksum "requirements_dev.txt" }}
|
|
62
|
+
paths:
|
|
63
|
+
- "/home/circleci/.cache/pip/wheels"
|
|
58
64
|
- run:
|
|
59
65
|
name: Download new circleci tool
|
|
60
66
|
command: |
|
|
@@ -78,7 +84,7 @@ commands:
|
|
|
78
84
|
jobs:
|
|
79
85
|
overcommit:
|
|
80
86
|
docker:
|
|
81
|
-
- image: apiology/circleci:latest
|
|
87
|
+
- image: apiology/circleci-ruby:latest
|
|
82
88
|
steps:
|
|
83
89
|
- when:
|
|
84
90
|
condition:
|
|
@@ -93,7 +99,7 @@ jobs:
|
|
|
93
99
|
build:
|
|
94
100
|
working_directory: ~/checkoff
|
|
95
101
|
docker:
|
|
96
|
-
- image: apiology/circleci:latest
|
|
102
|
+
- image: apiology/circleci-ruby:latest
|
|
97
103
|
steps:
|
|
98
104
|
- set_up_environment
|
|
99
105
|
- run_with_languages:
|
|
@@ -117,6 +123,7 @@ workflows:
|
|
|
117
123
|
- main
|
|
118
124
|
jobs:
|
|
119
125
|
- build
|
|
126
|
+
- overcommit
|
|
120
127
|
overcommit:
|
|
121
128
|
jobs:
|
|
122
129
|
- overcommit
|
data/DEVELOPMENT.md
CHANGED
|
@@ -21,8 +21,10 @@ development. See the `.envrc` file for detail.
|
|
|
21
21
|
To publish new version as a maintainer:
|
|
22
22
|
|
|
23
23
|
```sh
|
|
24
|
+
git checkout main && git pull
|
|
24
25
|
git log "v$(bump current)..."
|
|
25
26
|
# Set type_of_bump to patch, minor, or major
|
|
26
27
|
bump --tag --tag-prefix=v ${type_of_bump:?}
|
|
27
28
|
rake release
|
|
29
|
+
git push
|
|
28
30
|
```
|
data/Gemfile
CHANGED
|
@@ -4,3 +4,16 @@ source 'https://rubygems.org'
|
|
|
4
4
|
|
|
5
5
|
# Specify your gem's dependencies in checkoff.gemspec
|
|
6
6
|
gemspec
|
|
7
|
+
|
|
8
|
+
# ruby-asana gem is pending key bugfixes for checkoff as of
|
|
9
|
+
# 2021-07-29:
|
|
10
|
+
#
|
|
11
|
+
# See
|
|
12
|
+
# https://github.com/Asana/ruby-asana/issues/109
|
|
13
|
+
# https://github.com/Asana/ruby-asana/issues/110
|
|
14
|
+
#
|
|
15
|
+
gem 'asana',
|
|
16
|
+
git: 'https://github.com/apiology/ruby-asana',
|
|
17
|
+
branch: 'checkoff_fixes'
|
|
18
|
+
|
|
19
|
+
# gem 'asana', path: '/Users/broz/src/ruby-asana'
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
|
+
GIT
|
|
2
|
+
remote: https://github.com/apiology/ruby-asana
|
|
3
|
+
revision: 83732f040728a4ef1cfc9ed61f65878dbee6aa23
|
|
4
|
+
branch: checkoff_fixes
|
|
5
|
+
specs:
|
|
6
|
+
asana (0.10.4)
|
|
7
|
+
faraday (~> 1.0)
|
|
8
|
+
faraday_middleware (~> 1.0)
|
|
9
|
+
faraday_middleware-multi_json (~> 0.0)
|
|
10
|
+
oauth2 (~> 1.4)
|
|
11
|
+
|
|
1
12
|
PATH
|
|
2
13
|
remote: .
|
|
3
14
|
specs:
|
|
4
|
-
checkoff (0.
|
|
15
|
+
checkoff (0.13.1)
|
|
5
16
|
activesupport
|
|
6
17
|
asana (> 0.10.0)
|
|
7
18
|
cache_method
|
|
@@ -11,51 +22,52 @@ PATH
|
|
|
11
22
|
GEM
|
|
12
23
|
remote: https://rubygems.org/
|
|
13
24
|
specs:
|
|
14
|
-
activesupport (6.1.
|
|
25
|
+
activesupport (6.1.4)
|
|
15
26
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
16
27
|
i18n (>= 1.6, < 2)
|
|
17
28
|
minitest (>= 5.1)
|
|
18
29
|
tzinfo (~> 2.0)
|
|
19
30
|
zeitwerk (~> 2.3)
|
|
20
|
-
asana (0.10.3)
|
|
21
|
-
faraday (~> 1.0)
|
|
22
|
-
faraday_middleware (~> 1.0)
|
|
23
|
-
faraday_middleware-multi_json (~> 0.0)
|
|
24
|
-
oauth2 (~> 1.4)
|
|
25
31
|
ast (2.4.2)
|
|
26
32
|
bump (0.10.0)
|
|
27
33
|
cache (0.4.1)
|
|
28
34
|
cache_method (0.2.7)
|
|
29
35
|
cache (>= 0.2.1)
|
|
30
36
|
childprocess (4.0.0)
|
|
37
|
+
coderay (1.1.3)
|
|
31
38
|
concurrent-ruby (1.1.9)
|
|
32
39
|
dalli (2.7.11)
|
|
33
40
|
docile (1.4.0)
|
|
34
|
-
faraday (1.
|
|
41
|
+
faraday (1.5.1)
|
|
35
42
|
faraday-em_http (~> 1.0)
|
|
36
43
|
faraday-em_synchrony (~> 1.0)
|
|
37
44
|
faraday-excon (~> 1.1)
|
|
45
|
+
faraday-httpclient (~> 1.0.1)
|
|
38
46
|
faraday-net_http (~> 1.0)
|
|
39
47
|
faraday-net_http_persistent (~> 1.1)
|
|
48
|
+
faraday-patron (~> 1.0)
|
|
40
49
|
multipart-post (>= 1.2, < 3)
|
|
41
50
|
ruby2_keywords (>= 0.0.4)
|
|
42
51
|
faraday-em_http (1.0.0)
|
|
43
52
|
faraday-em_synchrony (1.0.0)
|
|
44
53
|
faraday-excon (1.1.0)
|
|
54
|
+
faraday-httpclient (1.0.1)
|
|
45
55
|
faraday-net_http (1.0.1)
|
|
46
|
-
faraday-net_http_persistent (1.
|
|
56
|
+
faraday-net_http_persistent (1.2.0)
|
|
57
|
+
faraday-patron (1.0.0)
|
|
47
58
|
faraday_middleware (1.0.0)
|
|
48
59
|
faraday (~> 1.0)
|
|
49
60
|
faraday_middleware-multi_json (0.0.6)
|
|
50
61
|
faraday_middleware
|
|
51
62
|
multi_json
|
|
52
|
-
gli (2.20.
|
|
63
|
+
gli (2.20.1)
|
|
53
64
|
i18n (1.8.10)
|
|
54
65
|
concurrent-ruby (~> 1.0)
|
|
55
66
|
imagen (0.1.8)
|
|
56
67
|
parser (>= 2.5, != 2.5.1.1)
|
|
57
68
|
iniparse (1.5.0)
|
|
58
69
|
jwt (2.2.3)
|
|
70
|
+
method_source (1.0.0)
|
|
59
71
|
minitest (5.14.4)
|
|
60
72
|
minitest-profile (0.0.2)
|
|
61
73
|
mocha (1.12.0)
|
|
@@ -75,6 +87,9 @@ GEM
|
|
|
75
87
|
parallel (1.20.1)
|
|
76
88
|
parser (3.0.1.1)
|
|
77
89
|
ast (~> 2.4.1)
|
|
90
|
+
pry (0.14.1)
|
|
91
|
+
coderay (~> 1.1)
|
|
92
|
+
method_source (~> 1.0)
|
|
78
93
|
rack (2.2.3)
|
|
79
94
|
rainbow (3.0.0)
|
|
80
95
|
rake (13.0.3)
|
|
@@ -96,7 +111,7 @@ GEM
|
|
|
96
111
|
rubocop-rake (0.5.1)
|
|
97
112
|
rubocop
|
|
98
113
|
ruby-progressbar (1.11.0)
|
|
99
|
-
ruby2_keywords (0.0.
|
|
114
|
+
ruby2_keywords (0.0.5)
|
|
100
115
|
rugged (1.1.0)
|
|
101
116
|
simplecov (0.21.2)
|
|
102
117
|
docile (~> 1.1)
|
|
@@ -121,19 +136,21 @@ PLATFORMS
|
|
|
121
136
|
x86_64-linux
|
|
122
137
|
|
|
123
138
|
DEPENDENCIES
|
|
139
|
+
asana!
|
|
124
140
|
bump
|
|
125
141
|
bundler
|
|
126
142
|
checkoff!
|
|
127
143
|
minitest-profile
|
|
128
144
|
mocha
|
|
129
145
|
overcommit (>= 0.58.0)
|
|
146
|
+
pry
|
|
130
147
|
rake (~> 13.0)
|
|
131
148
|
rubocop
|
|
132
149
|
rubocop-minitest
|
|
133
150
|
rubocop-rake
|
|
134
|
-
simplecov
|
|
151
|
+
simplecov (>= 0.18.0)
|
|
135
152
|
simplecov-lcov
|
|
136
153
|
undercover
|
|
137
154
|
|
|
138
155
|
BUNDLED WITH
|
|
139
|
-
2.2.
|
|
156
|
+
2.2.24
|
data/README.md
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
[](https://circleci.com/gh/apiology/checkoff)
|
|
4
|
-
[](https://travis-ci.org/apiology/checkoff)
|
|
1
|
+
[](https://circleci.com/gh/apiology/checkoff)
|
|
5
2
|
|
|
6
3
|
Command-line and gem client for Asana (unofficial)
|
|
7
4
|
|
|
@@ -12,7 +9,7 @@ $ checkoff --help
|
|
|
12
9
|
View tasks:
|
|
13
10
|
checkoff view workspace project [section]
|
|
14
11
|
|
|
15
|
-
'project' can be set to a project name, or :my_tasks
|
|
12
|
+
'project' can be set to a project name, or :my_tasks for your user task list.
|
|
16
13
|
```
|
|
17
14
|
|
|
18
15
|
Let's say we have a project like this:
|
data/checkoff.gemspec
CHANGED
|
@@ -15,8 +15,10 @@ Gem::Specification.new do |spec|
|
|
|
15
15
|
spec.license = 'MIT license'
|
|
16
16
|
spec.required_ruby_version = '>= 2.6'
|
|
17
17
|
|
|
18
|
-
spec.files =
|
|
19
|
-
|
|
18
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
19
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
20
|
+
f.match(%r{^(test|spec|features)/})
|
|
21
|
+
end
|
|
20
22
|
end
|
|
21
23
|
spec.bindir = 'exe'
|
|
22
24
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
@@ -36,11 +38,13 @@ Gem::Specification.new do |spec|
|
|
|
36
38
|
# magit doesn't seem to want to use the bundled version at the moment,
|
|
37
39
|
# so let's favor the more recent version...
|
|
38
40
|
spec.add_development_dependency 'overcommit', ['>=0.58.0']
|
|
41
|
+
spec.add_development_dependency 'pry'
|
|
39
42
|
spec.add_development_dependency 'rake', '~> 13.0'
|
|
40
43
|
spec.add_development_dependency 'rubocop'
|
|
41
44
|
spec.add_development_dependency 'rubocop-minitest'
|
|
42
45
|
spec.add_development_dependency 'rubocop-rake'
|
|
43
|
-
|
|
46
|
+
# ensure version with branch coverage
|
|
47
|
+
spec.add_development_dependency 'simplecov', ['>=0.18.0']
|
|
44
48
|
spec.add_development_dependency 'simplecov-lcov'
|
|
45
49
|
spec.add_development_dependency 'undercover'
|
|
46
50
|
end
|
data/coverage/.last_run.json
CHANGED
data/fix.sh
CHANGED
|
@@ -95,7 +95,7 @@ ensure_ruby_build_requirements() {
|
|
|
95
95
|
# been release here: https://www.ruby-lang.org/en/downloads/
|
|
96
96
|
ensure_ruby_versions() {
|
|
97
97
|
# You can find out which feature versions are still supported / have
|
|
98
|
-
# been release here: https://www.
|
|
98
|
+
# been release here: https://www.ruby-lang.org/en/downloads/
|
|
99
99
|
ruby_versions="$(latest_ruby_version 2.6)"
|
|
100
100
|
|
|
101
101
|
echo "Latest Ruby versions: ${ruby_versions}"
|
|
@@ -227,6 +227,8 @@ ensure_python_build_requirements() {
|
|
|
227
227
|
ensure_dev_library openssl/ssl.h openssl libssl-dev
|
|
228
228
|
ensure_dev_library ffi.h libffi libffi-dev
|
|
229
229
|
ensure_dev_library sqlite3.h sqlite3 libsqlite3-dev
|
|
230
|
+
ensure_dev_library lzma.h xz liblzma-dev
|
|
231
|
+
ensure_dev_library readline.h readline libreadline-dev
|
|
230
232
|
}
|
|
231
233
|
|
|
232
234
|
# You can find out which feature versions are still supported / have
|
|
@@ -234,7 +236,7 @@ ensure_python_build_requirements() {
|
|
|
234
236
|
ensure_python_versions() {
|
|
235
237
|
# You can find out which feature versions are still supported / have
|
|
236
238
|
# been release here: https://www.python.org/downloads/
|
|
237
|
-
python_versions="$(latest_python_version 3.
|
|
239
|
+
python_versions="$(latest_python_version 3.10)"
|
|
238
240
|
|
|
239
241
|
echo "Latest Python versions: ${python_versions}"
|
|
240
242
|
|
|
@@ -271,7 +273,7 @@ ensure_pyenv_virtualenvs() {
|
|
|
271
273
|
pyenv local "${virtualenv_name}" ${python_versions} mylibs
|
|
272
274
|
}
|
|
273
275
|
|
|
274
|
-
|
|
276
|
+
ensure_pip_and_wheel() {
|
|
275
277
|
# Make sure we have a pip with the 20.3 resolver, and after the
|
|
276
278
|
# initial bugfix release
|
|
277
279
|
major_pip_version=$(pip --version | cut -d' ' -f2 | cut -d '.' -f 1)
|
|
@@ -279,6 +281,8 @@ ensure_pip() {
|
|
|
279
281
|
then
|
|
280
282
|
pip install 'pip>=20.3.1'
|
|
281
283
|
fi
|
|
284
|
+
# wheel is helpful for being able to cache long package builds
|
|
285
|
+
pip show wheel >/dev/null 2>&1 || pip install wheel
|
|
282
286
|
}
|
|
283
287
|
|
|
284
288
|
ensure_python_requirements() {
|
|
@@ -325,7 +329,7 @@ ensure_python_versions
|
|
|
325
329
|
|
|
326
330
|
ensure_pyenv_virtualenvs
|
|
327
331
|
|
|
328
|
-
|
|
332
|
+
ensure_pip_and_wheel
|
|
329
333
|
|
|
330
334
|
ensure_python_requirements
|
|
331
335
|
|
data/lib/checkoff/cli.rb
CHANGED
|
@@ -12,6 +12,112 @@ require_relative 'tasks'
|
|
|
12
12
|
require_relative 'sections'
|
|
13
13
|
|
|
14
14
|
module Checkoff
|
|
15
|
+
# Move tasks from one place to another
|
|
16
|
+
class MvSubcommand
|
|
17
|
+
def validate_and_assign_from_location(from_workspace_arg, from_project_arg, from_section_arg)
|
|
18
|
+
if from_workspace_arg == :default_workspace
|
|
19
|
+
# Figure out what to do here - we accept a default
|
|
20
|
+
# workspace gid and default workspace_gid arguments elsewhere.
|
|
21
|
+
# however, there are undefaulted workspace_name arguments as
|
|
22
|
+
# well...
|
|
23
|
+
raise NotImplementedError, 'Not implemented: Teach me how to look up default workspace name'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
@from_workspace_name = from_workspace_arg
|
|
27
|
+
@from_project_name = project_arg_to_name(from_project_arg)
|
|
28
|
+
@from_section_name = from_section_arg
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def create_to_project_name(to_project_arg)
|
|
32
|
+
if to_project_arg == :source_project
|
|
33
|
+
from_project_name
|
|
34
|
+
else
|
|
35
|
+
project_arg_to_name(to_project_arg)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def create_to_section_name(to_section_arg)
|
|
40
|
+
if to_section_arg == :source_section
|
|
41
|
+
from_section_name
|
|
42
|
+
else
|
|
43
|
+
to_section_arg
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def validate_and_assign_to_location(to_workspace_arg, to_project_arg, to_section_arg)
|
|
48
|
+
@to_workspace_name = to_workspace_arg
|
|
49
|
+
@to_workspace_name = from_workspace_name if to_workspace_arg == :source_workspace
|
|
50
|
+
@to_project_name = create_to_project_name(to_project_arg)
|
|
51
|
+
@to_section_name = create_to_section_name(to_section_arg)
|
|
52
|
+
|
|
53
|
+
return unless from_workspace_name != to_workspace_name
|
|
54
|
+
|
|
55
|
+
raise NotImplementedError, 'Not implemented: Teach me how to move tasks between workspaces'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def initialize(from_workspace_arg:,
|
|
59
|
+
from_project_arg:,
|
|
60
|
+
from_section_arg:,
|
|
61
|
+
to_workspace_arg:,
|
|
62
|
+
to_project_arg:,
|
|
63
|
+
to_section_arg:,
|
|
64
|
+
config: Checkoff::ConfigLoader.load(:asana),
|
|
65
|
+
projects: Checkoff::Projects.new(config: config),
|
|
66
|
+
sections: Checkoff::Sections.new(config: config),
|
|
67
|
+
logger: $stderr)
|
|
68
|
+
validate_and_assign_from_location(from_workspace_arg, from_project_arg, from_section_arg)
|
|
69
|
+
validate_and_assign_to_location(to_workspace_arg, to_project_arg, to_section_arg)
|
|
70
|
+
|
|
71
|
+
@projects = projects
|
|
72
|
+
@sections = sections
|
|
73
|
+
@logger = logger
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def move_tasks(tasks, to_project, to_section)
|
|
77
|
+
tasks.each do |task|
|
|
78
|
+
# a. check if already in correct project and section (TODO)
|
|
79
|
+
# b. if not, put it there
|
|
80
|
+
@logger.puts "Moving #{task.name} to #{to_section.name}..."
|
|
81
|
+
task.add_project(project: to_project.gid, section: to_section.gid)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def fetch_tasks(from_workspace_name, from_project_name, from_section_name)
|
|
86
|
+
if from_section_name == :all_sections
|
|
87
|
+
raise NotImplementedError, 'Not implemented: Teach me how to move all sections of a project'
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
sections.tasks(from_workspace_name, from_project_name, from_section_name)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def run
|
|
94
|
+
# 0. Look up project and section gids
|
|
95
|
+
to_project = projects.project_or_raise(to_workspace_name, to_project_name)
|
|
96
|
+
to_section = sections.section_or_raise(to_workspace_name, to_project_name, to_section_name)
|
|
97
|
+
|
|
98
|
+
# 1. Get list of tasks which match
|
|
99
|
+
tasks = fetch_tasks(from_workspace_name, from_project_name, from_section_name)
|
|
100
|
+
# 2. for each task,
|
|
101
|
+
move_tasks(tasks, to_project, to_section)
|
|
102
|
+
# 3. tell the user we're done'
|
|
103
|
+
@logger.puts 'Done moving tasks'
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
attr_reader :from_workspace_name, :from_project_name, :from_section_name,
|
|
109
|
+
:to_workspace_name, :to_project_name, :to_section_name,
|
|
110
|
+
:projects, :sections
|
|
111
|
+
|
|
112
|
+
def project_arg_to_name(project_arg)
|
|
113
|
+
if project_arg.start_with? ':'
|
|
114
|
+
project_arg[1..].to_sym
|
|
115
|
+
else
|
|
116
|
+
project_arg
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
15
121
|
# CLI subcommand that shows tasks in JSON form
|
|
16
122
|
class ViewSubcommand
|
|
17
123
|
def initialize(workspace_name, project_name, section_name,
|
|
@@ -151,5 +257,50 @@ module Checkoff
|
|
|
151
257
|
puts ViewSubcommand.new(workspace_name, project_name, section_name, task_name).run
|
|
152
258
|
end
|
|
153
259
|
end
|
|
260
|
+
|
|
261
|
+
desc 'Move tasks from one section to another within a project'
|
|
262
|
+
|
|
263
|
+
# rubocop:disable Metrics/BlockLength
|
|
264
|
+
command :mv do |c|
|
|
265
|
+
c.flag :from_workspace,
|
|
266
|
+
type: String,
|
|
267
|
+
default_value: :default_workspace,
|
|
268
|
+
desc: 'Workspace to move tasks from'
|
|
269
|
+
c.flag :from_project,
|
|
270
|
+
type: String,
|
|
271
|
+
required: true,
|
|
272
|
+
desc: 'Project to move tasks from'
|
|
273
|
+
c.flag :from_section,
|
|
274
|
+
type: String,
|
|
275
|
+
default_value: :all_sections,
|
|
276
|
+
desc: 'Section to move tasks from'
|
|
277
|
+
c.flag :to_workspace,
|
|
278
|
+
type: String,
|
|
279
|
+
default_value: :source_workspace,
|
|
280
|
+
desc: 'Workspace to move tasks to'
|
|
281
|
+
c.flag :to_project,
|
|
282
|
+
type: String,
|
|
283
|
+
default_value: :source_project,
|
|
284
|
+
desc: 'Section to move tasks to'
|
|
285
|
+
c.flag :to_section,
|
|
286
|
+
type: String,
|
|
287
|
+
default_value: :source_section,
|
|
288
|
+
desc: 'Section to move tasks to'
|
|
289
|
+
c.action do |_global_options, options, _args|
|
|
290
|
+
from_workspace = options.fetch('from_workspace')
|
|
291
|
+
from_project = options.fetch('from_project')
|
|
292
|
+
from_section = options.fetch('from_section')
|
|
293
|
+
to_workspace = options.fetch('to_workspace')
|
|
294
|
+
to_project = options.fetch('to_project')
|
|
295
|
+
to_section = options.fetch('to_section')
|
|
296
|
+
MvSubcommand.new(from_workspace_arg: from_workspace,
|
|
297
|
+
from_project_arg: from_project,
|
|
298
|
+
from_section_arg: from_section,
|
|
299
|
+
to_workspace_arg: to_workspace,
|
|
300
|
+
to_project_arg: to_project,
|
|
301
|
+
to_section_arg: to_section).run
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
# rubocop:enable Metrics/BlockLength
|
|
154
305
|
end
|
|
155
306
|
end
|
data/lib/checkoff/projects.rb
CHANGED
|
@@ -35,6 +35,17 @@ module Checkoff
|
|
|
35
35
|
@workspaces.client
|
|
36
36
|
end
|
|
37
37
|
|
|
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
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
38
49
|
# pulls an Asana API project class given a name
|
|
39
50
|
def project(workspace_name, project_name)
|
|
40
51
|
if project_name.is_a?(Symbol) && project_name.to_s.start_with?('my_tasks')
|
|
@@ -48,6 +59,14 @@ module Checkoff
|
|
|
48
59
|
end
|
|
49
60
|
cache_method :project, LONG_CACHE_TIME
|
|
50
61
|
|
|
62
|
+
def project_or_raise(workspace_name, project_name)
|
|
63
|
+
project = project(workspace_name, project_name)
|
|
64
|
+
raise "Could not find project #{project_name} under workspace #{workspace_name}." if project.nil?
|
|
65
|
+
|
|
66
|
+
project
|
|
67
|
+
end
|
|
68
|
+
cache_method :project_or_raise, LONG_CACHE_TIME
|
|
69
|
+
|
|
51
70
|
# find uncompleted tasks in a list
|
|
52
71
|
def active_tasks(tasks)
|
|
53
72
|
tasks.select { |task| task.completed_at.nil? }
|
|
@@ -84,15 +103,6 @@ module Checkoff
|
|
|
84
103
|
gid = result.gid
|
|
85
104
|
projects.find_by_id(gid)
|
|
86
105
|
end
|
|
87
|
-
|
|
88
|
-
def task_options
|
|
89
|
-
{
|
|
90
|
-
per_page: 100,
|
|
91
|
-
options: {
|
|
92
|
-
fields: %w[name completed_at due_at due_on assignee_status tags
|
|
93
|
-
memberships.project.gid memberships.section.name dependencies],
|
|
94
|
-
},
|
|
95
|
-
}
|
|
96
|
-
end
|
|
106
|
+
cache_method :my_tasks, LONG_CACHE_TIME
|
|
97
107
|
end
|
|
98
108
|
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,118 +22,84 @@ module Checkoff
|
|
|
23
22
|
@time = time
|
|
24
23
|
end
|
|
25
24
|
|
|
26
|
-
#
|
|
27
|
-
def
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
file_task_by_section(by_section, task, project_gid)
|
|
31
|
-
end
|
|
32
|
-
by_section
|
|
33
|
-
end
|
|
34
|
-
cache_method :by_section, LONG_CACHE_TIME
|
|
35
|
-
|
|
36
|
-
# Given a project object, pull all tasks, then provide a Hash of
|
|
37
|
-
# tasks with section name -> task list of the uncompleted tasks
|
|
38
|
-
def tasks_by_section_for_project(project)
|
|
39
|
-
raw_tasks = projects.tasks_from_project(project)
|
|
40
|
-
active_tasks = projects.active_tasks(raw_tasks)
|
|
41
|
-
by_section(active_tasks, project.gid)
|
|
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)
|
|
42
29
|
end
|
|
43
30
|
|
|
44
31
|
# Given a workspace name and project name, then provide a Hash of
|
|
45
32
|
# tasks with section name -> task list of the uncompleted tasks
|
|
46
33
|
def tasks_by_section(workspace_name, project_name)
|
|
47
34
|
project = project_or_raise(workspace_name, project_name)
|
|
48
|
-
|
|
49
|
-
when :my_tasks, :my_tasks_new, :my_tasks_today, :my_tasks_upcoming
|
|
50
|
-
user_task_list_by_section(workspace_name, project_name)
|
|
51
|
-
else
|
|
52
|
-
tasks_by_section_for_project(project)
|
|
53
|
-
end
|
|
35
|
+
tasks_by_section_for_project(project)
|
|
54
36
|
end
|
|
55
37
|
cache_method :tasks_by_section, SHORT_CACHE_TIME
|
|
56
38
|
|
|
57
39
|
# XXX: Rename to section_tasks
|
|
58
40
|
#
|
|
59
41
|
# Pulls task objects from a specified section
|
|
60
|
-
def tasks(workspace_name, project_name, section_name
|
|
61
|
-
|
|
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
|
+
options[:options][:fields] += extra_fields
|
|
48
|
+
options[:completed_since] = '9999-12-01' if only_uncompleted
|
|
49
|
+
client.tasks.get_tasks(section: section.gid,
|
|
50
|
+
**options)
|
|
62
51
|
end
|
|
63
52
|
cache_method :tasks, SHORT_CACHE_TIME
|
|
64
53
|
|
|
65
54
|
# Pulls just names of tasks from a given section.
|
|
66
55
|
def section_task_names(workspace_name, project_name, section_name)
|
|
67
56
|
tasks = tasks(workspace_name, project_name, section_name)
|
|
68
|
-
if tasks.nil?
|
|
69
|
-
by_section = tasks_by_section(workspace_name, project_name)
|
|
70
|
-
desc = "#{workspace_name} | #{project_name} | #{section_name}"
|
|
71
|
-
raise "Could not find task names for #{desc}. " \
|
|
72
|
-
"Valid sections: #{by_section.keys}"
|
|
73
|
-
end
|
|
74
57
|
tasks.map(&:name)
|
|
75
58
|
end
|
|
76
59
|
cache_method :section_task_names, SHORT_CACHE_TIME
|
|
77
60
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
true # set a due date if you don't want to do this now
|
|
61
|
+
def section_or_raise(workspace_name, project_name, section_name)
|
|
62
|
+
section = section(workspace_name, project_name, section_name)
|
|
63
|
+
if section.nil?
|
|
64
|
+
valid_sections = sections_or_raise(workspace_name, project_name).map(&:name)
|
|
65
|
+
|
|
66
|
+
raise "Could not find section #{section_name} under project #{project_name} " \
|
|
67
|
+
"under workspace #{workspace_name}. Valid sections: #{valid_sections}"
|
|
86
68
|
end
|
|
69
|
+
section
|
|
87
70
|
end
|
|
71
|
+
cache_method :section_or_raise, LONG_CACHE_TIME
|
|
88
72
|
|
|
89
73
|
private
|
|
90
74
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
def
|
|
94
|
-
membership = task.memberships.find { |m| m['project']['gid'] == project_gid }
|
|
95
|
-
raise "Could not find task in project_gid #{project_gid}: #{task}" if membership.nil?
|
|
96
|
-
|
|
97
|
-
current_section = membership['section']['name']
|
|
98
|
-
current_section = nil if current_section == '(no section)'
|
|
99
|
-
by_section[current_section] ||= []
|
|
100
|
-
by_section[current_section] << task
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def legacy_tasks_by_section_for_project(project)
|
|
75
|
+
# Given a project object, pull all tasks, then provide a Hash of
|
|
76
|
+
# tasks with section name -> task list of the uncompleted tasks
|
|
77
|
+
def tasks_by_section_for_project(project)
|
|
104
78
|
raw_tasks = projects.tasks_from_project(project)
|
|
105
79
|
active_tasks = projects.active_tasks(raw_tasks)
|
|
106
|
-
|
|
80
|
+
by_section(active_tasks, project.gid)
|
|
107
81
|
end
|
|
108
82
|
|
|
109
|
-
|
|
110
|
-
if task.name =~ /:$/
|
|
111
|
-
current_section = task.name
|
|
112
|
-
by_section[current_section] = []
|
|
113
|
-
else
|
|
114
|
-
by_section[current_section] ||= []
|
|
115
|
-
by_section[current_section] << task
|
|
116
|
-
end
|
|
117
|
-
[current_section, by_section]
|
|
118
|
-
end
|
|
83
|
+
def_delegators :@projects, :client
|
|
119
84
|
|
|
120
|
-
|
|
121
|
-
|
|
85
|
+
# Given a list of tasks, pull a Hash of tasks with section name -> task list
|
|
86
|
+
def by_section(tasks, project_gid)
|
|
122
87
|
by_section = {}
|
|
123
88
|
tasks.each do |task|
|
|
124
|
-
|
|
125
|
-
legacy_file_task_by_section(current_section, by_section, task)
|
|
89
|
+
file_task_by_section(by_section, task, project_gid)
|
|
126
90
|
end
|
|
127
91
|
by_section
|
|
128
92
|
end
|
|
93
|
+
cache_method :by_section, LONG_CACHE_TIME
|
|
129
94
|
|
|
130
|
-
def
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
95
|
+
def file_task_by_section(by_section, task, project_gid)
|
|
96
|
+
membership = task.memberships.find { |m| m['project']['gid'] == project_gid }
|
|
97
|
+
raise "Could not find task in project_gid #{project_gid}: #{task}" if membership.nil?
|
|
98
|
+
|
|
99
|
+
current_section = membership['section']['name']
|
|
100
|
+
current_section = nil if current_section == '(no section)'
|
|
101
|
+
by_section[current_section] ||= []
|
|
102
|
+
by_section[current_section] << task
|
|
138
103
|
end
|
|
139
104
|
|
|
140
105
|
def project_or_raise(workspace_name, project_name)
|
|
@@ -146,49 +111,9 @@ module Checkoff
|
|
|
146
111
|
project
|
|
147
112
|
end
|
|
148
113
|
|
|
149
|
-
def
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
raise NotImplementedError, 'Section-based user task lists not yet supported'
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
ASSIGNEE_STATUS_BY_PROJECT_NAME = {
|
|
156
|
-
my_tasks_new: 'inbox',
|
|
157
|
-
my_tasks_today: 'today',
|
|
158
|
-
my_tasks_upcoming: 'upcoming',
|
|
159
|
-
}.freeze
|
|
160
|
-
|
|
161
|
-
def user_task_list_by_section(workspace_name, project_name)
|
|
162
|
-
verify_legacy_user_task_list!(workspace_name)
|
|
163
|
-
|
|
164
|
-
project = projects.project(workspace_name, project_name)
|
|
165
|
-
if project_name == :my_tasks
|
|
166
|
-
legacy_tasks_by_section_for_project(project)
|
|
167
|
-
else
|
|
168
|
-
legacy_tasks_by_section_for_project_and_assignee_status(project,
|
|
169
|
-
ASSIGNEE_STATUS_BY_PROJECT_NAME.fetch(project_name))
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def user_task_list_migrated_to_real_sections?(workspace_name)
|
|
174
|
-
workspace = workspaces.workspace_by_name(workspace_name)
|
|
175
|
-
result = client.user_task_lists.get_user_task_list_for_user(user_gid: 'me',
|
|
176
|
-
workspace: workspace.gid)
|
|
177
|
-
result.migration_status != 'not_migrated'
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
def project_task_names(workspace_name, project_name)
|
|
181
|
-
by_section = tasks_by_section(workspace_name, project_name)
|
|
182
|
-
by_section.flat_map do |section_name, tasks|
|
|
183
|
-
task_names = tasks.map(&:name)
|
|
184
|
-
if section_name.nil?
|
|
185
|
-
task_names
|
|
186
|
-
else
|
|
187
|
-
[section_name, task_names]
|
|
188
|
-
end
|
|
189
|
-
end
|
|
114
|
+
def section(workspace_name, project_name, section_name)
|
|
115
|
+
sections = sections_or_raise(workspace_name, project_name)
|
|
116
|
+
sections.find { |section| section.name.chomp(':') == section_name.chomp(':') }
|
|
190
117
|
end
|
|
191
|
-
cache_method :project_task_names, SHORT_CACHE_TIME
|
|
192
118
|
end
|
|
193
|
-
# rubocop:enable Metrics/ClassLength
|
|
194
119
|
end
|
data/lib/checkoff/subtasks.rb
CHANGED
|
@@ -11,6 +11,11 @@ module Checkoff
|
|
|
11
11
|
|
|
12
12
|
extend Forwardable
|
|
13
13
|
|
|
14
|
+
def initialize(config: Checkoff::ConfigLoader.load(:asana),
|
|
15
|
+
projects: Checkoff::Projects.new(config: config))
|
|
16
|
+
@projects = projects
|
|
17
|
+
end
|
|
18
|
+
|
|
14
19
|
# pulls a Hash of subtasks broken out by section
|
|
15
20
|
def by_section(tasks)
|
|
16
21
|
current_section = nil
|
|
@@ -30,6 +35,8 @@ module Checkoff
|
|
|
30
35
|
|
|
31
36
|
private
|
|
32
37
|
|
|
38
|
+
attr_reader :projects
|
|
39
|
+
|
|
33
40
|
def file_task_by_section(current_section, by_section, task)
|
|
34
41
|
if task.name =~ /:$/
|
|
35
42
|
current_section = task.name
|
data/lib/checkoff/tasks.rb
CHANGED
|
@@ -31,7 +31,8 @@ module Checkoff
|
|
|
31
31
|
projects.tasks_from_project(project,
|
|
32
32
|
only_uncompleted: only_uncompleted)
|
|
33
33
|
else
|
|
34
|
-
@sections.tasks(workspace_name, project_name, section_name
|
|
34
|
+
@sections.tasks(workspace_name, project_name, section_name,
|
|
35
|
+
only_uncompleted: only_uncompleted)
|
|
35
36
|
end
|
|
36
37
|
tasks.find { |task| task.name == task_name }
|
|
37
38
|
end
|
|
@@ -54,10 +55,6 @@ module Checkoff
|
|
|
54
55
|
@projects ||= @sections.projects
|
|
55
56
|
end
|
|
56
57
|
|
|
57
|
-
def tasks_minus_sections(tasks)
|
|
58
|
-
@sections.by_section(tasks).values.flatten
|
|
59
|
-
end
|
|
60
|
-
|
|
61
58
|
def default_assignee_gid
|
|
62
59
|
@config.fetch(:default_assignee_gid)
|
|
63
60
|
end
|
data/lib/checkoff/version.rb
CHANGED
data/rakelib/default.rake
CHANGED
data/rakelib/repl.rake
ADDED
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.13.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-31 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -150,6 +150,20 @@ dependencies:
|
|
|
150
150
|
- - ">="
|
|
151
151
|
- !ruby/object:Gem::Version
|
|
152
152
|
version: 0.58.0
|
|
153
|
+
- !ruby/object:Gem::Dependency
|
|
154
|
+
name: pry
|
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
|
156
|
+
requirements:
|
|
157
|
+
- - ">="
|
|
158
|
+
- !ruby/object:Gem::Version
|
|
159
|
+
version: '0'
|
|
160
|
+
type: :development
|
|
161
|
+
prerelease: false
|
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
+
requirements:
|
|
164
|
+
- - ">="
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: '0'
|
|
153
167
|
- !ruby/object:Gem::Dependency
|
|
154
168
|
name: rake
|
|
155
169
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -212,14 +226,14 @@ dependencies:
|
|
|
212
226
|
requirements:
|
|
213
227
|
- - ">="
|
|
214
228
|
- !ruby/object:Gem::Version
|
|
215
|
-
version:
|
|
229
|
+
version: 0.18.0
|
|
216
230
|
type: :development
|
|
217
231
|
prerelease: false
|
|
218
232
|
version_requirements: !ruby/object:Gem::Requirement
|
|
219
233
|
requirements:
|
|
220
234
|
- - ">="
|
|
221
235
|
- !ruby/object:Gem::Version
|
|
222
|
-
version:
|
|
236
|
+
version: 0.18.0
|
|
223
237
|
- !ruby/object:Gem::Dependency
|
|
224
238
|
name: simplecov-lcov
|
|
225
239
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -311,11 +325,13 @@ files:
|
|
|
311
325
|
- metrics/shellcheck_high_water_mark
|
|
312
326
|
- rakelib/citest.rake
|
|
313
327
|
- rakelib/clear_metrics.rake
|
|
328
|
+
- rakelib/console.rake
|
|
314
329
|
- rakelib/default.rake
|
|
315
330
|
- rakelib/gem_tasks.rake
|
|
316
331
|
- rakelib/localtest.rake
|
|
317
332
|
- rakelib/overcommit.rake
|
|
318
333
|
- rakelib/quality.rake
|
|
334
|
+
- rakelib/repl.rake
|
|
319
335
|
- rakelib/test.rake
|
|
320
336
|
- rakelib/undercover.rake
|
|
321
337
|
- requirements_dev.txt
|