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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f4eeb7148c4b835088d3fedc674bf3195b92c9889bfe935a745b50e0b7228c65
4
- data.tar.gz: 2a48471aae38c5ed10bed024968bb51ccd772f1e1efe3acd70b7a4c92bbb59ae
3
+ metadata.gz: 8a996aad5215c1625924296ca8c851cb3293968c74e6bc3da78dce7996fa8041
4
+ data.tar.gz: 574da8911c1c542f6334ed2baa670dd69a3e09828ec6a76d3d3fe78b5c3e9d7a
5
5
  SHA512:
6
- metadata.gz: cf4c95afee408b7937ea09c543c21e50b2855eba0393d9ac542e8d67f4eeb9c02c67b82cb23b7b00a81ca09f9678fafbe294baf947d3fcf1418ecd0208947298
7
- data.tar.gz: 3ec3e6ef6b0a6c1583a96326cd0fa545b500f61e21ddc97624aeab31dadcd9125928d2bc78bbb8e7ec2827837d700ab651d5bb26a201ddf2a14acb862c246777
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-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.1)
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.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.69,
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
@@ -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
@@ -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
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Checkoff
4
4
  # Version of library
5
- VERSION = '0.11.1'
5
+ VERSION = '0.13.1'
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.1
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-06-11 00:00:00.000000000 Z
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: '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