logstats 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/CHANGELOG +6 -2
- data/lib/logstats/haml/helpers.rb +36 -12
- data/lib/logstats/template.haml +26 -13
- data/lib/logstats/version.rb +1 -1
- data/lib/logstats/worklog.rb +31 -9
- metadata +3 -6
- data/.rvmrc +0 -7
- data/examples/example.haml +0 -128
- data/watchr/all.watchr +0 -1
data/.gitignore
CHANGED
data/CHANGELOG
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
+
0.0.3 (08Dec2011)
|
2
|
+
BUGFIX: Update to correctly distinguish between billable (with project code) and non-billable (without project code).
|
3
|
+
Added basic "% billable" statistic, and total time for the period. Default display is now billable.
|
4
|
+
Tweaked styles to work better with the additional data.
|
1
5
|
0.0.2 (08Dec2011)
|
2
|
-
BUGFIX: Failed to calculate today's time when there wasn't an open task
|
6
|
+
BUGFIX: Failed to calculate today's time when there wasn't an open task.
|
3
7
|
0.0.1 (08Dec2011)
|
4
|
-
Initial Release
|
8
|
+
Initial Release.
|
@@ -9,6 +9,9 @@ module Helpers
|
|
9
9
|
o=[]
|
10
10
|
o << "<span class=\"hour\">#{hrs}</span> hr" if hrs.to_i > 0
|
11
11
|
o << "<span class=\"minute\">#{min}</span> min" if min.to_i > 0
|
12
|
+
if o.size == 0 then
|
13
|
+
o << "<span class=\"none\">NONE :(</span>"
|
14
|
+
end
|
12
15
|
o.join(' ')
|
13
16
|
end
|
14
17
|
|
@@ -19,8 +22,40 @@ module Helpers
|
|
19
22
|
"<span class=\"#{options[:class]}\">#{self.time_to_html(seconds)}</span>"
|
20
23
|
end
|
21
24
|
|
25
|
+
def self.threshold_met?(seconds, period)
|
26
|
+
(self.threshold_for_period(period) - seconds) < 0
|
27
|
+
end
|
28
|
+
|
22
29
|
def self.remaining_tag(seconds_so_far, period)
|
23
|
-
|
30
|
+
seconds=self.threshold_for_period(period) - seconds_so_far
|
31
|
+
|
32
|
+
css_class=[ 'remaining ']
|
33
|
+
if seconds < 0 then
|
34
|
+
content=self.time_to_html(seconds.abs) + ' over!'
|
35
|
+
css_class << 'met'
|
36
|
+
else
|
37
|
+
content=self.time_to_html(seconds) + ' remaining'
|
38
|
+
end
|
39
|
+
"<div class=\"#{css_class.join(' ')}\">#{content}</div>"
|
40
|
+
end
|
41
|
+
|
42
|
+
# Outputs some basic productivity statistics
|
43
|
+
def self.productivity_tag(data)
|
44
|
+
o=[]
|
45
|
+
css_classes=[ 'productivity' ]
|
46
|
+
|
47
|
+
if data[:total] > 0 then
|
48
|
+
o << self.time_to_html(data[:total]) if data[:total] > 0
|
49
|
+
percent=((data[:billable] / data[:total]) * 100).ceil
|
50
|
+
o << "#{percent}% billable"
|
51
|
+
end
|
52
|
+
|
53
|
+
"<div class=\"#{css_classes.join(' ')}\">#{o.join(' | ')}</div>"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the threshold number of seconds that need to be attained each period
|
57
|
+
def self.threshold_for_period(period)
|
58
|
+
case period
|
24
59
|
when :day
|
25
60
|
# 5 hrs per day
|
26
61
|
5 * 3600
|
@@ -30,17 +65,6 @@ module Helpers
|
|
30
65
|
else
|
31
66
|
raise "Unknown period: #{period}"
|
32
67
|
end
|
33
|
-
|
34
|
-
seconds=seconds_required - seconds_so_far
|
35
|
-
|
36
|
-
css_class=[ 'remaining ']
|
37
|
-
if seconds.nil? || seconds < 0 then
|
38
|
-
content=self.time_to_html(seconds.abs) + ' over!'
|
39
|
-
css_class << 'met'
|
40
|
-
else
|
41
|
-
content=self.time_to_html(seconds) + ' remaining'
|
42
|
-
end
|
43
|
-
"<div class=\"#{css_class.join(' ')}\">(#{content})</div>"
|
44
68
|
end
|
45
69
|
end
|
46
70
|
end
|
data/lib/logstats/template.haml
CHANGED
@@ -46,23 +46,30 @@
|
|
46
46
|
:padding 0
|
47
47
|
|
48
48
|
.duration
|
49
|
-
:font-size 200%
|
50
49
|
:color $text_faded_color
|
51
50
|
:line-height 40px
|
51
|
+
:font-variant small-caps
|
52
|
+
:font-size 150%
|
52
53
|
|
53
54
|
span
|
54
55
|
:color $text_color
|
56
|
+
// 1.5x bigger than the HR/MIN
|
57
|
+
:font-size 150%
|
55
58
|
|
56
59
|
.met
|
57
60
|
:color $success_color
|
58
61
|
|
62
|
+
.remaining
|
63
|
+
:color $success_color
|
64
|
+
|
59
65
|
.duration
|
60
66
|
:color $success_faded_color
|
61
67
|
|
62
68
|
span
|
63
69
|
:color $success_color
|
64
70
|
|
65
|
-
|
71
|
+
.remaining
|
72
|
+
:color red
|
66
73
|
|
67
74
|
.recent, .history
|
68
75
|
:width 200px
|
@@ -86,6 +93,9 @@
|
|
86
93
|
:padding 5px
|
87
94
|
:border-left 1px solid $border_color
|
88
95
|
|
96
|
+
.duration
|
97
|
+
:line-height 35px
|
98
|
+
|
89
99
|
.today, .week
|
90
100
|
:border-bottom 1px solid $border-color
|
91
101
|
|
@@ -96,28 +106,31 @@
|
|
96
106
|
.current
|
97
107
|
%h1 Current Task
|
98
108
|
- if current then
|
99
|
-
= duration_tag(current)
|
109
|
+
= duration_tag(current[:total])
|
100
110
|
- else
|
101
111
|
N/A
|
102
112
|
.today
|
103
|
-
%h1 Projects
|
113
|
+
%h1 Today's Projects
|
104
114
|
%ul.projects
|
105
115
|
- today[:projects].each do |project, time|
|
106
116
|
%li
|
107
|
-
%span.project= project
|
117
|
+
%span.project= project || "Misc."
|
108
118
|
= duration_tag(time)
|
109
119
|
.history
|
110
|
-
.today{ :class => (today[:
|
120
|
+
.today{ :class => (threshold_met?(today[:billable], :day) ? 'met' : nil )}
|
111
121
|
%h1 Today
|
112
|
-
= duration_tag(today[:
|
113
|
-
= remaining_tag(today[:
|
122
|
+
= duration_tag(today[:billable])
|
123
|
+
= remaining_tag(today[:billable], :day)
|
124
|
+
= productivity_tag(today)
|
114
125
|
|
115
|
-
.week{ :class => (week[:
|
126
|
+
.week{ :class => (threshold_met?(week[:billable], :week) ? 'met' : nil )}
|
116
127
|
%h1 Week
|
117
|
-
= duration_tag(week[:
|
118
|
-
= remaining_tag(week[:
|
128
|
+
= duration_tag(week[:billable])
|
129
|
+
= remaining_tag(week[:billable], :week)
|
130
|
+
= productivity_tag(week)
|
119
131
|
.month
|
120
132
|
%h1 Month
|
121
|
-
= duration_tag(month[:
|
133
|
+
= duration_tag(month[:billable])
|
122
134
|
.average
|
123
|
-
|
135
|
+
#{time_to_html(month[:average])} / day avg.
|
136
|
+
= productivity_tag(month)
|
data/lib/logstats/version.rb
CHANGED
data/lib/logstats/worklog.rb
CHANGED
@@ -55,19 +55,21 @@ class WorkLog < TailFromSentinel::Base
|
|
55
55
|
when DATE_SENTINEL_REGEX
|
56
56
|
# It's a date marker - set current_time
|
57
57
|
current_time=Time.parse(WorkLog.parse_sentinel($1,$2,$3).reverse.join('-'))
|
58
|
-
days[current_time]={ :total => 0, :projects => {} }
|
58
|
+
days[current_time]={ :total => 0, :billable => 0, :projects => {} }
|
59
59
|
|
60
60
|
when WORKLOG_RECORD_REGEX
|
61
61
|
# It's a worklog record - parse it, and add it to the relevant buckets
|
62
62
|
duration, project=parse_worklog_record(current_time, $1, $2, $3, $4, $5)
|
63
63
|
|
64
64
|
days[current_time][:total] += duration
|
65
|
+
days[current_time][:billable] += duration if project
|
65
66
|
days[current_time][:projects][project] ||= 0
|
66
67
|
days[current_time][:projects][project] += duration
|
67
68
|
|
68
69
|
when OPEN_WORKLOG_RECORD_REGEX
|
69
70
|
duration, project=parse_worklog_record(current_time, $1, $2, $3)
|
70
|
-
days[:current]=duration
|
71
|
+
days[:current]={ :total => duration, :billable => 0 }
|
72
|
+
days[:current][:billable] = duration if project
|
71
73
|
|
72
74
|
when /^\s*$/
|
73
75
|
# Blank line - ignore
|
@@ -92,7 +94,7 @@ class WorkLog < TailFromSentinel::Base
|
|
92
94
|
if msg.match(/^([A-Z0-9]{3})/) then
|
93
95
|
project=$1
|
94
96
|
else
|
95
|
-
project=
|
97
|
+
project=false
|
96
98
|
end
|
97
99
|
[ duration, project ]
|
98
100
|
end
|
@@ -100,14 +102,19 @@ class WorkLog < TailFromSentinel::Base
|
|
100
102
|
def compile_day_data(days)
|
101
103
|
stats={ :current => days[:current],
|
102
104
|
:today => { :total => 0,
|
105
|
+
:billable => 0,
|
103
106
|
:projects => { }
|
104
107
|
},
|
105
108
|
:week => { :total => 0,
|
109
|
+
:billable => 0,
|
106
110
|
:average => 0,
|
111
|
+
:average_billable => 0,
|
107
112
|
:projects => { }
|
108
113
|
},
|
109
114
|
:month => { :total => 0,
|
115
|
+
:billable => 0,
|
110
116
|
:average => 0,
|
117
|
+
:average_billable => 0,
|
111
118
|
:days_logged => 0,
|
112
119
|
:projects => { }
|
113
120
|
}
|
@@ -124,25 +131,40 @@ class WorkLog < TailFromSentinel::Base
|
|
124
131
|
|
125
132
|
if time_to_date(time).cweek == today.cweek then
|
126
133
|
stats[:week][:total] += data[:total]
|
134
|
+
stats[:week][:billable] += data[:billable]
|
127
135
|
stats[:week][:average] = (stats[:week][:total] / time_to_date(time).wday.to_f)
|
136
|
+
stats[:week][:average_billable] = (stats[:week][:billable] / time_to_date(time).wday.to_f)
|
128
137
|
data[:projects].each do |project, duration|
|
129
|
-
|
130
|
-
|
138
|
+
if project then
|
139
|
+
stats[:week][:projects][project] ||= 0
|
140
|
+
stats[:week][:projects][project] += duration
|
141
|
+
end
|
131
142
|
end
|
132
143
|
end
|
133
144
|
|
134
145
|
# Everything is included in this month
|
135
146
|
stats[:month][:total] += data[:total]
|
147
|
+
stats[:month][:billable] += data[:billable]
|
136
148
|
stats[:month][:days_logged] += 1
|
137
149
|
stats[:month][:average] = (stats[:month][:total] / stats[:month][:days_logged])
|
150
|
+
stats[:month][:average_billable] = (stats[:month][:billable] / stats[:month][:days_logged])
|
138
151
|
data[:projects].each do |project, duration|
|
139
|
-
|
140
|
-
|
152
|
+
if project then
|
153
|
+
stats[:month][:projects][project] ||= 0
|
154
|
+
stats[:month][:projects][project] += duration
|
155
|
+
end
|
141
156
|
end
|
142
157
|
end
|
143
158
|
|
144
|
-
|
145
|
-
|
159
|
+
# Now add in the current task, if it is present
|
160
|
+
if days[:current] then
|
161
|
+
stats[:today][:total] += days[:current][:total]
|
162
|
+
stats[:today][:billable] += days[:current][:billable]
|
163
|
+
stats[:week][:total] += days[:current][:total]
|
164
|
+
stats[:week][:billable] += days[:current][:billable]
|
165
|
+
stats[:month][:total] += days[:current][:total]
|
166
|
+
stats[:month][:billable] += days[:current][:billable]
|
167
|
+
end
|
146
168
|
|
147
169
|
return stats
|
148
170
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstats
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 3
|
10
|
+
version: 0.0.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jason Stirk
|
@@ -61,21 +61,18 @@ extra_rdoc_files: []
|
|
61
61
|
|
62
62
|
files:
|
63
63
|
- .gitignore
|
64
|
-
- .rvmrc
|
65
64
|
- CHANGELOG
|
66
65
|
- Gemfile
|
67
66
|
- Gemfile.lock
|
68
67
|
- README
|
69
68
|
- Rakefile
|
70
69
|
- bin/logstats
|
71
|
-
- examples/example.haml
|
72
70
|
- lib/logstats.rb
|
73
71
|
- lib/logstats/haml/helpers.rb
|
74
72
|
- lib/logstats/template.haml
|
75
73
|
- lib/logstats/version.rb
|
76
74
|
- lib/logstats/worklog.rb
|
77
75
|
- logstats.gemspec
|
78
|
-
- watchr/all.watchr
|
79
76
|
has_rdoc: true
|
80
77
|
homepage: http://github.com/jstirk/logstats
|
81
78
|
licenses: []
|
data/.rvmrc
DELETED
data/examples/example.haml
DELETED
@@ -1,128 +0,0 @@
|
|
1
|
-
!!!
|
2
|
-
%html
|
3
|
-
%head
|
4
|
-
%title Log Stats @ 2011-01-18 07:09
|
5
|
-
%style
|
6
|
-
:sass
|
7
|
-
$background_color_light: #454342
|
8
|
-
$background_color_dark: #333335
|
9
|
-
$text_color: white
|
10
|
-
$link_color: #a9c743
|
11
|
-
$border_color: #272b3b
|
12
|
-
|
13
|
-
body
|
14
|
-
:font-size 62.5%
|
15
|
-
:font-family Helvetica, sans-serif
|
16
|
-
:background $background_color_light
|
17
|
-
:color $text_color
|
18
|
-
|
19
|
-
#logstats
|
20
|
-
:width 400px
|
21
|
-
:height 241px
|
22
|
-
:background $background_color_dark
|
23
|
-
:border 1px solid $border_color
|
24
|
-
:text-align center
|
25
|
-
|
26
|
-
h1
|
27
|
-
:font-size 120%
|
28
|
-
:text-decoration underline
|
29
|
-
:font-variant small-caps
|
30
|
-
:text-align center
|
31
|
-
:margin 0 0 10px 0
|
32
|
-
:padding 0
|
33
|
-
|
34
|
-
ul
|
35
|
-
:list-style-type none
|
36
|
-
:margin 0
|
37
|
-
:padding 0
|
38
|
-
:text-align left
|
39
|
-
|
40
|
-
li
|
41
|
-
:margin 0
|
42
|
-
:padding 0
|
43
|
-
|
44
|
-
.duration
|
45
|
-
:font-size 200%
|
46
|
-
:color #cacaca
|
47
|
-
|
48
|
-
span
|
49
|
-
:color $text_color
|
50
|
-
|
51
|
-
.remaining, .average
|
52
|
-
:margin-top 5px
|
53
|
-
|
54
|
-
.recent, .history
|
55
|
-
:width 200px
|
56
|
-
:float left
|
57
|
-
|
58
|
-
.recent
|
59
|
-
.current, .today
|
60
|
-
:padding 5px
|
61
|
-
|
62
|
-
.current
|
63
|
-
:height 50px
|
64
|
-
:border-bottom 1px solid $border-color
|
65
|
-
|
66
|
-
.history
|
67
|
-
.today, .week, .month
|
68
|
-
:height 70px
|
69
|
-
:padding 5px
|
70
|
-
:border-left 1px solid $border_color
|
71
|
-
|
72
|
-
.today, .week
|
73
|
-
:border-bottom 1px solid $border-color
|
74
|
-
|
75
|
-
|
76
|
-
%body
|
77
|
-
#logstats
|
78
|
-
.recent
|
79
|
-
.current
|
80
|
-
%h1 Current
|
81
|
-
%span.duration
|
82
|
-
%span.minute 45
|
83
|
-
min
|
84
|
-
.today
|
85
|
-
%h1 Projects
|
86
|
-
%ul.projects
|
87
|
-
%li
|
88
|
-
%span.project MB1
|
89
|
-
%span.duration
|
90
|
-
%span.hour 1
|
91
|
-
hr
|
92
|
-
%span.minute 57
|
93
|
-
min
|
94
|
-
%li
|
95
|
-
%span.project MY1
|
96
|
-
%span.duration
|
97
|
-
%span.hour 2
|
98
|
-
hr
|
99
|
-
%span.minute 30
|
100
|
-
min
|
101
|
-
.history
|
102
|
-
.today
|
103
|
-
%h1 Today
|
104
|
-
%span.duration
|
105
|
-
%span.hour 5
|
106
|
-
hr
|
107
|
-
%span.minute 57
|
108
|
-
min
|
109
|
-
.remaining
|
110
|
-
(target met!)
|
111
|
-
.week
|
112
|
-
%h1 Week
|
113
|
-
%span.duration
|
114
|
-
%span.hour 5
|
115
|
-
hr
|
116
|
-
%span.minute 57
|
117
|
-
min
|
118
|
-
.remaining
|
119
|
-
(20 hr 3 min remaining)
|
120
|
-
.month
|
121
|
-
%h1 Month
|
122
|
-
%span.duration
|
123
|
-
%span.hour 35
|
124
|
-
hr
|
125
|
-
%span.minute 57
|
126
|
-
min
|
127
|
-
.average
|
128
|
-
(6 hr 12 min average)
|
data/watchr/all.watchr
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
watch( 'examples/.*\.haml' ) {|md| system("haml #{md[0]} #{md[0].gsub(/haml$/,'html')}") }
|