tempest_time 0.6.2 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 74bde0bc0ada40a2f0b8b0ddd15e8ac34496bd162b991e6d128955de9f5b063b
4
- data.tar.gz: 63aca556189c6448a228e87cea9c66d8d553755a80e02ac2224ced683db02653
3
+ metadata.gz: bee4c127b0d41d0f31cf49f9b3291c5948060eb44a8e9835720cb94ab9ffd123
4
+ data.tar.gz: 7d8102ca79d54c07b6ade649135f5a545d6a6e2297564420e1cd762b18980b4d
5
5
  SHA512:
6
- metadata.gz: 5d996e6427394195f2a0c145a3bb728326b253b245cc242759bfc7d5fd57197639b97d5ae9b6c0b2161bb8a94cf45a2fa05964f8e04349ef5c45031c6f302a28
7
- data.tar.gz: bc50f8b3504c5adf0165c7a9906e3a371aa503e0f1691305ccb65531768a842be4eeb403f9a6ae07efc9a39678c9037a7e26c911aa56011e43f086420c94b9ff
6
+ metadata.gz: 6db29dd83c233f730e47f8226292f305ae8c99c8f640846f271314ab3365c59e0ebbed337910fbf7b28971ccde1e5e645b1f8b19ea0e3cdfd368a2c1b34ec0e2
7
+ data.tar.gz: c8d4feac6d657e579d0382e72331dd18a33b5a13e024c12baf67c7d7131c2acc6087e95ebceb53a0199cda97888e6e7976462bec2fd4c2992f70613b1c37c2cb
data/.gitignore CHANGED
@@ -7,4 +7,5 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
  .byebug_history
10
- *.gem
10
+ Gemfile.lock
11
+ *.gem
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'httparty'
2
4
  require 'json'
3
5
 
@@ -9,7 +11,7 @@ module TempestTime
9
11
  class Request
10
12
  include HTTParty
11
13
 
12
- DATE_FORMAT = "%Y-%m-%d".freeze
14
+ DATE_FORMAT = "%Y-%m-%d"
13
15
 
14
16
  def initialize(*args)
15
17
  self.class.base_uri credentials.fetch(:url)
@@ -81,6 +81,10 @@ module TempestTime
81
81
  TempestTime::Commands::Track.new(time, issues, options).execute
82
82
  end
83
83
 
84
+ require_relative 'commands/timer'
85
+ register TempestTime::Commands::Timer,
86
+ 'timer', 'timer [SUBCOMMAND]', 'Start, Stop, Delete, Submit Timers'
87
+
84
88
  map log: :track
85
89
  end
86
90
  end
@@ -25,14 +25,14 @@ module TempestTime
25
25
  private
26
26
 
27
27
  def browser_prompt(issues)
