twstats 0.1.4 → 0.2
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 +5 -5
- data/.gitignore +1 -0
- data/Gemfile.lock +3 -1
- data/lib/twstats.rb +2 -3
- data/lib/twstats/csv_reader.rb +33 -7
- data/lib/twstats/runner.rb +50 -13
- data/lib/twstats/tw_log.rb +3 -2
- data/twstats.gemspec +2 -1
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b03a403d2eac6f0356f6e8bad78c073c93565139101566472b67d1a2901c0368
|
4
|
+
data.tar.gz: 44e5e4ae4cd5eff7e937882fdf01dcddb12d3130a2427ee0ea08cee0a3ed8a1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f97828091ec24a7edc400dbf79a82ff4861ea9b17ee4cc937c2709c1fde0c1fae919534c1b89f39efcf22e31062dc37ea0c306e1e9f3f82dac1ef6854af7acff
|
7
|
+
data.tar.gz: f7aac1d4b1569fe79b5218601f59305f2b73409281f1cdeafd7eaa06c89b9ed28200d5913d915b779800cbd9aa537110509db078a41011fb72593af128ae2eff
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
twstats (0)
|
4
|
+
twstats (0.1.4)
|
5
|
+
rainbow (~> 3.0)
|
5
6
|
tty-prompt (~> 0.17)
|
6
7
|
|
7
8
|
GEM
|
@@ -14,6 +15,7 @@ GEM
|
|
14
15
|
pastel (0.7.2)
|
15
16
|
equatable (~> 0.5.0)
|
16
17
|
tty-color (~> 0.4.0)
|
18
|
+
rainbow (3.0.0)
|
17
19
|
rake (10.5.0)
|
18
20
|
rspec (3.8.0)
|
19
21
|
rspec-core (~> 3.8.0)
|
data/lib/twstats.rb
CHANGED
@@ -13,9 +13,8 @@ module Twstats
|
|
13
13
|
{name: 'Projects', value: 0},
|
14
14
|
{name: 'People', value: 1},
|
15
15
|
{name: 'Tags', value: 2},
|
16
|
-
{name: '
|
17
|
-
{name: '
|
18
|
-
{name: 'Back', value: 5}
|
16
|
+
{name: 'Full stats', value: 3},
|
17
|
+
{name: 'Back', value: 4}
|
19
18
|
]
|
20
19
|
MENU_CHOICES = [
|
21
20
|
{name: 'Stats', value: 0},
|
data/lib/twstats/csv_reader.rb
CHANGED
@@ -29,19 +29,45 @@ module Twstats
|
|
29
29
|
@logs << log
|
30
30
|
end
|
31
31
|
|
32
|
-
def
|
32
|
+
def mean_time
|
33
|
+
logs.inject(0) {|sum, log| sum + log.decimal_time }.to_f/logs.size
|
34
|
+
end
|
35
|
+
|
36
|
+
def mean_time_per_task
|
37
|
+
time_per_task = []
|
38
|
+
tasks.each do |task|
|
39
|
+
time_per_task << logs.select{|ll| ll.task == task}.inject(0){|sum, ll| sum + ll.decimal_time}
|
40
|
+
end
|
41
|
+
time_per_task.inject(0){|sum, x| sum + x}/time_per_task.size
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_total_time(object, element, filter_billable)
|
45
|
+
billable = 0
|
46
|
+
non_billable = 0
|
33
47
|
case object
|
34
48
|
when :projects
|
35
|
-
logs.select{|log| log.project == element}.inject(0){ |sum, l| sum + l.decimal_time}
|
49
|
+
billable = logs.select{|log| log.project == element && log.billable}.inject(0){ |sum, l| sum + l.decimal_time}
|
50
|
+
non_billable = logs.select{|log| log.project == element && !log.billable}.inject(0){ |sum, l| sum + l.decimal_time}
|
36
51
|
when :tags
|
37
|
-
logs.select{|log| log.tags.include?(element)}.inject(0){ |sum, l| sum + l.decimal_time}
|
52
|
+
billable = logs.select{|log| log.tags.include?(element) && log.billable}.inject(0){ |sum, l| sum + l.decimal_time}
|
53
|
+
non_billable = logs.select{|log| log.tags.include?(element) && !log.billable}.inject(0){ |sum, l| sum + l.decimal_time}
|
38
54
|
when :people
|
39
|
-
logs.select{|log| log.who == element}.inject(0){ |sum, l| sum + l.decimal_time}
|
55
|
+
billable = logs.select{|log| log.who == element && log.billable}.inject(0){ |sum, l| sum + l.decimal_time}
|
56
|
+
non_billable = logs.select{|log| log.who == element && !log.billable}.inject(0){ |sum, l| sum + l.decimal_time}
|
40
57
|
when :tasks
|
41
|
-
logs.select{|log| log.task == element}.inject(0){ |sum, l| sum + l.decimal_time}
|
58
|
+
billable = logs.select{|log| log.task == element && log.billable}.inject(0){ |sum, l| sum + l.decimal_time}
|
59
|
+
non_billable = logs.select{|log| log.task == element && !log.billable}.inject(0){ |sum, l| sum + l.decimal_time}
|
60
|
+
when :all
|
61
|
+
billable = logs.select{|log| log.billable}.inject(0){ |sum, l| sum + l.decimal_time}
|
62
|
+
non_billable = logs.select{|log| !log.billable}.inject(0){ |sum, l| sum + l.decimal_time}
|
42
63
|
else
|
43
|
-
-99999
|
64
|
+
return -99999
|
65
|
+
end
|
66
|
+
if filter_billable
|
67
|
+
return billable, non_billable
|
68
|
+
else
|
69
|
+
return billable + non_billable
|
44
70
|
end
|
45
71
|
end
|
46
72
|
end
|
47
|
-
end
|
73
|
+
end
|
data/lib/twstats/runner.rb
CHANGED
@@ -1,20 +1,22 @@
|
|
1
1
|
require 'tty-prompt'
|
2
|
+
require 'rainbow/refinement'
|
3
|
+
using Rainbow
|
2
4
|
|
3
5
|
require_relative 'csv_reader'
|
4
6
|
|
5
7
|
module Twstats
|
6
8
|
class Runner
|
7
|
-
def initialize
|
9
|
+
def initialize(file = nil)
|
8
10
|
# Load interactive console
|
9
11
|
@prompt = TTY::Prompt.new
|
10
|
-
puts WELLCOME_MESSAGE
|
12
|
+
puts WELLCOME_MESSAGE.bright
|
11
13
|
# Ask for the csv file
|
12
|
-
file
|
14
|
+
file ||= @prompt.ask('Specify the CSV file from a Teamwork time log export', default: 'exportTimeLog.csv') do |input|
|
13
15
|
input.modify :chomp
|
14
16
|
end
|
15
17
|
@csv = CSVReader.new(file)
|
16
18
|
loop do
|
17
|
-
option = @prompt.select("Choose an option", Twstats::MENU_CHOICES)
|
19
|
+
option = @prompt.select("Choose an option", Twstats::MENU_CHOICES, cycle: true)
|
18
20
|
case option
|
19
21
|
when 0
|
20
22
|
show_stats_menu
|
@@ -30,7 +32,7 @@ module Twstats
|
|
30
32
|
|
31
33
|
def show_stats_menu
|
32
34
|
loop do
|
33
|
-
option = @prompt.select("Select what time logging stats you want to see", Twstats::STATS_MENU_CHOICES)
|
35
|
+
option = @prompt.select("Select what time logging stats you want to see", Twstats::STATS_MENU_CHOICES, cycle: true)
|
34
36
|
case option
|
35
37
|
when 0
|
36
38
|
show_stats :projects
|
@@ -39,10 +41,8 @@ module Twstats
|
|
39
41
|
when 2
|
40
42
|
show_stats :tags
|
41
43
|
when 3
|
42
|
-
show_bil_vs_non_stats
|
43
|
-
when 4
|
44
44
|
show_full_stats
|
45
|
-
when
|
45
|
+
when 4
|
46
46
|
return
|
47
47
|
else
|
48
48
|
puts 'Option not recognized'
|
@@ -50,16 +50,22 @@ module Twstats
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
def show_stats(obj)
|
54
|
-
puts "Time logged vs #{obj.to_s}:"
|
53
|
+
def show_stats(obj, filter_billable = nil)
|
54
|
+
puts "Time logged vs #{obj.to_s}:" if filter_billable.nil?
|
55
55
|
toshow = {}
|
56
56
|
max = 0
|
57
|
+
filter_billable ||= @prompt.yes?('Do you want to filter billable and non-billable time?', default: true)
|
57
58
|
@csv.send(obj).each do |element|
|
58
|
-
toshow[element] = @csv.get_total_time(obj, element)
|
59
|
+
toshow[element] = @csv.get_total_time(obj, element, filter_billable)
|
59
60
|
max = element.size if max < element.size
|
60
61
|
end
|
61
62
|
toshow.sort_by{ |k,v| v}.reverse.to_h.each do |k,v|
|
62
|
-
|
63
|
+
if filter_billable
|
64
|
+
puts " - #{k.ljust(max,' ').bright.blue} | #{"Billable:".ljust(13," ").bright} #{v[0].round(2)} (#{((v[0]/(v[0]+v[1]))*100).round(2)} %)" unless v[0].zero?
|
65
|
+
puts " #{"".ljust(max,' ').bright.blue} | #{"Non-billable:".bright} #{v[1].round(2)}" unless v[1].zero?
|
66
|
+
else
|
67
|
+
puts " - #{k.ljust(max,' ').bright.blue} | #{v.round(2)}" unless v.zero?
|
68
|
+
end
|
63
69
|
end
|
64
70
|
end
|
65
71
|
|
@@ -67,5 +73,36 @@ module Twstats
|
|
67
73
|
|
68
74
|
end
|
69
75
|
|
76
|
+
def show_full_stats
|
77
|
+
billable, non_billable = @csv.get_total_time(:all, nil, true)
|
78
|
+
to_bill = @prompt.yes? 'Do you want to calculate the billed amount?'
|
79
|
+
if to_bill
|
80
|
+
amount_per_hour = @prompt.ask 'What the amount billed per hour?', default: 46
|
81
|
+
end
|
82
|
+
section "Total time spent"
|
83
|
+
puts " - Billable ".bright.blue + " | " + billable.round(2).to_s
|
84
|
+
puts " - Non-billable ".bright.blue + " | " + non_billable.round(2).to_s
|
85
|
+
if to_bill
|
86
|
+
puts " - Billed amount ".bright.blue + " | " + (billable*amount_per_hour).round(2).to_s + " € "
|
87
|
+
end
|
88
|
+
section "People involved"
|
89
|
+
show_stats :people, true
|
90
|
+
section "Tags used"
|
91
|
+
show_stats :tags, true
|
92
|
+
section "Usefull metrics"
|
93
|
+
show_metrics
|
94
|
+
end
|
95
|
+
|
96
|
+
def section(text)
|
97
|
+
puts ""
|
98
|
+
puts (" "+text+" ").center(70,"*").bright
|
99
|
+
end
|
100
|
+
|
101
|
+
def show_metrics
|
102
|
+
mean = @csv.logs.inject(0) {|sum, log| sum + log.decimal_time }.to_f/@csv.logs.size
|
103
|
+
mean_per_task = @csv.mean_time_per_task
|
104
|
+
puts " - Mean time logged: ".bright.blue + mean.round(2).to_s
|
105
|
+
puts " - Mean time per task: ".bright.blue + mean_per_task.round(2).to_s
|
106
|
+
end
|
70
107
|
end
|
71
|
-
end
|
108
|
+
end
|
data/lib/twstats/tw_log.rb
CHANGED
@@ -8,6 +8,7 @@ module Twstats
|
|
8
8
|
attr_reader :tags
|
9
9
|
attr_reader :project
|
10
10
|
attr_reader :task
|
11
|
+
attr_reader :billable
|
11
12
|
|
12
13
|
|
13
14
|
# Class use to store the information needed form a given log
|
@@ -20,7 +21,7 @@ module Twstats
|
|
20
21
|
@tags = row["Tags"].split(',')
|
21
22
|
@project = row["Project"]
|
22
23
|
@task = row["Task"]
|
23
|
-
@billable = row["Is it Billable?"]
|
24
|
+
@billable = !row["Is it Billable?"].to_i.zero?
|
24
25
|
end
|
25
26
|
end
|
26
|
-
end
|
27
|
+
end
|
data/twstats.gemspec
CHANGED
@@ -36,6 +36,7 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.add_development_dependency "rake", "~> 10.0"
|
37
37
|
spec.add_development_dependency "rspec", "~> 3.0"
|
38
38
|
spec.add_runtime_dependency "tty-prompt", "~> 0.17"
|
39
|
+
spec.add_runtime_dependency "rainbow", "~> 3.0"
|
39
40
|
|
40
|
-
spec.required_ruby_version = '~> 2.
|
41
|
+
spec.required_ruby_version = '~> 2.1'
|
41
42
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twstats
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.2'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- J.P. Araque
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0.17'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rainbow
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
69
83
|
description: Teamwork allows to log time for all tasks within a project but the stats
|
70
84
|
capabilities do not allow for a quick overview of time loggin stats. TWStats produces
|
71
85
|
useful stats from a CSV file exported from Teamwork
|
@@ -107,7 +121,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
107
121
|
requirements:
|
108
122
|
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
|
-
version: '2.
|
124
|
+
version: '2.1'
|
111
125
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
126
|
requirements:
|
113
127
|
- - ">="
|
@@ -115,7 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
115
129
|
version: '0'
|
116
130
|
requirements: []
|
117
131
|
rubyforge_project:
|
118
|
-
rubygems_version: 2.
|
132
|
+
rubygems_version: 2.7.7
|
119
133
|
signing_key:
|
120
134
|
specification_version: 4
|
121
135
|
summary: Read a csv file generate by Teamwork to quickly get some stats from time
|