tempest_time 0.7.4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|