tempest_time 0.6.2 → 0.7.0

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