checkoff 0.11.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f9082aa251f94c438c29019a88db01de1732564c237bb3dff8d4aef6ce8eea6
4
- data.tar.gz: 90cc397c4381839ddc1ba55c0096e84e7baf735653d2aa46267a1e52dd684603
3
+ metadata.gz: d54f389a54d7a47d43ec3fb15e1281482d54b3d1fbfd7fe5622a75e2710ab8d9
4
+ data.tar.gz: c4696373fb23d4dd798803bafeb09c64eb04f7f68cf9c94edcf35ac37d60f02b
5
5
  SHA512:
6
- metadata.gz: 21438c7ac67f3e782a1fee1c7adae43baa13df126904668dc6784cfab27b57d4abea67380af34560ff834e6b6a00a1fcd50ce12f171781991c273cc8bd9ce10e
7
- data.tar.gz: 863441bc08bd83be385bb8b5806ad37a720a421cdfab44efcb9c31e528b649ee11a6e0d392401e4fb2931ff4c6f4a7934d2b652df4fb200c8286bc758c9ad7ba
6
+ metadata.gz: 87aefeff6e2b41c1aea50835e2552a6c44cbbdb3017f478a6f004de2052cc639d7bb887fb3613fecdf4429e1e87c541cbfa875d5ae4c008cf5ecd4d4fa90eb4f
7
+ data.tar.gz: 20de66650b388bc3f243a584efa2607637d46e8dfb51eb6ddd44b8bb15bafd9d71343e334c1a7af3f5417679d5da2d0a53f14cbf6221ead4b16cc3b89109f9c5
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-v4-{{ checksum "Gemfile.lock.deversioned" }}
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-v4-{{ checksum "Gemfile.lock.deversioned" }}
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.11.0)
15
+ checkoff (0.13.0)
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.3.2)
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.4.2)
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.1.0)
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.0)
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.4)
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.19
156
+ 2.2.24
data/README.md CHANGED
@@ -1,7 +1,4 @@
1
- Command-line and gem client for Asana (unofficial)
2
-
3
- [![Circle CI](https://circleci.com/gh/apiology/checkoff.svg?style=svg)](https://circleci.com/gh/apiology/checkoff)
4
- [![Travis CI](https://travis-ci.org/apiology/checkoff.svg?branch=main)](https://travis-ci.org/apiology/checkoff)
1
+ [![CircleCI](https://circleci.com/gh/apiology/checkoff.svg?style=svg)](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, :my_tasks_upcoming, :my_tasks_new, or :my_tasks_today
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 = `git ls-files -z`.split("\x0").reject do |f|
19
- f.match(%r{^(test|spec|features)/})
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
- spec.add_development_dependency 'simplecov'
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "result": {
3
- "line": 94.61,
4
- "branch": 71.42
3
+ "line": 98.57,
4
+ "branch": 91.66
5
5
  }
6
6
  }
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.python.org/downloads/
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.9)"
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
- ensure_pip() {
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
- ensure_pip
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
@@ -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
@@ -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
- # Given a list of tasks, pull a Hash of tasks with section name -> task list
27
- def by_section(tasks, project_gid)
28
- by_section = {}
29
- tasks.each do |task|
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
- case project_name
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
- tasks_by_section(workspace_name, project_name).fetch(section_name, [])
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
- # returns if a task's due field is at or before the current date/time
79
- def task_due?(task)
80
- if task.due_at
81
- Time.parse(task.due_at) <= time.now
82
- elsif task.due_on
83
- Date.parse(task.due_on) <= time.today
84
- else
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
- def_delegators :@projects, :client
92
-
93
- def file_task_by_section(by_section, task, project_gid)
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
- legacy_by_section(active_tasks)
80
+ by_section(active_tasks, project.gid)
107
81
  end
108
82
 
109
- def legacy_file_task_by_section(current_section, by_section, task)
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
- def legacy_by_section(tasks)
121
- current_section = nil
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
- current_section, by_section =
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 legacy_tasks_by_section_for_project_and_assignee_status(project,
131
- assignee_status)
132
- raw_tasks = projects.tasks_from_project(project)
133
- by_assignee_status =
134
- projects.active_tasks(raw_tasks)
135
- .group_by(&:assignee_status)
136
- active_tasks = by_assignee_status[assignee_status]
137
- legacy_by_section(active_tasks)
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 verify_legacy_user_task_list!(workspace_name)
150
- return unless user_task_list_migrated_to_real_sections?(workspace_name)
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
@@ -31,11 +31,20 @@ 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
38
39
 
40
+ def add_task(name,
41
+ workspace_gid: default_workspace_gid,
42
+ assignee_gid: default_assignee_gid)
43
+ @asana_task.create(client,
44
+ assignee: assignee_gid,
45
+ workspace: workspace_gid, name: name)
46
+ end
47
+
39
48
  private
40
49
 
41
50
  def client
@@ -46,18 +55,6 @@ module Checkoff
46
55
  @projects ||= @sections.projects
47
56
  end
48
57
 
49
- def tasks_minus_sections(tasks)
50
- @sections.by_section(tasks).values.flatten
51
- end
52
-
53
- def add_task(name,
54
- workspace_gid: default_workspace_gid,
55
- assignee_gid: default_assignee_gid)
56
- @asana_task.create(client,
57
- assignee: assignee_gid,
58
- workspace: workspace_gid, name: name)
59
- end
60
-
61
58
  def default_assignee_gid
62
59
  @config.fetch(:default_assignee_gid)
63
60
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Checkoff
4
4
  # Version of library
5
- VERSION = '0.11.0'
5
+ VERSION = '0.13.0'
6
6
  end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc 'Load up checkoff in pry'
4
+ task :console do |_t|
5
+ exec 'pry -I lib -r checkoff'
6
+ end
data/rakelib/default.rake CHANGED
@@ -1,3 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ desc 'Standard build'
3
4
  task default: :localtest
data/rakelib/repl.rake ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc 'Load up checkoff in pry'
4
+ task repl: [:console]
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.11.0
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vince Broz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-06-10 00:00:00.000000000 Z
11
+ date: 2021-07-30 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: '0'
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: '0'
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