abt-cli 0.0.2 → 0.0.7

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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/bin/abt +4 -1
  3. data/lib/abt.rb +11 -0
  4. data/lib/abt/cli.rb +37 -32
  5. data/lib/abt/cli/dialogs.rb +18 -2
  6. data/lib/abt/cli/io.rb +23 -0
  7. data/lib/abt/docs.rb +57 -0
  8. data/lib/abt/{help → docs}/cli.rb +4 -4
  9. data/lib/abt/{help → docs}/markdown.rb +4 -4
  10. data/lib/abt/git_config.rb +55 -49
  11. data/lib/abt/helpers.rb +16 -0
  12. data/lib/abt/providers/asana.rb +9 -50
  13. data/lib/abt/providers/asana/api.rb +57 -0
  14. data/lib/abt/providers/asana/base_command.rb +14 -16
  15. data/lib/abt/providers/asana/commands/clear.rb +24 -0
  16. data/lib/abt/providers/asana/commands/clear_global.rb +24 -0
  17. data/lib/abt/providers/asana/commands/current.rb +77 -0
  18. data/lib/abt/providers/asana/commands/finalize.rb +71 -0
  19. data/lib/abt/providers/asana/commands/harvest_time_entry_data.rb +50 -0
  20. data/lib/abt/providers/asana/commands/init.rb +70 -0
  21. data/lib/abt/providers/asana/commands/pick.rb +55 -0
  22. data/lib/abt/providers/asana/commands/projects.rb +39 -0
  23. data/lib/abt/providers/asana/commands/share.rb +29 -0
  24. data/lib/abt/providers/asana/commands/start.rb +105 -0
  25. data/lib/abt/providers/asana/commands/tasks.rb +40 -0
  26. data/lib/abt/providers/asana/configuration.rb +125 -0
  27. data/lib/abt/providers/harvest.rb +9 -42
  28. data/lib/abt/providers/harvest/api.rb +62 -0
  29. data/lib/abt/providers/harvest/base_command.rb +12 -16
  30. data/lib/abt/providers/harvest/commands/clear.rb +24 -0
  31. data/lib/abt/providers/harvest/commands/clear_global.rb +24 -0
  32. data/lib/abt/providers/harvest/commands/current.rb +83 -0
  33. data/lib/abt/providers/harvest/commands/init.rb +83 -0
  34. data/lib/abt/providers/harvest/commands/pick.rb +51 -0
  35. data/lib/abt/providers/harvest/commands/projects.rb +40 -0
  36. data/lib/abt/providers/harvest/commands/share.rb +29 -0
  37. data/lib/abt/providers/harvest/commands/start.rb +101 -0
  38. data/lib/abt/providers/harvest/commands/stop.rb +58 -0
  39. data/lib/abt/providers/harvest/commands/tasks.rb +45 -0
  40. data/lib/abt/providers/harvest/configuration.rb +91 -0
  41. data/lib/abt/version.rb +1 -1
  42. metadata +32 -26
  43. data/lib/abt/asana_client.rb +0 -53
  44. data/lib/abt/harvest_client.rb +0 -58
  45. data/lib/abt/help.rb +0 -56
  46. data/lib/abt/providers/asana/clear.rb +0 -24
  47. data/lib/abt/providers/asana/clear_global.rb +0 -24
  48. data/lib/abt/providers/asana/current.rb +0 -69
  49. data/lib/abt/providers/asana/harvest_link_entry_data.rb +0 -48
  50. data/lib/abt/providers/asana/init.rb +0 -62
  51. data/lib/abt/providers/asana/move.rb +0 -54
  52. data/lib/abt/providers/asana/pick_task.rb +0 -46
  53. data/lib/abt/providers/asana/projects.rb +0 -30
  54. data/lib/abt/providers/asana/start.rb +0 -22
  55. data/lib/abt/providers/asana/tasks.rb +0 -35
  56. data/lib/abt/providers/harvest/clear.rb +0 -24
  57. data/lib/abt/providers/harvest/clear_global.rb +0 -24
  58. data/lib/abt/providers/harvest/current.rb +0 -79
  59. data/lib/abt/providers/harvest/init.rb +0 -61
  60. data/lib/abt/providers/harvest/pick_task.rb +0 -45
  61. data/lib/abt/providers/harvest/projects.rb +0 -29
  62. data/lib/abt/providers/harvest/start.rb +0 -58
  63. data/lib/abt/providers/harvest/stop.rb +0 -51
  64. data/lib/abt/providers/harvest/tasks.rb +0 -36
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- module Providers
5
- class Asana
6
- class PickTask < BaseCommand
7
- def self.command
8
- 'pick-task asana[:<project-gid>]'
9
- end
10
-
11
- def self.description
12
- 'Pick task for current git repository'
13
- end
14
-
15
- def call
16
- warn project['name']
17
- task = cli.prompt_choice 'Select a task', tasks
18
-
19
- remember_project_gid(project_gid) # We might have gotten the project ID as an argument
20
- remember_task_gid(task['gid'])
21
-
22
- print_task(project, task)
23
- end
24
-
25
- private
26
-
27
- def project
28
- @project ||= Asana.client.get("projects/#{project_gid}")
29
- end
30
-
31
- def tasks
32
- @tasks ||= begin
33
- section = cli.prompt_choice 'Which section?', sections
34
- Asana.client.get_paged('tasks', section: section['gid'])
35
- end
36
- end
37
-
38
- def sections
39
- Asana.client.get_paged("projects/#{project_gid}/sections")
40
- rescue Abt::HttpError::HttpError
41
- []
42
- end
43
- end
44
- end
45
- end
46
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- module Providers
5
- class Asana
6
- class Projects < BaseCommand
7
- def self.command
8
- 'projects asana'
9
- end
10
-
11
- def self.description
12
- 'List all available projects - useful for piping into grep etc.'
13
- end
14
-
15
- def call
16
- projects.map do |project|
17
- print_project(project)
18
- end
19
- end
20
-
21
- private
22
-
23
- def projects
24
- @projects ||=
25
- Asana.client.get_paged('projects', workspace: Asana.workspace_gid, archived: false)
26
- end
27
- end
28
- end
29
- end
30
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- module Providers
5
- class Asana
6
- class Start < BaseCommand
7
- def self.command
8
- 'start asana[:<project-gid>/<task-gid>]'
9
- end
10
-
11
- def self.description
12
- 'Set current task and move it to a section (column) of your choice'
13
- end
14
-
15
- def call
16
- Current.new(arg_str: arg_str, cli: cli).call unless arg_str.nil?
17
- Move.new(arg_str: arg_str, cli: cli).call
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- module Providers
5
- class Asana
6
- class Tasks < BaseCommand
7
- def self.command
8
- 'tasks asana'
9
- end
10
-
11
- def self.description
12
- 'List available tasks on project - useful for piping into grep etc.'
13
- end
14
-
15
- def call
16
- tasks.each do |task|
17
- print_task(project, task)
18
- end
19
- end
20
-
21
- private
22
-
23
- def project
24
- @project ||= begin
25
- Asana.client.get("projects/#{project_gid}")
26
- end
27
- end
28
-
29
- def tasks
30
- @tasks ||= Asana.client.get_paged('tasks', project: project['gid'])
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- module Providers
5
- class Harvest
6
- class Clear
7
- def self.command
8
- 'clear harvest'
9
- end
10
-
11
- def self.description
12
- 'Clear project/task for current git repository'
13
- end
14
-
15
- def initialize(**); end
16
-
17
- def call
18
- warn 'Clearing Harvest project configuration'
19
- Harvest.clear
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- module Providers
5
- class Harvest
6
- class ClearGlobal
7
- def self.command
8
- 'clear-global harvest'
9
- end
10
-
11
- def self.description
12
- 'Clear all global configuration'
13
- end
14
-
15
- def initialize(**); end
16
-
17
- def call
18
- warn 'Clearing Harvest project configuration'
19
- Harvest.clear_global
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- module Providers
5
- class Harvest
6
- class Current < BaseCommand
7
- def self.command
8
- 'current harvest[:<project-id>[/<task-id>]]'
9
- end
10
-
11
- def self.description
12
- 'Get or set project and or task for current git repository'
13
- end
14
-
15
- def call
16
- if arg_str.nil?
17
- show_current_configuration
18
- else
19
- warn 'Updating configuration'
20
- update_configuration
21
- end
22
- end
23
-
24
- private
25
-
26
- def show_current_configuration
27
- if project_id.nil?
28
- warn 'No project selected'
29
- elsif task_id.nil?
30
- print_project(project)
31
- else
32
- print_task(project, task)
33
- end
34
- end
35
-
36
- def update_configuration
37
- ensure_project_is_valid!
38
- remember_project_id(project_id)
39
-
40
- if task_id.nil?
41
- print_project(project)
42
- remember_task_id(nil)
43
- else
44
- ensure_task_is_valid!
45
- remember_task_id(task_id)
46
-
47
- print_task(project, task)
48
- end
49
- end
50
-
51
- def ensure_project_is_valid!
52
- abort "Invalid project: #{project_id}" if project.nil?
53
- end
54
-
55
- def ensure_task_is_valid!
56
- abort "Invalid task: #{task_id}" if task.nil?
57
- end
58
-
59
- def project
60
- @project ||= Harvest.client.get("projects/#{project_id}")
61
- end
62
-
63
- def task
64
- project_task_assignments
65
- .map { |assignment| assignment['task'] }
66
- .find { |task| task['id'].to_s == task_id }
67
- end
68
-
69
- def project_task_assignments
70
- @project_task_assignments ||= begin
71
- Harvest.client.get_paged("projects/#{project_id}/task_assignments", is_active: true)
72
- rescue Abt::HttpError::HttpError # rubocop:disable Layout/RescueEnsureAlignment
73
- []
74
- end
75
- end
76
- end
77
- end
78
- end
79
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- module Providers
5
- class Harvest
6
- class Init < BaseCommand
7
- def self.command
8
- 'init harvest'
9
- end
10
-
11
- def self.description
12
- 'Pick Harvest project for current git repository'
13
- end
14
-
15
- def call
16
- warn 'Loading projects'
17
-
18
- projects # Load projects up front to make it obvious that searches are instant
19
- project = find_search_result
20
-
21
- remember_project_id(project['id'])
22
- remember_task_id(nil)
23
-
24
- print_project(project)
25
- end
26
-
27
- private
28
-
29
- def find_search_result
30
- loop do
31
- matches = matches_for_string cli.prompt('Enter search')
32
- if matches.empty?
33
- warn 'No matches'
34
- next
35
- end
36
-
37
- warn 'Showing the 10 first matches' if matches.size > 10
38
- choice = cli.prompt_choice 'Select a project', matches[0...10], true
39
- break choice unless choice.nil?
40
- end
41
- end
42
-
43
- def matches_for_string(string)
44
- search_string = sanitize_string(string)
45
-
46
- projects.select do |project|
47
- sanitize_string(project['name']).include?(search_string)
48
- end
49
- end
50
-
51
- def sanitize_string(string)
52
- string.downcase.gsub(/[^\w]/, '')
53
- end
54
-
55
- def projects
56
- @projects ||= Harvest.client.get_paged('projects')
57
- end
58
- end
59
- end
60
- end
61
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- module Providers
5
- class Harvest
6
- class PickTask < BaseCommand
7
- def self.command
8
- 'pick-task harvest[:<project-id>]'
9
- end
10
-
11
- def self.description
12
- 'Pick task for current git repository'
13
- end
14
-
15
- def call
16
- warn project['name']
17
- task = cli.prompt_choice 'Select a task', tasks
18
-
19
- remember_project_id(project_id) # We might have gotten the project ID as an argument
20
- remember_task_id(task['id'])
21
-
22
- print_task(project, task)
23
- end
24
-
25
- private
26
-
27
- def project
28
- @project ||= Harvest.client.get("projects/#{project_id}")
29
- end
30
-
31
- def tasks
32
- project_task_assignments.map { |assignment| assignment['task'] }
33
- end
34
-
35
- def project_task_assignments
36
- @project_task_assignments ||= begin
37
- Harvest.client.get_paged("projects/#{project_id}/task_assignments", is_active: true)
38
- rescue Abt::HttpError::HttpError # rubocop:disable Layout/RescueEnsureAlignment
39
- []
40
- end
41
- end
42
- end
43
- end
44
- end
45
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- module Providers
5
- class Harvest
6
- class Projects < BaseCommand
7
- def self.command
8
- 'projects harvest'
9
- end
10
-
11
- def self.description
12
- 'List all available projects - useful for piping into grep etc.'
13
- end
14
-
15
- def call
16
- projects.map do |project|
17
- print_project(project)
18
- end
19
- end
20
-
21
- private
22
-
23
- def projects
24
- @projects ||= Harvest.client.get_paged('projects', is_active: true)
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Abt
4
- module Providers
5
- class Harvest
6
- class Start < BaseCommand
7
- def self.command
8
- 'start harvest[:<project-id>/<task-id>]'
9
- end
10
-
11
- def self.description
12
- 'Start tracker for current or specified task. Add a relevant provider to link the time entry: E.g. `abt start harvest asana`' # rubocop:disable Layout/LineLength
13
- end
14
-
15
- def call
16
- Current.new(arg_str: arg_str, cli: cli).call unless arg_str.nil?
17
-
18
- abort('No task selected') if task_id.nil?
19
-
20
- create_time_entry
21
-
22
- warn 'Tracker successfully started'
23
- rescue Abt::HttpError::HttpError => e
24
- warn e
25
- abort 'Unable to start tracker'
26
- end
27
-
28
- private
29
-
30
- def create_time_entry
31
- body = Oj.dump({
32
- project_id: Abt::GitConfig.local('abt.harvest.projectId'),
33
- task_id: Abt::GitConfig.local('abt.harvest.taskId'),
34
- user_id: Harvest.user_id,
35
- spent_date: Date.today.iso8601
36
- }.merge(external_link_data), mode: :json)
37
- Harvest.client.post('time_entries', body)
38
- end
39
-
40
- def external_link_data
41
- @external_link_data ||= begin
42
- arg_strs = cli.args.join(' ')
43
- lines = `#{$PROGRAM_NAME} harvest-time-entry-data #{arg_strs}`.split("\n")
44
-
45
- return {} if lines.empty?
46
-
47
- # TODO: Make user choose which reference to use by printing the urls
48
- if lines.length > 1
49
- abort 'Multiple providers had harvest reference data, only one is supported at a time'
50
- end
51
-
52
- Oj.load(lines.first)
53
- end
54
- end
55
- end
56
- end
57
- end
58
- end