28
- abort if prompt.no?('Open an issue in your browser?')
29
- issue = prompt.select(
28
+ abort if prompt.no?('Open any issues in your browser?')
29
+ selections = prompt.multi_select(
30
30
  'Select an issue to open in browser, or press ^C to quit.',
31
31
  issues.map(&:key),
32
32
  per_page: 5
33
33
  )
34
34
  require_relative 'open'
35
- Open.new(issue).execute
35
+ Open.new(selections).execute
36
36
  end
37
37
 
38
38
  def format_output(issues)
@@ -7,13 +7,13 @@ module TempestTime
7
7
  module Commands
8
8
  class Issue
9
9
  class Open < TempestTime::Command
10
- def initialize(issue)
11
- @issue = issue.upcase
12
- @issue = automatic_issue if issue.empty?
10
+ def initialize(issues)
11
+ @issues = issues.map(&:upcase)
12
+ @issues = [automatic_issue] if issues.empty?
13
13
  end
14
14
 
15
15
  def execute(input: $stdin, output: $stdout)
16
- command.run("open #{url(@issue)}")
16
+ @issues.each { |issue| command.run("open #{url(issue)}") }
17
17
  end
18
18
 
19
19
  private
@@ -15,9 +15,9 @@ module TempestTime
15
15
  end
16
16
 
17
17
  desc 'open', 'Open an issue in your browser. (Default: current branch)'
18
- def open(issue = '')
18
+ def open(*issues)
19
19
  require_relative 'issue/open'
20
- TempestTime::Commands::Issue::Open.new(issue).execute
20
+ TempestTime::Commands::Issue::Open.new(issues).execute
21
21
  end
22
22
  end
23
23
  end
@@ -14,7 +14,6 @@ module TempestTime
14
14
  end
15
15
 
16
16
  def execute(input: $stdin, output: $stdout)
17
- # Command logic goes here ...
18
17
  reviewer = prompt.ask('Who should review this timesheet? (username)')
19
18
  dates = week_dates(week_prompt('Select a week to submit.'))
20
19
 
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../command'
4
+ require_relative '../../models/timer'
5
+
6
+ module TempestTime
7
+ module Commands
8
+ class Timer
9
+ class Delete < TempestTime::Command
10
+ def initialize(issue)
11
+ @issue = issue || automatic_issue
12
+ @timer = TempestTime::Models::Timer.new(@issue)
13
+ end
14
+
15
+ def execute(input: $stdin, output: $stdout)
16
+ timer.delete ? deleted_message : no_timer_message
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :issue, :timer
22
+
23
+ def deleted_message
24
+ prompt.say('baleeted')
25
+ end
26
+
27
+ def no_timer_message
28
+ prompt.say('noleeted')
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './status'
4
+ require_relative '../../command'
5
+ require_relative '../../models/timer'
6
+
7
+ module TempestTime
8
+ module Commands
9
+ class Timer
10
+ class List < TempestTime::Command
11
+ def execute(input: $stdin, output: $stdout)
12
+ all_timers.each do |timer|
13
+ TempestTime::Commands::Timer::Status.new(timer.issue).execute
14
+ end
15
+ issue = prompt.select(
16
+ 'Please select a timer to toggle / track, or enter ^C to exit.',
17
+ all_timers.map(&:issue)
18
+ )
19
+ action = action_prompt(timer(issue))
20
+ take_action(issue, action)
21
+ end
22
+
23
+ private
24
+
25
+ def all_timers
26
+ @all_timers ||= TempestTime::Models::Timer.all_timers
27
+ end
28
+
29
+ def timer(issue)
30
+ all_timers.detect { |timer| timer.issue == issue }
31
+ end
32
+
33
+ def action_prompt(timer)
34
+ if timer.running?
35
+ prompt.select('', %w[Pause Track])
36
+ else
37
+ prompt.select('', %w[Resume Track])
38
+ end
39
+ end
40
+
41
+ def take_action(issue, action)
42
+ case action
43
+ when 'Pause'
44
+ require_relative './pause'
45
+ TempestTime::Commands::Timer::Pause.new(issue).execute
46
+ when 'Resume'
47
+ require_relative './start'
48
+ TempestTime::Commands::Timer::Start.new(issue).execute
49
+ when 'Track'
50
+ require_relative './track'
51
+ TempestTime::Commands::Timer::Track.new(issue).execute
52
+ else
53
+ abort
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../command'
4
+ require_relative '../../models/timer'
5
+
6
+ module TempestTime
7
+ module Commands
8
+ class Timer
9
+ class Pause < TempestTime::Command
10
+ def initialize(issue)
11
+ @issue = issue || automatic_issue
12
+ @timer = TempestTime::Models::Timer.new(@issue)
13
+ end
14
+
15
+ def execute(input: $stdin, output: $stdout)
16
+ timer.pause
17
+ TempestTime::Commands::Timer::Status.new(issue).execute
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :issue, :timer
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './status'
4
+ require_relative '../../command'
5
+ require_relative '../../models/timer'
6
+
7
+ module TempestTime
8
+ module Commands
9
+ class Timer
10
+ class Start < TempestTime::Command
11
+ def initialize(issue)
12
+ @issue = issue || automatic_issue
13
+ @timer = TempestTime::Models::Timer.new(@issue)
14
+ end
15
+
16
+ def execute(input: $stdin, output: $stdout)
17
+ timer.start
18
+ TempestTime::Commands::Timer::Status.new(issue).execute
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :issue, :timer
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../command'
4
+ require_relative '../../models/timer'
5
+
6
+ module TempestTime
7
+ module Commands
8
+ class Timer
9
+ class Status < TempestTime::Command
10
+ def initialize(issue)
11
+ @issue = issue
12
+ @timer = TempestTime::Models::Timer.new(issue)
13
+ end
14
+
15
+ def execute(input: $stdin, output: $stdout)
16
+ prompt.say("#{issue}: #{status_message}")
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :issue, :timer
22
+
23
+ def status_message
24
+ if timer.running?
25
+ pastel.green("#{formatted_time_long(timer.runtime)} running")
26
+ else
27
+ pastel.yellow("#{formatted_time_long(timer.runtime)} paused")
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../track'
4
+ require_relative '../../command'
5
+ require_relative '../../models/timer'
6
+
7
+ module TempestTime
8
+ module Commands
9
+ class Timer
10
+ class Track < TempestTime::Command
11
+ attr_reader :timer, :issue
12
+
13
+ def initialize(issue)
14
+ @issue = issue || automatic_issue
15
+ @timer = TempestTime::Models::Timer.new(@issue)
16
+ end
17
+
18
+ def execute(input: $stdin, output: $stdout)
19
+ abort(pastel.red("No timer for #{issue}!")) unless timer.exists?
20
+ timer.pause
21
+ track_time
22
+ timer.delete
23
+ end
24
+
25
+ private
26
+
27
+ def track_time
28
+ time = prompt.ask(
29
+ "How much time should be logged to #{issue}?",
30
+ default: formatted_time_for_input(timer.runtime)
31
+ )
32
+ billable = prompt.yes?('Is this time billable?')
33
+ message = prompt.ask('Add a message. (optional)')
34
+ Commands::Track.new(
35
+ parsed_time(time),
36
+ [issue],
37
+ 'message' => message,
38
+ 'billable' => billable
39
+ ).execute
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module TempestTime
6
+ module Commands
7
+ class Timer < Thor
8
+
9
+ namespace :timer
10
+
11
+ desc 'start [ISSUE]', 'Start a new timer, or continue a paused timer.'
12
+ def start(issue = nil)
13
+ require_relative 'timer/start'
14
+ TempestTime::Commands::Timer::Start.new(issue).execute
15
+ end
16
+
17
+ desc 'pause [ISSUE]', 'Pause a timer.'
18
+ def pause(issue = nil)
19
+ require_relative 'timer/pause'
20
+ TempestTime::Commands::Timer::Pause.new(issue).execute
21
+ end
22
+
23
+ desc 'track [ISSUE]', 'Stop and track a timer.'
24
+ def track(issue = nil)
25
+ require_relative 'timer/track'
26
+ TempestTime::Commands::Timer::Track.new(issue).execute
27
+ end
28
+
29
+ desc 'delete [ISSUE]', 'Delete current timer.'
30
+ def delete(issue = nil)
31
+ require_relative 'timer/delete'
32
+ TempestTime::Commands::Timer::Delete.new(issue).execute
33
+ end
34
+
35
+ desc 'status [ISSUE]', 'Display timer status.'
36
+ def status(issue = nil)
37
+ require_relative 'timer/status'
38
+ TempestTime::Commands::Timer::Status.new(issue).execute
39
+ end
40
+
41
+ desc 'list', 'Display all timers.'
42
+ def list
43
+ require_relative 'timer/list'
44
+ TempestTime::Commands::Timer::List.new.execute
45
+ end
46
+ end
47
+ end
48
+ end
@@ -2,9 +2,8 @@
2
2
 
3
3
  require_relative '../command'
4
4
  require_relative '../helpers/time_helper'
5
-
6
- require_relative '../api/tempo_api/requests/create_worklog'
7
5
  require_relative '../api/jira_api/requests/get_issue'
6
+ require_relative '../api/tempo_api/requests/create_worklog'
8
7
 
9
8
  module TempestTime
10
9
  module Commands
@@ -27,7 +26,7 @@ module TempestTime
27
26
  end
28
27
 
29
28
  issues.each do |issue|
30
- track_time(time, @options.merge(issue: issue))
29
+ track_time(time, @options.merge('issue' => issue))
31
30
  end
32
31
  end
33
32
 
@@ -21,6 +21,15 @@ module TempestTime
21
21
  "#{(seconds / 3600.to_f).round(2)} hours"
22
22
  end
23
23
 
24
+ def formatted_time_for_input(seconds)
25
+ return "#{(seconds / 60).to_i}m" if seconds < 3600
26
+ "#{(seconds / 3600.to_f).round(2)}h"
27
+ end
28
+
29
+ def formatted_time_long(seconds)
30
+ Time.at(seconds).utc.strftime("%H:%M:%S")
31
+ end
32
+
24
33
  def formatted_date_range(start_date, end_date)
25
34
  return formatted_date(start_date) if end_date.nil?
26
35
  "#{formatted_date(start_date)} - #{formatted_date(end_date)}"
@@ -13,7 +13,6 @@ module TempestTime
13
13
  @user = user
14
14
  @worklogs = worklogs
15
15
  @number_of_users = number_of_users
16
- require 'byebug'; byebug
17
16
  end
18
17
 
19
18
  def project_total_times
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tempfile'
4
+
5
+ module TempestTime
6
+ module Models
7
+ class Timer
8
+ TEMP_DIR = '/tmp/timer/logs'
9
+ FILE_EXT = '.timer'
10
+ PREFIX_SEPARATOR = '___'
11
+
12
+ class << self
13
+ def all_timers
14
+ issues = Dir.glob("#{TEMP_DIR}/*#{FILE_EXT}").map do |file|
15
+ file.delete(TEMP_DIR).split(PREFIX_SEPARATOR).first
16
+ end
17
+
18
+ issues.uniq.sort.map { |issue| new(issue) }
19
+ end
20
+ end
21
+
22
+ attr_reader :issue
23
+
24
+ def initialize(issue)
25
+ ensure_tmp_dir
26
+ @issue = issue || automatic_issue
27
+ end
28
+
29
+ def start
30
+ return false if running?
31
+ Tempfile.create([log_file_prefix, FILE_EXT], TEMP_DIR)
32
+ end
33
+
34
+ def pause
35
+ log_files.each do |log|
36
+ FileUtils.touch(log) if log_running?(log)
37
+ end
38
+ end
39
+
40
+ def delete
41
+ return false unless exists?
42
+ log_files.each { |log| File.unlink log }
43
+ end
44
+
45
+ def runtime
46
+ @logs ||= log_files.each_with_object([]) do |log, array|
47
+ start_time = File.birthtime(log)
48
+ end_time = log_running?(log) ? Time.now : File.mtime(log)
49
+
50
+ array << (end_time - start_time)
51
+ end.sum
52
+ end
53
+
54
+ def running?
55
+ log_files.any? { |log| log_running?(log) }
56
+ end
57
+
58
+ def exists?
59
+ log_files.any?
60
+ end
61
+
62
+ private
63
+
64
+ def log_file_prefix
65
+ "#{issue}#{PREFIX_SEPARATOR}"
66
+ end
67
+
68
+ def log_running?(log)
69
+ File.birthtime(log).eql? File.mtime(log)
70
+ end
71
+
72
+ def log_files
73
+ @log_files ||= Dir.glob("#{TEMP_DIR}/#{issue}*#{FILE_EXT}")
74
+ end
75
+
76
+ def ensure_tmp_dir
77
+ FileUtils.mkdir_p TEMP_DIR unless File.directory? TEMP_DIR
78
+ end
79
+ end
80
+ end
81
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TempestTime
2
- VERSION = '0.6.2'.freeze
4
+ VERSION = '0.7.0'
3
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tempest_time
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Devan Hurst
@@ -463,11 +463,19 @@ files:
463
463
  - lib/tempest_time/commands/teams/add.rb
464
464
  - lib/tempest_time/commands/teams/delete.rb
465
465
  - lib/tempest_time/commands/teams/edit.rb
466
+ - lib/tempest_time/commands/timer.rb
467
+ - lib/tempest_time/commands/timer/delete.rb
468
+ - lib/tempest_time/commands/timer/list.rb
469
+ - lib/tempest_time/commands/timer/pause.rb
470
+ - lib/tempest_time/commands/timer/start.rb
471
+ - lib/tempest_time/commands/timer/status.rb
472
+ - lib/tempest_time/commands/timer/track.rb
466
473
  - lib/tempest_time/commands/track.rb
467
474
  - lib/tempest_time/helpers/formatting_helper.rb
468
475
  - lib/tempest_time/helpers/git_helper.rb
469
476
  - lib/tempest_time/helpers/time_helper.rb
470
477
  - lib/tempest_time/models/report.rb
478
+ - lib/tempest_time/models/timer.rb
471
479
  - lib/tempest_time/setting.rb
472
480
  - lib/tempest_time/settings/app.rb
473
481
  - lib/tempest_time/settings/authorization.rb