logstats 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rvmrc +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +21 -0
- data/README +71 -0
- data/Rakefile +2 -0
- data/bin/logstats +6 -0
- data/examples/example.haml +128 -0
- data/lib/logstats/haml/helpers.rb +46 -0
- data/lib/logstats/template.haml +123 -0
- data/lib/logstats/version.rb +3 -0
- data/lib/logstats/worklog.rb +154 -0
- data/lib/logstats.rb +40 -0
- data/logstats.gemspec +24 -0
- data/watchr/all.watchr +1 -0
- metadata +113 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
GIT
|
2
|
+
remote: git://github.com/jstirk/tail_from_sentinel.git
|
3
|
+
revision: def3003aa61a46dfd239cad938700d8ad26c2988
|
4
|
+
specs:
|
5
|
+
tail_from_sentinel (0.0.1)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
haml (3.0.25)
|
11
|
+
ruby-fsevent (0.2.1)
|
12
|
+
watchr (0.7)
|
13
|
+
|
14
|
+
PLATFORMS
|
15
|
+
ruby
|
16
|
+
|
17
|
+
DEPENDENCIES
|
18
|
+
haml
|
19
|
+
ruby-fsevent
|
20
|
+
tail_from_sentinel!
|
21
|
+
watchr
|
data/README
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
LogStats
|
2
|
+
========
|
3
|
+
|
4
|
+
Generates a simple HTML file based upon my custom timesheet format.
|
5
|
+
|
6
|
+
I can then embed this on my OSX Dashboard to be able to see how I'm
|
7
|
+
progressing throughout the day/week/month.
|
8
|
+
|
9
|
+
It's probably not so useful to you, unless you happen to like my
|
10
|
+
worklog format.
|
11
|
+
|
12
|
+
I run the script every 5 minutes with cron so as that the HTML stays
|
13
|
+
up-to-date. The page is set to auto-refresh every 2.5 minutes.
|
14
|
+
|
15
|
+
The colours change when I have done at least 5 hours of work per day,
|
16
|
+
and 25 hours per week.
|
17
|
+
|
18
|
+
Requirements
|
19
|
+
============
|
20
|
+
* HAML
|
21
|
+
* tail_from_sentinel
|
22
|
+
|
23
|
+
Usage
|
24
|
+
=====
|
25
|
+
|
26
|
+
logstats INPUTFILE.txt OUTPUTFILE.html
|
27
|
+
|
28
|
+
|
29
|
+
Worklog Format
|
30
|
+
==============
|
31
|
+
|
32
|
+
I use a text based worklog format for all my timekeeping.
|
33
|
+
|
34
|
+
This is a simple text file that sits in my Dropbox, and it symlinked to
|
35
|
+
my home. I keep it open in an editor all day so as that I can quickly jot
|
36
|
+
down when my task changes.
|
37
|
+
|
38
|
+
It works great for unexpected interruptions, as I can quickly update it
|
39
|
+
when I am back, or even while I am on the phone (only need to type the time
|
40
|
+
I answer the call).
|
41
|
+
|
42
|
+
The format looks like this :
|
43
|
+
|
44
|
+
01Jan2011
|
45
|
+
0900 ABC 1234567 1012
|
46
|
+
1015 ABC 9876543 1100
|
47
|
+
1100 ABC Deploy 1110
|
48
|
+
|
49
|
+
03Jan
|
50
|
+
1200 XY1 2345678 1230
|
51
|
+
|
52
|
+
Each day has a simple header in DDMMMYYYY format, where YYYY is
|
53
|
+
optional where it is the same as the record before.
|
54
|
+
|
55
|
+
Within the day, each line follows a simple format :
|
56
|
+
* the start time (in 24hr)
|
57
|
+
|
58
|
+
* a message, which starts with a 3-character project code (if billable).
|
59
|
+
I usually follow this with a Pivotal Tracker Story ID, or a note
|
60
|
+
about what I was doing.
|
61
|
+
If it's not billable work, I might use this space to log an
|
62
|
+
unexpected call for instance.
|
63
|
+
This field is freeform, with the project picked up if it's there.
|
64
|
+
|
65
|
+
* the end time (in 24hr). It's fine for this to be in the next day.
|
66
|
+
Eg: 2330 ABC allnighter 0230
|
67
|
+
|
68
|
+
TODO
|
69
|
+
====
|
70
|
+
* Tests!
|
71
|
+
* Make it more customizable. Not everyone has the same thresholds that I keep.
|
data/Rakefile
ADDED
data/bin/logstats
ADDED
@@ -0,0 +1,128 @@
|
|
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)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module LogStats
|
2
|
+
module Helpers
|
3
|
+
# NOTE: Because of the way I'm calling this (as the HAML context) these all need to be class methods
|
4
|
+
|
5
|
+
# Turns a number of seconds into a pretty HTML string
|
6
|
+
def self.time_to_html(seconds, options={})
|
7
|
+
hrs=(seconds / 3600).floor
|
8
|
+
min=(seconds % 3600).floor / 60
|
9
|
+
o=[]
|
10
|
+
o << "<span class=\"hour\">#{hrs}</span> hr" if hrs.to_i > 0
|
11
|
+
o << "<span class=\"minute\">#{min}</span> min" if min.to_i > 0
|
12
|
+
o.join(' ')
|
13
|
+
end
|
14
|
+
|
15
|
+
# Options:
|
16
|
+
# :class : The CSS class to apply to the top element (default: duration)
|
17
|
+
def self.duration_tag(seconds, options={})
|
18
|
+
options[:class]='duration' if options[:class].nil?
|
19
|
+
"<span class=\"#{options[:class]}\">#{self.time_to_html(seconds)}</span>"
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.remaining_tag(seconds_so_far, period)
|
23
|
+
seconds_required=case period
|
24
|
+
when :day
|
25
|
+
# 5 hrs per day
|
26
|
+
5 * 3600
|
27
|
+
when :week
|
28
|
+
# 5 hrs per day, 5 days per week
|
29
|
+
5 * 5 * 3600
|
30
|
+
else
|
31
|
+
raise "Unknown period: #{period}"
|
32
|
+
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
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
!!!
|
2
|
+
%html
|
3
|
+
%head
|
4
|
+
%title Log Stats @ #{Time.now.strftime('%a, %e %b %Y %H:%M')}
|
5
|
+
%meta{ "http-equiv" => "refresh", :content => "150" }
|
6
|
+
%style
|
7
|
+
:sass
|
8
|
+
$background_color_light: #454342
|
9
|
+
$background_color_dark: #333335
|
10
|
+
$text_color: white
|
11
|
+
$text_faded_color: #cacaca
|
12
|
+
$link_color: #a9c743
|
13
|
+
$border_color: #272b3b
|
14
|
+
$success_color: #a9c743
|
15
|
+
$success_faded_color: #7D9231
|
16
|
+
|
17
|
+
body
|
18
|
+
:font-size 62.5%
|
19
|
+
:font-family Helvetica, sans-serif
|
20
|
+
:background $background_color_light
|
21
|
+
:color $text_color
|
22
|
+
|
23
|
+
#logstats
|
24
|
+
:width 400px
|
25
|
+
:height 241px
|
26
|
+
:background $background_color_dark
|
27
|
+
:border 1px solid $border_color
|
28
|
+
:text-align center
|
29
|
+
|
30
|
+
h1
|
31
|
+
:font-size 120%
|
32
|
+
:text-decoration underline
|
33
|
+
:font-variant small-caps
|
34
|
+
:text-align center
|
35
|
+
:margin 0
|
36
|
+
:padding 0
|
37
|
+
|
38
|
+
ul
|
39
|
+
:list-style-type none
|
40
|
+
:margin 0
|
41
|
+
:padding 0
|
42
|
+
:text-align left
|
43
|
+
|
44
|
+
li
|
45
|
+
:margin 0
|
46
|
+
:padding 0
|
47
|
+
|
48
|
+
.duration
|
49
|
+
:font-size 200%
|
50
|
+
:color $text_faded_color
|
51
|
+
:line-height 40px
|
52
|
+
|
53
|
+
span
|
54
|
+
:color $text_color
|
55
|
+
|
56
|
+
.met
|
57
|
+
:color $success_color
|
58
|
+
|
59
|
+
.duration
|
60
|
+
:color $success_faded_color
|
61
|
+
|
62
|
+
span
|
63
|
+
:color $success_color
|
64
|
+
|
65
|
+
//.remaining, .average
|
66
|
+
|
67
|
+
.recent, .history
|
68
|
+
:width 200px
|
69
|
+
:float left
|
70
|
+
|
71
|
+
.recent
|
72
|
+
.current, .today
|
73
|
+
:padding 5px
|
74
|
+
|
75
|
+
.current
|
76
|
+
:height 50px
|
77
|
+
:border-bottom 1px solid $border-color
|
78
|
+
|
79
|
+
.today ul
|
80
|
+
.duration
|
81
|
+
:line-height 30px
|
82
|
+
|
83
|
+
.history
|
84
|
+
.today, .week, .month
|
85
|
+
:height 70px
|
86
|
+
:padding 5px
|
87
|
+
:border-left 1px solid $border_color
|
88
|
+
|
89
|
+
.today, .week
|
90
|
+
:border-bottom 1px solid $border-color
|
91
|
+
|
92
|
+
|
93
|
+
%body
|
94
|
+
#logstats
|
95
|
+
.recent
|
96
|
+
.current
|
97
|
+
%h1 Current Task
|
98
|
+
- if current then
|
99
|
+
= duration_tag(current)
|
100
|
+
- else
|
101
|
+
N/A
|
102
|
+
.today
|
103
|
+
%h1 Projects
|
104
|
+
%ul.projects
|
105
|
+
- today[:projects].each do |project, time|
|
106
|
+
%li
|
107
|
+
%span.project= project
|
108
|
+
= duration_tag(time)
|
109
|
+
.history
|
110
|
+
.today{ :class => (today[:remaining].nil? ? 'met' : nil )}
|
111
|
+
%h1 Today
|
112
|
+
= duration_tag(today[:total])
|
113
|
+
= remaining_tag(today[:total], :day)
|
114
|
+
|
115
|
+
.week{ :class => (week[:remaining].nil? ? 'met' : nil )}
|
116
|
+
%h1 Week
|
117
|
+
= duration_tag(week[:total])
|
118
|
+
= remaining_tag(week[:total], :week)
|
119
|
+
.month
|
120
|
+
%h1 Month
|
121
|
+
= duration_tag(month[:total])
|
122
|
+
.average
|
123
|
+
(#{time_to_html(month[:average])} average)
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'tail_from_sentinel'
|
2
|
+
require 'time'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
# Parses my own WorkLog file format and extracts the stats for the past month
|
6
|
+
module LogStats
|
7
|
+
class WorkLog < TailFromSentinel::Base
|
8
|
+
DATE_SENTINEL_REGEX=/(\d+)([A-Z]+)(\d{4})?$/i
|
9
|
+
WORKLOG_RECORD_REGEX=/(\d{2})(\d{2}) (.*) (\d{2})(\d{2})$/i
|
10
|
+
OPEN_WORKLOG_RECORD_REGEX=/(\d{2})(\d{2}) (.*)$/i
|
11
|
+
|
12
|
+
DATE_SENTINEL_PROC=Proc.new do |line|
|
13
|
+
if line =~ DATE_SENTINEL_REGEX then
|
14
|
+
# It's a date marker - parse it to see whether it's close enough to when we want
|
15
|
+
day,month,year=self.parse_sentinel($1, $2, $3)
|
16
|
+
(month.downcase == self.now.strftime('%b').downcase) && (year.to_i == self.now.year)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.now
|
21
|
+
@@now ||= Time.now
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.last_seen_year
|
25
|
+
@@last_seen_year
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.last_seen_year=(year)
|
29
|
+
@@last_seen_year=year
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.parse_sentinel(d,m,y)
|
33
|
+
year=y || self.last_seen_year
|
34
|
+
self.last_seen_year=year
|
35
|
+
[ d, m, year ]
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(filename)
|
39
|
+
@last_seen_year=nil
|
40
|
+
@stas=nil
|
41
|
+
super(File.open(filename, 'r'), &DATE_SENTINEL_PROC)
|
42
|
+
end
|
43
|
+
|
44
|
+
# TODO: Document this
|
45
|
+
def stats
|
46
|
+
return @stats unless @stats.nil?
|
47
|
+
|
48
|
+
days={}
|
49
|
+
now=WorkLog.now
|
50
|
+
current_time=nil
|
51
|
+
|
52
|
+
data.each do |line|
|
53
|
+
line=line.strip
|
54
|
+
case line
|
55
|
+
when DATE_SENTINEL_REGEX
|
56
|
+
# It's a date marker - set current_time
|
57
|
+
current_time=Time.parse(WorkLog.parse_sentinel($1,$2,$3).reverse.join('-'))
|
58
|
+
days[current_time]={ :total => 0, :projects => {} }
|
59
|
+
|
60
|
+
when WORKLOG_RECORD_REGEX
|
61
|
+
# It's a worklog record - parse it, and add it to the relevant buckets
|
62
|
+
duration, project=parse_worklog_record(current_time, $1, $2, $3, $4, $5)
|
63
|
+
|
64
|
+
days[current_time][:total] += duration
|
65
|
+
days[current_time][:projects][project] ||= 0
|
66
|
+
days[current_time][:projects][project] += duration
|
67
|
+
|
68
|
+
when OPEN_WORKLOG_RECORD_REGEX
|
69
|
+
duration, project=parse_worklog_record(current_time, $1, $2, $3)
|
70
|
+
days[:current]=duration
|
71
|
+
|
72
|
+
when /^\s*$/
|
73
|
+
# Blank line - ignore
|
74
|
+
else
|
75
|
+
puts "Warning: Unknown format of \"#{line}\""
|
76
|
+
end
|
77
|
+
end
|
78
|
+
@stats=compile_day_data(days)
|
79
|
+
end
|
80
|
+
|
81
|
+
def parse_worklog_record(current_time, sh,sm,msg,eh=nil,em=nil)
|
82
|
+
start_time=current_time + (sh.to_i * 3600) + (sm.to_i * 60)
|
83
|
+
|
84
|
+
if eh && em then
|
85
|
+
end_time=current_time + (eh.to_i * 3600) + (em.to_i * 60)
|
86
|
+
end_time += (24 * 3600) if end_time < start_time
|
87
|
+
else
|
88
|
+
end_time=Time.now
|
89
|
+
end
|
90
|
+
duration=end_time - start_time
|
91
|
+
|
92
|
+
if msg.match(/^([A-Z0-9]{3})/) then
|
93
|
+
project=$1
|
94
|
+
else
|
95
|
+
project='MISC'
|
96
|
+
end
|
97
|
+
[ duration, project ]
|
98
|
+
end
|
99
|
+
|
100
|
+
def compile_day_data(days)
|
101
|
+
stats={ :current => days[:current],
|
102
|
+
:today => { :total => 0,
|
103
|
+
:projects => { }
|
104
|
+
},
|
105
|
+
:week => { :total => 0,
|
106
|
+
:average => 0,
|
107
|
+
:projects => { }
|
108
|
+
},
|
109
|
+
:month => { :total => 0,
|
110
|
+
:average => 0,
|
111
|
+
:days_logged => 0,
|
112
|
+
:projects => { }
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
now=WorkLog.now
|
117
|
+
today=Date.today
|
118
|
+
|
119
|
+
days.each do |time, data|
|
120
|
+
next if time == :current
|
121
|
+
|
122
|
+
# It's data for today - use it
|
123
|
+
stats[:today]=data if time.day == now.day
|
124
|
+
|
125
|
+
if time_to_date(time).cweek == today.cweek then
|
126
|
+
stats[:week][:total] += data[:total]
|
127
|
+
stats[:week][:average] = (stats[:week][:total] / time_to_date(time).wday.to_f)
|
128
|
+
data[:projects].each do |project, duration|
|
129
|
+
stats[:week][:projects][project] ||= 0
|
130
|
+
stats[:week][:projects][project] += duration
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Everything is included in this month
|
135
|
+
stats[:month][:total] += data[:total]
|
136
|
+
stats[:month][:days_logged] += 1
|
137
|
+
stats[:month][:average] = (stats[:month][:total] / stats[:month][:days_logged])
|
138
|
+
data[:projects].each do |project, duration|
|
139
|
+
stats[:month][:projects][project] ||= 0
|
140
|
+
stats[:month][:projects][project] += duration
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
stats[:today][:total] += days[:current]
|
145
|
+
#stats[:today][:projects]['WIP'] = days[:current]
|
146
|
+
|
147
|
+
return stats
|
148
|
+
end
|
149
|
+
|
150
|
+
def time_to_date(time)
|
151
|
+
Date.new(time.year, time.month, time.day)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/logstats.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'haml'
|
2
|
+
require 'logstats/haml/helpers'
|
3
|
+
require 'logstats/worklog'
|
4
|
+
module LogStats
|
5
|
+
class Base
|
6
|
+
attr_reader :worklog
|
7
|
+
|
8
|
+
def initialize(source_file, output_path)
|
9
|
+
@worklog=WorkLog.new(source_file)
|
10
|
+
@output_path=output_path
|
11
|
+
@base_path=File.dirname(__FILE__)
|
12
|
+
end
|
13
|
+
|
14
|
+
def generate!
|
15
|
+
# Calculate the stats from the file
|
16
|
+
locals={}
|
17
|
+
@worklog.stats.each { |key, data| locals[key]=data }
|
18
|
+
process_haml(locals)
|
19
|
+
return true
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def process_haml(locals)
|
25
|
+
# Inject them into the HAML layout
|
26
|
+
haml=nil
|
27
|
+
File.open(File.join(@base_path, 'logstats', 'template.haml'), 'r') do |f|
|
28
|
+
haml=f.read
|
29
|
+
end
|
30
|
+
engine = Haml::Engine.new(haml)
|
31
|
+
html=engine.render(LogStats::Helpers, locals)
|
32
|
+
|
33
|
+
# Save the HAML to a file
|
34
|
+
File.open(@output_path, 'w') do |f|
|
35
|
+
f << html
|
36
|
+
end
|
37
|
+
return true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/logstats.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "logstats/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "logstats"
|
7
|
+
s.version = Logstats::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Jason Stirk"]
|
10
|
+
s.email = ["jstirk@oobleyboo.com"]
|
11
|
+
s.homepage = "http://github.com/jstirk/logstats"
|
12
|
+
s.summary = "Generates a simple HTML file based upon my custom timesheet format."
|
13
|
+
s.description = "Generates a simple HTML file based upon my custom timesheet format."
|
14
|
+
|
15
|
+
s.rubyforge_project = "logstats"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency('tail_from_sentinel', '>= 0.0.1')
|
23
|
+
s.add_dependency('haml', '~> 3.0.25')
|
24
|
+
end
|
data/watchr/all.watchr
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
watch( 'examples/.*\.haml' ) {|md| system("haml #{md[0]} #{md[0].gsub(/haml$/,'html')}") }
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: logstats
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jason Stirk
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-01-08 00:00:00 +11:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: tail_from_sentinel
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 29
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 0
|
33
|
+
- 1
|
34
|
+
version: 0.0.1
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: haml
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 53
|
46
|
+
segments:
|
47
|
+
- 3
|
48
|
+
- 0
|
49
|
+
- 25
|
50
|
+
version: 3.0.25
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
description: Generates a simple HTML file based upon my custom timesheet format.
|
54
|
+
email:
|
55
|
+
- jstirk@oobleyboo.com
|
56
|
+
executables:
|
57
|
+
- logstats
|
58
|
+
extensions: []
|
59
|
+
|
60
|
+
extra_rdoc_files: []
|
61
|
+
|
62
|
+
files:
|
63
|
+
- .gitignore
|
64
|
+
- .rvmrc
|
65
|
+
- Gemfile
|
66
|
+
- Gemfile.lock
|
67
|
+
- README
|
68
|
+
- Rakefile
|
69
|
+
- bin/logstats
|
70
|
+
- examples/example.haml
|
71
|
+
- lib/logstats.rb
|
72
|
+
- lib/logstats/haml/helpers.rb
|
73
|
+
- lib/logstats/template.haml
|
74
|
+
- lib/logstats/version.rb
|
75
|
+
- lib/logstats/worklog.rb
|
76
|
+
- logstats.gemspec
|
77
|
+
- watchr/all.watchr
|
78
|
+
has_rdoc: true
|
79
|
+
homepage: http://github.com/jstirk/logstats
|
80
|
+
licenses: []
|
81
|
+
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
hash: 3
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
version: "0"
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
hash: 3
|
102
|
+
segments:
|
103
|
+
- 0
|
104
|
+
version: "0"
|
105
|
+
requirements: []
|
106
|
+
|
107
|
+
rubyforge_project: logstats
|
108
|
+
rubygems_version: 1.3.7
|
109
|
+
signing_key:
|
110
|
+
specification_version: 3
|
111
|
+
summary: Generates a simple HTML file based upon my custom timesheet format.
|
112
|
+
test_files: []
|
113
|
+
|