tempest_time 0.7.4 → 1.0.0
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/.rubocop.yml +7 -1
- data/.rubocop_airbnb.yml +3 -0
- data/CONTRIBUTING.md +6 -0
- data/Gemfile +1 -1
- data/README.md +44 -5
- data/bin/tempest +1 -1
- data/lib/tempest_time/api/authorization.rb +3 -1
- data/lib/tempest_time/api/jira_api/authorization.rb +2 -0
- data/lib/tempest_time/api/jira_api/models/issue.rb +9 -5
- data/lib/tempest_time/api/jira_api/request.rb +3 -1
- data/lib/tempest_time/api/jira_api/requests/get_current_user.rb +0 -1
- data/lib/tempest_time/api/jira_api/requests/get_issue.rb +1 -1
- data/lib/tempest_time/api/jira_api/requests/get_user_issues.rb +3 -3
- data/lib/tempest_time/api/jira_api/response.rb +3 -1
- data/lib/tempest_time/api/jira_api/responses/get_current_user.rb +0 -1
- data/lib/tempest_time/api/jira_api/responses/get_issue.rb +1 -1
- data/lib/tempest_time/api/jira_api/responses/get_user_issues.rb +1 -1
- data/lib/tempest_time/api/request.rb +3 -3
- data/lib/tempest_time/api/response.rb +3 -1
- data/lib/tempest_time/api/tempo_api/models/worklog.rb +1 -1
- data/lib/tempest_time/api/tempo_api/request.rb +1 -1
- data/lib/tempest_time/api/tempo_api/requests/create_worklog.rb +2 -2
- data/lib/tempest_time/api/tempo_api/requests/delete_worklog.rb +1 -1
- data/lib/tempest_time/api/tempo_api/requests/get_worklog.rb +1 -1
- data/lib/tempest_time/api/tempo_api/requests/list_worklogs.rb +2 -3
- data/lib/tempest_time/api/tempo_api/requests/submit_timesheet.rb +3 -3
- data/lib/tempest_time/api/tempo_api/response.rb +1 -1
- data/lib/tempest_time/api/tempo_api/responses/create_worklog.rb +1 -1
- data/lib/tempest_time/api/tempo_api/responses/delete_worklog.rb +1 -1
- data/lib/tempest_time/api/tempo_api/responses/get_worklog.rb +4 -2
- data/lib/tempest_time/api/tempo_api/responses/list_worklogs.rb +4 -6
- data/lib/tempest_time/api/tempo_api/responses/submit_timesheet.rb +1 -1
- data/lib/tempest_time/cli.rb +5 -2
- data/lib/tempest_time/command.rb +13 -6
- data/lib/tempest_time/commands/config.rb +0 -1
- data/lib/tempest_time/commands/config/edit.rb +1 -1
- data/lib/tempest_time/commands/config/setup.rb +2 -3
- data/lib/tempest_time/commands/delete.rb +1 -1
- data/lib/tempest_time/commands/issue.rb +0 -1
- data/lib/tempest_time/commands/issue/list.rb +4 -4
- data/lib/tempest_time/commands/issue/open.rb +2 -2
- data/lib/tempest_time/commands/list.rb +5 -5
- data/lib/tempest_time/commands/report.rb +13 -10
- data/lib/tempest_time/commands/submit.rb +2 -2
- data/lib/tempest_time/commands/teams.rb +0 -1
- data/lib/tempest_time/commands/teams/add.rb +2 -2
- data/lib/tempest_time/commands/teams/delete.rb +2 -2
- data/lib/tempest_time/commands/teams/edit.rb +2 -2
- data/lib/tempest_time/commands/timer.rb +0 -1
- data/lib/tempest_time/commands/timer/delete.rb +1 -1
- data/lib/tempest_time/commands/timer/list.rb +3 -3
- data/lib/tempest_time/commands/timer/pause.rb +2 -2
- data/lib/tempest_time/commands/timer/start.rb +1 -1
- data/lib/tempest_time/commands/timer/status.rb +1 -1
- data/lib/tempest_time/commands/timer/track.rb +1 -1
- data/lib/tempest_time/commands/track.rb +12 -15
- data/lib/tempest_time/helpers/formatting_helper.rb +1 -1
- data/lib/tempest_time/helpers/git_helper.rb +1 -1
- data/lib/tempest_time/helpers/time_helper.rb +1 -1
- data/lib/tempest_time/models/report.rb +7 -6
- data/lib/tempest_time/models/timer.rb +3 -3
- data/lib/tempest_time/setting.rb +1 -1
- data/lib/tempest_time/settings/app.rb +1 -1
- data/lib/tempest_time/settings/authorization.rb +1 -1
- data/lib/tempest_time/settings/teams.rb +1 -1
- data/lib/tempest_time/version.rb +1 -1
- data/tempest_time.gemspec +4 -4
- metadata +18 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 19ecc90a766883eb5563c29111a30ffc3bd5c2fcaf13e549894af2b94c68fafc
|
|
4
|
+
data.tar.gz: 40db9b3ab1cadc3e01ae3ee324c0a0cb0bbb5a2cb9955907c6e6feef97e40a09
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1daa51c89fb224a118379a213d4ad99a5102e628985f8095a6574b439c9d759f18e9b6699895da7fe1fb9a374b97b3b0acc0215dcfdac7df22af991fd2480472
|
|
7
|
+
data.tar.gz: 477bd2d18f6335e6087fc1cc2857f23be945313983b8ac2eceb89bdcf63e0bbbd46eb975894480c48114e96279c4bdf195fdb99b874e283be61ef7470ce9a418
|
data/.rubocop.yml
CHANGED
data/.rubocop_airbnb.yml
ADDED
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Contributing to Tempest
|
|
2
|
+
1. Fork the repo.
|
|
3
|
+
2. When running your local copy of tempest, run `bin/tempest` from the project directory.
|
|
4
|
+
3. Run `./git-hooks/install.sh` to install a pre-commit hook that will lint your code.
|
|
5
|
+
4. Make your changes.
|
|
6
|
+
5. Submit a pull request to this repo.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -1,8 +1,47 @@
|
|
|
1
|
-
# Tempest
|
|
1
|
+
# Tempest Time Tracker
|
|
2
|
+
Smart CLI for Jira Cloud.
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
## Install and Setup
|
|
5
|
+
### Install Ruby
|
|
6
|
+
All you need to install Tempest is Ruby! It is recommended to install a Ruby version manager if you have not already done so.
|
|
7
|
+
```
|
|
8
|
+
brew install rbenv
|
|
9
|
+
OR
|
|
10
|
+
brew install rvm
|
|
11
|
+
```
|
|
12
|
+
### Install the Tempest Gem
|
|
13
|
+
The application is hosted on [Rubygems](https://rubygems.org/gems/tempest_time), so you can install it with the following command:
|
|
14
|
+
```
|
|
15
|
+
gem install tempest_time
|
|
16
|
+
```
|
|
4
17
|
|
|
5
|
-
|
|
18
|
+
### Provide Tempest with your Credentials
|
|
19
|
+
Run the setup command to get started.
|
|
20
|
+
```
|
|
21
|
+
tempest config setup
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Keep Tempest Updated
|
|
25
|
+
```
|
|
26
|
+
gem update tempest_time
|
|
6
27
|
```
|
|
7
|
-
|
|
8
|
-
|
|
28
|
+
|
|
29
|
+
## Built With
|
|
30
|
+
* [TTY: The Ruby Terminal Apps Toolkit](https://piotrmurach.github.io/tty/)
|
|
31
|
+
* [Jira Cloud API](https://developer.atlassian.com/cloud/jira/platform/rest/v3/)
|
|
32
|
+
* [Tempo Cloud API](https://tempo-io.github.io/tempo-api-docs/)
|
|
33
|
+
|
|
34
|
+
## Contributing
|
|
35
|
+
Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.
|
|
36
|
+
|
|
37
|
+
## Versioning
|
|
38
|
+
We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/devanhurst/tempest_time/tags).
|
|
39
|
+
|
|
40
|
+
## Authors
|
|
41
|
+
* **Devan Hurst** - *Initial work* - [Bounteous](https://www.bounteous.ca)
|
|
42
|
+
|
|
43
|
+
See also the list of [contributors](https://github.com/devanhurst/tempest_time/contributors) who participated in this project.
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
|
data/bin/tempest
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative '../../../helpers/formatting_helper'
|
|
2
4
|
|
|
3
5
|
module JiraAPI
|
|
@@ -5,7 +7,7 @@ module JiraAPI
|
|
|
5
7
|
class Issue
|
|
6
8
|
include TempestTime::Helpers::FormattingHelper
|
|
7
9
|
|
|
8
|
-
attr_reader :fields, :
|
|
10
|
+
attr_reader :fields, :key
|
|
9
11
|
|
|
10
12
|
def initialize(issue)
|
|
11
13
|
@key = issue['key']
|
|
@@ -13,16 +15,18 @@ module JiraAPI
|
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def remaining_estimate
|
|
16
|
-
@remaining_estimate ||= fields.
|
|
18
|
+
@remaining_estimate ||= fields.fetch('timetracking', {}).fetch(
|
|
19
|
+
'remainingEstimateSeconds', nil
|
|
20
|
+
)
|
|
17
21
|
end
|
|
18
22
|
|
|
19
23
|
def summary
|
|
20
|
-
@summary ||= fields.
|
|
24
|
+
@summary ||= fields.fetch('summary')
|
|
21
25
|
end
|
|
22
26
|
|
|
23
27
|
def status
|
|
24
|
-
@status ||= fields.
|
|
28
|
+
@status ||= fields.fetch('status', {}).fetch('name', nil)
|
|
25
29
|
end
|
|
26
30
|
end
|
|
27
31
|
end
|
|
28
|
-
end
|
|
32
|
+
end
|
|
@@ -6,7 +6,7 @@ module JiraAPI
|
|
|
6
6
|
class GetUserIssues < JiraAPI::Request
|
|
7
7
|
attr_reader :requested_user
|
|
8
8
|
|
|
9
|
-
def initialize(requested_user
|
|
9
|
+
def initialize(requested_user: nil)
|
|
10
10
|
super
|
|
11
11
|
@requested_user = requested_user || username
|
|
12
12
|
end
|
|
@@ -24,7 +24,7 @@ module JiraAPI
|
|
|
24
24
|
def query_params
|
|
25
25
|
{
|
|
26
26
|
'jql' => "assignee=#{requested_user} AND resolution is EMPTY",
|
|
27
|
-
'maxResults' => 100
|
|
27
|
+
'maxResults' => 100,
|
|
28
28
|
}
|
|
29
29
|
end
|
|
30
30
|
|
|
@@ -33,4 +33,4 @@ module JiraAPI
|
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
|
-
end
|
|
36
|
+
end
|
|
@@ -11,9 +11,9 @@ module TempestTime
|
|
|
11
11
|
class Request
|
|
12
12
|
include HTTParty
|
|
13
13
|
|
|
14
|
-
DATE_FORMAT =
|
|
14
|
+
DATE_FORMAT = '%Y-%m-%d'
|
|
15
15
|
|
|
16
|
-
def initialize(*
|
|
16
|
+
def initialize(*_args)
|
|
17
17
|
self.class.base_uri credentials.fetch(:url)
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -86,4 +86,4 @@ module TempestTime
|
|
|
86
86
|
end
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
|
-
end
|
|
89
|
+
end
|
|
@@ -39,7 +39,7 @@ module TempoAPI
|
|
|
39
39
|
"startDate": date.strftime('%Y-%m-%d'),
|
|
40
40
|
"startTime": '12:00:00',
|
|
41
41
|
"authorUsername": user,
|
|
42
|
-
"description": message
|
|
42
|
+
"description": message,
|
|
43
43
|
}
|
|
44
44
|
end
|
|
45
45
|
|
|
@@ -48,4 +48,4 @@ module TempoAPI
|
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
|
-
end
|
|
51
|
+
end
|
|
@@ -5,11 +5,10 @@ require_relative '../responses/list_worklogs'
|
|
|
5
5
|
|
|
6
6
|
module TempoAPI
|
|
7
7
|
module Requests
|
|
8
|
-
# :no-doc:
|
|
9
8
|
class ListWorklogs < TempoAPI::Request
|
|
10
9
|
attr_reader :start_date, :end_date
|
|
11
10
|
|
|
12
|
-
def initialize(start_date, end_date
|
|
11
|
+
def initialize(start_date, end_date:, requested_user:)
|
|
13
12
|
super
|
|
14
13
|
@start_date = start_date
|
|
15
14
|
@end_date = end_date || start_date
|
|
@@ -36,7 +35,7 @@ module TempoAPI
|
|
|
36
35
|
{
|
|
37
36
|
"from": start_date.strftime(DATE_FORMAT),
|
|
38
37
|
"to": end_date.strftime(DATE_FORMAT),
|
|
39
|
-
"limit": 1000
|
|
38
|
+
"limit": 1000,
|
|
40
39
|
}
|
|
41
40
|
end
|
|
42
41
|
end
|
|
@@ -32,15 +32,15 @@ module TempoAPI
|
|
|
32
32
|
def query_params
|
|
33
33
|
{
|
|
34
34
|
from: dates.first.strftime(DATE_FORMAT),
|
|
35
|
-
to: dates.last.strftime(DATE_FORMAT)
|
|
35
|
+
to: dates.last.strftime(DATE_FORMAT),
|
|
36
36
|
}
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def request_body
|
|
40
40
|
{
|
|
41
|
-
reviewerUsername: reviewer
|
|
41
|
+
reviewerUsername: reviewer,
|
|
42
42
|
}
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
|
-
end
|
|
46
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative '../response'
|
|
2
4
|
|
|
3
5
|
module TempoAPI
|
|
@@ -8,7 +10,7 @@ module TempoAPI
|
|
|
8
10
|
def worklog
|
|
9
11
|
@worklog ||= TempoAPI::Models::Worklog.new(
|
|
10
12
|
id: raw_response['tempoWorklogId'],
|
|
11
|
-
issue: raw_response.
|
|
13
|
+
issue: raw_response.fetch('issue', {}).fetch('key', nil),
|
|
12
14
|
seconds: raw_response['timeSpentSeconds'],
|
|
13
15
|
description: raw_response['description']
|
|
14
16
|
)
|
|
@@ -19,4 +21,4 @@ module TempoAPI
|
|
|
19
21
|
end
|
|
20
22
|
end
|
|
21
23
|
end
|
|
22
|
-
end
|
|
24
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative '../response'
|
|
2
4
|
require_relative '../models/worklog'
|
|
3
5
|
require_relative '../../../helpers/time_helper'
|
|
@@ -7,13 +9,11 @@ module TempoAPI
|
|
|
7
9
|
class ListWorklogs < TempoAPI::Response
|
|
8
10
|
include TempestTime::Helpers::TimeHelper
|
|
9
11
|
|
|
10
|
-
attr_reader :worklogs, :total_hours_spent
|
|
11
|
-
|
|
12
12
|
def worklogs
|
|
13
13
|
@worklogs ||= results.map do |worklog|
|
|
14
14
|
TempoAPI::Models::Worklog.new(
|
|
15
15
|
id: worklog['tempoWorklogId'],
|
|
16
|
-
issue: worklog.
|
|
16
|
+
issue: worklog.fetch('issue', {}).fetch('key', nil),
|
|
17
17
|
seconds: worklog['timeSpentSeconds'],
|
|
18
18
|
description: worklog['description']
|
|
19
19
|
)
|
|
@@ -26,11 +26,9 @@ module TempoAPI
|
|
|
26
26
|
|
|
27
27
|
private
|
|
28
28
|
|
|
29
|
-
attr_reader :results
|
|
30
|
-
|
|
31
29
|
def results
|
|
32
30
|
@results = raw_response['results'] || []
|
|
33
31
|
end
|
|
34
32
|
end
|
|
35
33
|
end
|
|
36
|
-
end
|
|
34
|
+
end
|
data/lib/tempest_time/cli.rb
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
4
|
require 'git'
|
|
3
5
|
|
|
4
6
|
module TempestTime
|
|
5
7
|
class CLI < Thor
|
|
8
|
+
package_name 'Tempest'
|
|
6
9
|
# Error raised by this runner
|
|
7
10
|
Error = Class.new(StandardError)
|
|
8
11
|
|
|
@@ -62,7 +65,7 @@ module TempestTime
|
|
|
62
65
|
TempestTime::Commands::Delete.new(worklogs, options).execute
|
|
63
66
|
end
|
|
64
67
|
|
|
65
|
-
desc
|
|
68
|
+
desc 'track [TIME] [ISSUE(S)]', 'Track time to Tempo.'
|
|
66
69
|
long_desc <<-LONGDESC
|
|
67
70
|
`tempest track` or `tempest log` will track the specified number of hours or minutes to the issue(s) specified.\n
|
|
68
71
|
If not specified, it will check the name of the current git branch and automatically track the logged time to that issue, if found.\n
|
|
@@ -88,4 +91,4 @@ module TempestTime
|
|
|
88
91
|
|
|
89
92
|
map log: :track
|
|
90
93
|
end
|
|
91
|
-
end
|
|
94
|
+
end
|
data/lib/tempest_time/command.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'tty-reader'
|
|
3
4
|
require 'forwardable'
|
|
4
5
|
require_relative 'helpers/time_helper'
|
|
5
6
|
require_relative 'helpers/formatting_helper'
|
|
@@ -15,9 +16,15 @@ module TempestTime
|
|
|
15
16
|
def_delegators :command, :run
|
|
16
17
|
|
|
17
18
|
def execute(*)
|
|
19
|
+
execute!
|
|
20
|
+
rescue TTY::Reader::InputInterrupt
|
|
21
|
+
prompt.say(pastel.yellow("\nGoodbye!"))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def execute!(*)
|
|
18
25
|
raise(
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
NotImplementedError,
|
|
27
|
+
"#{self.class}##{__method__} must be implemented"
|
|
21
28
|
)
|
|
22
29
|
end
|
|
23
30
|
|
|
@@ -36,7 +43,7 @@ module TempestTime
|
|
|
36
43
|
TTY::Prompt.new(options)
|
|
37
44
|
end
|
|
38
45
|
|
|
39
|
-
def week_prompt(message, past_weeks
|
|
46
|
+
def week_prompt(message, past_weeks: 51)
|
|
40
47
|
require 'tty-prompt'
|
|
41
48
|
weeks = past_week_selections(past_weeks)
|
|
42
49
|
TTY::Prompt.new.select(
|
|
@@ -46,7 +53,7 @@ module TempestTime
|
|
|
46
53
|
)
|
|
47
54
|
end
|
|
48
55
|
|
|
49
|
-
def date_prompt(message, past_days
|
|
56
|
+
def date_prompt(message, past_days: 6)
|
|
50
57
|
require 'tty-prompt'
|
|
51
58
|
dates = past_date_selections(past_days)
|
|
52
59
|
TTY::Prompt.new.select(
|
|
@@ -66,13 +73,13 @@ module TempestTime
|
|
|
66
73
|
TTY::Table
|
|
67
74
|
end
|
|
68
75
|
|
|
69
|
-
def with_spinner(message, format
|
|
76
|
+
def with_spinner(message, format: :pong)
|
|
70
77
|
s = spinner.new(":spinner #{message}", format: format)
|
|
71
78
|
s.auto_spin
|
|
72
79
|
yield(s)
|
|
73
80
|
end
|
|
74
81
|
|
|
75
|
-
def with_success_fail_spinner(message, format
|
|
82
|
+
def with_success_fail_spinner(message, format: :spin_3)
|
|
76
83
|
s = spinner.new(":spinner #{message}", format: format)
|
|
77
84
|
s.auto_spin
|
|
78
85
|
response = yield
|
|
@@ -8,13 +8,12 @@ require_relative '../../api/tempo_api/requests/list_worklogs'
|
|
|
8
8
|
module TempestTime
|
|
9
9
|
module Commands
|
|
10
10
|
class Config
|
|
11
|
-
# :no-doc:
|
|
12
11
|
class Setup < TempestTime::Command
|
|
13
12
|
def initialize(options)
|
|
14
13
|
@options = options
|
|
15
14
|
end
|
|
16
15
|
|
|
17
|
-
def execute
|
|
16
|
+
def execute!
|
|
18
17
|
check_for_completion
|
|
19
18
|
set_authorization_values
|
|
20
19
|
with_spinner('Checking credentials...') do |spinner|
|
|
@@ -35,7 +34,7 @@ module TempestTime
|
|
|
35
34
|
'username' => username,
|
|
36
35
|
'subdomain' => subdomain,
|
|
37
36
|
'jira_token' => jira_token,
|
|
38
|
-
'tempo_token' => tempo_token
|
|
37
|
+
'tempo_token' => tempo_token,
|
|
39
38
|
}
|
|
40
39
|
end
|
|
41
40
|
|
|
@@ -11,8 +11,8 @@ module TempestTime
|
|
|
11
11
|
@user = user || current_user
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def execute
|
|
15
|
-
request = JiraAPI::Requests::GetUserIssues.new(@user)
|
|
14
|
+
def execute!
|
|
15
|
+
request = JiraAPI::Requests::GetUserIssues.new(requested_user: @user)
|
|
16
16
|
message = "Getting issues for #{request.requested_user}"
|
|
17
17
|
response = with_spinner(message) do |spinner|
|
|
18
18
|
request.send_request.tap { spinner.stop }
|
|
@@ -36,7 +36,7 @@ module TempestTime
|
|
|
36
36
|
|
|
37
37
|
def format_output(issues)
|
|
38
38
|
table.new(
|
|
39
|
-
%w
|
|
39
|
+
%w(Status Issue Summary),
|
|
40
40
|
issues.map { |issue| row(issue) }
|
|
41
41
|
).render(:ascii, padding: [0, 1], column_widths: [15, 10, 30])
|
|
42
42
|
end
|
|
@@ -52,4 +52,4 @@ module TempestTime
|
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
|
-
end
|
|
55
|
+
end
|
|
@@ -12,7 +12,7 @@ module TempestTime
|
|
|
12
12
|
@issues = [automatic_issue] if issues.empty?
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
def execute
|
|
15
|
+
def execute!
|
|
16
16
|
@issues.each { |issue| command.run("open #{url(issue)}") }
|
|
17
17
|
end
|
|
18
18
|
|
|
@@ -25,4 +25,4 @@ module TempestTime
|
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
|
-
end
|
|
28
|
+
end
|
|
@@ -11,14 +11,14 @@ module TempestTime
|
|
|
11
11
|
@date = options[:date] ? Date.parse(options[:date]) : nil
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def execute
|
|
14
|
+
def execute!
|
|
15
15
|
@date ||= date_prompt('Please select a date.')
|
|
16
16
|
|
|
17
17
|
with_spinner("Retrieving logs for #{formatted_date(@date)}...") do |spin|
|
|
18
18
|
@response = TempoAPI::Requests::ListWorklogs.new(
|
|
19
19
|
@date,
|
|
20
|
-
nil,
|
|
21
|
-
@user
|
|
20
|
+
end_date: nil,
|
|
21
|
+
requested_user: @user
|
|
22
22
|
).send_request
|
|
23
23
|
spin.stop(pastel.green('Done!'))
|
|
24
24
|
prompt.say(render_table)
|
|
@@ -32,7 +32,7 @@ module TempestTime
|
|
|
32
32
|
private
|
|
33
33
|
|
|
34
34
|
def table_headings
|
|
35
|
-
%w
|
|
35
|
+
%w(Worklog Issue Time Description)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def render_table
|
|
@@ -50,7 +50,7 @@ module TempestTime
|
|
|
50
50
|
worklog.id,
|
|
51
51
|
worklog.issue,
|
|
52
52
|
formatted_time(worklog.seconds),
|
|
53
|
-
worklog.description
|
|
53
|
+
worklog.description,
|
|
54
54
|
]
|
|
55
55
|
end
|
|
56
56
|
end
|
|
@@ -14,9 +14,12 @@ module TempestTime
|
|
|
14
14
|
@teams = TempestTime::Settings::Teams.new
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def execute
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
def execute!
|
|
18
|
+
if @users.empty? && @team.nil?
|
|
19
|
+
@users = user_prompt
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
@users.push(@teams.members(@team)) if @team
|
|
20
23
|
abort('No users specified.') unless @users.any?
|
|
21
24
|
|
|
22
25
|
@week = week_prompt('Please select the week to report.')
|
|
@@ -38,12 +41,12 @@ module TempestTime
|
|
|
38
41
|
type = prompt.select(
|
|
39
42
|
'Generate a report for a '\
|
|
40
43
|
"#{pastel.green('team')} or a specific #{pastel.green('user')}?",
|
|
41
|
-
%w
|
|
44
|
+
%w(Team User)
|
|
42
45
|
)
|
|
43
46
|
|
|
44
47
|
if type == 'User'
|
|
45
48
|
return [
|
|
46
|
-
prompt.ask("Please enter a #{pastel.green('user')}.")
|
|
49
|
+
prompt.ask("Please enter a #{pastel.green('user')}."),
|
|
47
50
|
]
|
|
48
51
|
end
|
|
49
52
|
|
|
@@ -70,8 +73,8 @@ module TempestTime
|
|
|
70
73
|
@reports ||= @users.map do |user|
|
|
71
74
|
list = TempoAPI::Requests::ListWorklogs.new(
|
|
72
75
|
start_date,
|
|
73
|
-
end_date,
|
|
74
|
-
user
|
|
76
|
+
end_date: end_date,
|
|
77
|
+
requested_user: user
|
|
75
78
|
).send_request
|
|
76
79
|
TempestTime::Models::Report.new(user, list.worklogs)
|
|
77
80
|
end || []
|
|
@@ -81,7 +84,7 @@ module TempestTime
|
|
|
81
84
|
@aggregate ||= TempestTime::Models::Report.new(
|
|
82
85
|
'TOTAL',
|
|
83
86
|
reports.flat_map(&:worklogs),
|
|
84
|
-
@users.count
|
|
87
|
+
number_of_users: @users.count
|
|
85
88
|
)
|
|
86
89
|
end
|
|
87
90
|
|
|
@@ -93,7 +96,7 @@ module TempestTime
|
|
|
93
96
|
end
|
|
94
97
|
|
|
95
98
|
def table_headings
|
|
96
|
-
%w
|
|
99
|
+
%w(User COMP% UTIL%) + projects
|
|
97
100
|
end
|
|
98
101
|
|
|
99
102
|
def render_table
|
|
@@ -109,7 +112,7 @@ module TempestTime
|
|
|
109
112
|
row = [
|
|
110
113
|
data.user,
|
|
111
114
|
right_align(percentage(data.total_compliance_percentage)),
|
|
112
|
-
right_align(percentage(data.utilization_percentage))
|
|
115
|
+
right_align(percentage(data.utilization_percentage)),
|
|
113
116
|
]
|
|
114
117
|
projects.each do |project|
|
|
115
118
|
row.push(
|
|
@@ -10,7 +10,7 @@ module TempestTime
|
|
|
10
10
|
@options = options
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def execute
|
|
13
|
+
def execute!
|
|
14
14
|
reviewer = prompt.ask('Who should review this timesheet? (username)')
|
|
15
15
|
dates = week_dates(week_prompt('Select a week to submit.'))
|
|
16
16
|
|
|
@@ -18,7 +18,7 @@ module TempestTime
|
|
|
18
18
|
abort unless prompt.yes?(message)
|
|
19
19
|
abort unless prompt.yes?('Are you sure? No edits can be made once submitted!')
|
|
20
20
|
|
|
21
|
-
with_success_fail_spinner(
|
|
21
|
+
with_success_fail_spinner('Submitting your timesheet...') do
|
|
22
22
|
TempoAPI::Requests::SubmitTimesheet.new(reviewer, dates).send_request
|
|
23
23
|
end
|
|
24
24
|
end
|
|
@@ -11,10 +11,10 @@ module TempestTime
|
|
|
11
11
|
@options = options
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def execute
|
|
14
|
+
def execute!
|
|
15
15
|
teams = TempestTime::Settings::Teams.new
|
|
16
16
|
message =
|
|
17
|
-
'Please enter ' + pastel.green('the members') +
|
|
17
|
+
'Please enter ' + pastel.green('the members') + ' of this team. '\
|
|
18
18
|
'(Comma-separated, e.g. jkirk, jpicard, bsisko, kjaneway) '
|
|
19
19
|
members = prompt.ask(message) do |q|
|
|
20
20
|
q.convert ->(input) { input.split(/,\s*/) }
|
|
@@ -12,8 +12,8 @@ module TempestTime
|
|
|
12
12
|
@options = options
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
def execute
|
|
16
|
-
abort(
|
|
15
|
+
def execute!
|
|
16
|
+
abort('There are no teams to delete!') unless @teams.keys.any?
|
|
17
17
|
team = prompt.select(
|
|
18
18
|
"Which #{pastel.green('team')} would you like to delete?",
|
|
19
19
|
@teams.names
|
|
@@ -11,9 +11,9 @@ module TempestTime
|
|
|
11
11
|
@options = options
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def execute
|
|
14
|
+
def execute!
|
|
15
15
|
teams = TempestTime::Settings::Teams.new
|
|
16
|
-
abort(
|
|
16
|
+
abort('There are no teams to edit!') unless teams.names.any?
|
|
17
17
|
team = prompt.select(
|
|
18
18
|
"Which #{pastel.green('team')} would you like to edit?",
|
|
19
19
|
teams.names
|
|
@@ -8,7 +8,7 @@ module TempestTime
|
|
|
8
8
|
module Commands
|
|
9
9
|
class Timer
|
|
10
10
|
class List < TempestTime::Command
|
|
11
|
-
def execute
|
|
11
|
+
def execute!
|
|
12
12
|
abort(pastel.red('No timers running!')) unless all_timers.any?
|
|
13
13
|
all_timers.each do |timer|
|
|
14
14
|
TempestTime::Commands::Timer::Status.new(timer.issue).execute
|
|
@@ -33,9 +33,9 @@ module TempestTime
|
|
|
33
33
|
|
|
34
34
|
def action_prompt(timer)
|
|
35
35
|
if timer.running?
|
|
36
|
-
prompt.select('', %w
|
|
36
|
+
prompt.select('', %w(Pause Track))
|
|
37
37
|
else
|
|
38
|
-
prompt.select('', %w
|
|
38
|
+
prompt.select('', %w(Resume Track))
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -13,7 +13,7 @@ module TempestTime
|
|
|
13
13
|
@timer = TempestTime::Models::Timer.new(@issue)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def execute
|
|
16
|
+
def execute!
|
|
17
17
|
timer.pause
|
|
18
18
|
TempestTime::Commands::Timer::Status.new(issue).execute
|
|
19
19
|
end
|
|
@@ -24,4 +24,4 @@ module TempestTime
|
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
|
-
end
|
|
27
|
+
end
|
|
@@ -7,26 +7,23 @@ require_relative '../api/tempo_api/requests/create_worklog'
|
|
|
7
7
|
module TempestTime
|
|
8
8
|
module Commands
|
|
9
9
|
class Track < TempestTime::Command
|
|
10
|
+
attr_reader :time, :issues, :options
|
|
11
|
+
|
|
10
12
|
def initialize(time, issues, options)
|
|
11
|
-
@time = time
|
|
12
|
-
@issues = issues
|
|
13
13
|
@options = options
|
|
14
|
+
@issues = issues.any? ? issues.map(&:upcase) : [automatic_issue]
|
|
15
|
+
@time = parsed_time(time) / issues.count
|
|
14
16
|
end
|
|
15
17
|
|
|
16
|
-
def execute
|
|
17
|
-
time = @options[:split] ? parsed_time(@time) / @issues.count : parsed_time(@time)
|
|
18
|
-
issues = @issues.any? ? @issues.map(&:upcase) : [automatic_issue]
|
|
19
|
-
|
|
18
|
+
def execute!
|
|
20
19
|
unless @options[:autoconfirm]
|
|
21
20
|
prompt_message = "Track #{formatted_time(time)}, "\
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
"#{billability(options)}, "\
|
|
22
|
+
"to #{issues.join(', ')}?"
|
|
24
23
|
abort unless prompt.yes?(prompt_message, convert: :bool)
|
|
25
24
|
end
|
|
26
25
|
|
|
27
|
-
issues.each
|
|
28
|
-
track_time(time, @options.merge('issue' => issue))
|
|
29
|
-
end
|
|
26
|
+
issues.each { |issue| track_time(time, options.merge('issue' => issue)) }
|
|
30
27
|
end
|
|
31
28
|
|
|
32
29
|
private
|
|
@@ -35,10 +32,10 @@ module TempestTime
|
|
|
35
32
|
message = "Tracking #{formatted_time(time)} to #{options['issue']}..."
|
|
36
33
|
with_success_fail_spinner(message) do
|
|
37
34
|
options['remaining'] = if options['remaining'].nil?
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
remaining_estimate(options['issue'], time)
|
|
36
|
+
else
|
|
37
|
+
parsed_time(options['remaining'])
|
|
38
|
+
end
|
|
42
39
|
TempoAPI::Requests::CreateWorklog.new(time, options).send_request
|
|
43
40
|
end
|
|
44
41
|
end
|
|
@@ -9,7 +9,7 @@ module TempestTime
|
|
|
9
9
|
|
|
10
10
|
attr_reader :user, :worklogs, :number_of_users
|
|
11
11
|
|
|
12
|
-
def initialize(user, worklogs, number_of_users
|
|
12
|
+
def initialize(user, worklogs, number_of_users: 1)
|
|
13
13
|
@user = user
|
|
14
14
|
@worklogs = worklogs
|
|
15
15
|
@number_of_users = number_of_users
|
|
@@ -36,10 +36,11 @@ module TempestTime
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def utilization_percentage
|
|
39
|
-
@utilization_percentage ||=
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
@utilization_percentage ||=
|
|
40
|
+
project_compliance_percentages.inject(0) do |memo, (project, percentage)|
|
|
41
|
+
memo += percentage unless project == INTERNAL_PROJECT
|
|
42
|
+
memo
|
|
43
|
+
end
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
def time_logged_seconds(logs)
|
|
@@ -55,4 +56,4 @@ module TempestTime
|
|
|
55
56
|
end
|
|
56
57
|
end
|
|
57
58
|
end
|
|
58
|
-
end
|
|
59
|
+
end
|
|
@@ -5,7 +5,7 @@ require 'tempfile'
|
|
|
5
5
|
module TempestTime
|
|
6
6
|
module Models
|
|
7
7
|
class Timer
|
|
8
|
-
TEMP_DIR = '/
|
|
8
|
+
TEMP_DIR = Dir.home + '/.tempest/timer/logs'
|
|
9
9
|
FILE_EXT = '.timer'
|
|
10
10
|
PREFIX_SEPARATOR = '___'
|
|
11
11
|
|
|
@@ -43,12 +43,12 @@ module TempestTime
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def runtime
|
|
46
|
-
@
|
|
46
|
+
@runtime ||= log_files.each_with_object([]) do |log, array|
|
|
47
47
|
start_time = File.birthtime(log)
|
|
48
48
|
end_time = log_running?(log) ? Time.now : File.mtime(log)
|
|
49
49
|
|
|
50
50
|
array << (end_time - start_time)
|
|
51
|
-
end.
|
|
51
|
+
end.reduce(:+)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def running?
|
data/lib/tempest_time/setting.rb
CHANGED
data/lib/tempest_time/version.rb
CHANGED
data/tempest_time.gemspec
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
lib = File.expand_path('../lib', __FILE__)
|
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
3
|
require 'tempest_time/version'
|
|
@@ -11,13 +10,13 @@ Gem::Specification.new do |spec|
|
|
|
11
10
|
spec.authors = ['Devan Hurst']
|
|
12
11
|
spec.email = ['devan.hurst@gmail.com']
|
|
13
12
|
|
|
14
|
-
spec.summary = %q
|
|
15
|
-
spec.description = %q
|
|
13
|
+
spec.summary = %q(Smart CLI for Jira Cloud.)
|
|
14
|
+
spec.description = %q(Log time and more... directly from the command line!)
|
|
16
15
|
spec.homepage = 'https://github.com/devanhurst/tempest_time'
|
|
17
16
|
|
|
18
17
|
spec.required_ruby_version = '~> 2.3'
|
|
19
18
|
|
|
20
|
-
spec.files
|
|
19
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
21
20
|
f.match(%r{^(test|spec|features)/})
|
|
22
21
|
end
|
|
23
22
|
spec.executables = ['tempest']
|
|
@@ -27,6 +26,7 @@ Gem::Specification.new do |spec|
|
|
|
27
26
|
spec.add_development_dependency 'byebug', '~> 10.0'
|
|
28
27
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
|
29
28
|
spec.add_development_dependency 'rake', '~> 10.0'
|
|
29
|
+
spec.add_development_dependency 'rubocop-airbnb', '~> 2.0'
|
|
30
30
|
|
|
31
31
|
spec.add_dependency 'git', '~>1.5'
|
|
32
32
|
spec.add_dependency 'httparty', '~>0.16'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tempest_time
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Devan Hurst
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2019-02-
|
|
11
|
+
date: 2019-02-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -66,6 +66,20 @@ dependencies:
|
|
|
66
66
|
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '10.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rubocop-airbnb
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '2.0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '2.0'
|
|
69
83
|
- !ruby/object:Gem::Dependency
|
|
70
84
|
name: git
|
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -216,8 +230,10 @@ extra_rdoc_files: []
|
|
|
216
230
|
files:
|
|
217
231
|
- ".gitignore"
|
|
218
232
|
- ".rubocop.yml"
|
|
233
|
+
- ".rubocop_airbnb.yml"
|
|
219
234
|
- ".ruby-version"
|
|
220
235
|
- ".travis.yml"
|
|
236
|
+
- CONTRIBUTING.md
|
|
221
237
|
- Gemfile
|
|
222
238
|
- LICENSE
|
|
223
239
|
- README.md
|