timesheets 1.1.1 → 1.2.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 +4 -4
- data/.editorconfig +7 -0
- data/lib/timesheets/cli.rb +7 -1
- data/lib/timesheets/commands/base.rb +8 -2
- data/lib/timesheets/commands/summary.rb +79 -22
- data/lib/timesheets/csv_table.rb +20 -0
- data/lib/timesheets/html_table.rb +76 -0
- data/lib/timesheets/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c632d977a47f14254837ce09b33af73f1e61725d
|
4
|
+
data.tar.gz: 427c2f8e184c21b261b5ebc4051f6e50ee7551f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 90c5365557a0560e2b07d40e576c75da15b59e6898a8f36c27c5468a0f07fffac3e34c21afe3fe2738e1c65abfad25cb15157b2207da63965876b49ec674a8c2
|
7
|
+
data.tar.gz: d87d8e382e56c7f56383a386e201c3b893df17153a3cced8864445224c712436c09537f55b3c484bb8541be06027fed1ec88dbb307d00a9ee0aad26a3d572790
|
data/.editorconfig
ADDED
data/lib/timesheets/cli.rb
CHANGED
@@ -7,6 +7,8 @@ require "timesheets/commands/edit"
|
|
7
7
|
require "timesheets/commands/start"
|
8
8
|
require "timesheets/commands/status"
|
9
9
|
require "timesheets/commands/stop"
|
10
|
+
require 'timesheets/html_table'
|
11
|
+
require 'timesheets/csv_table'
|
10
12
|
require "timesheets/commands/summary"
|
11
13
|
|
12
14
|
module Timesheets
|
@@ -32,8 +34,12 @@ module Timesheets
|
|
32
34
|
end
|
33
35
|
|
34
36
|
desc "summary", "See a summary table of time worked"
|
37
|
+
option :format
|
38
|
+
option :'week-of'
|
39
|
+
option :rate
|
40
|
+
option :template
|
35
41
|
def summary
|
36
|
-
Commands::Summary.run
|
42
|
+
Commands::Summary.run(options)
|
37
43
|
end
|
38
44
|
end
|
39
45
|
end
|
@@ -7,29 +7,60 @@ module Timesheets
|
|
7
7
|
|
8
8
|
private
|
9
9
|
|
10
|
+
def tableClass
|
11
|
+
{
|
12
|
+
'html' => Timesheets::HTMLTable,
|
13
|
+
'csv' => Timesheets::CSVTable,
|
14
|
+
}[options[:format]] || Terminal::Table
|
15
|
+
end
|
16
|
+
|
17
|
+
def table_renderer(&block)
|
18
|
+
tableClass.new({ headings: heading, template: options[:template] }, &block)
|
19
|
+
end
|
20
|
+
|
10
21
|
def summary_table
|
11
|
-
|
22
|
+
table_renderer {|t|
|
12
23
|
entries_by_week.each_with_index {|entries, index|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
}
|
18
|
-
|
19
|
-
t << (heading.length - 2).times.map { '' } + ['Total:', sprintf('%0.02f', total_hours)]
|
24
|
+
rows_for_entries(entries).each {|row| t << row }
|
25
|
+
t << :separator unless index == entries_by_week.length - 1
|
26
|
+
}
|
27
|
+
t << total_row(entries) unless options['week-of']
|
20
28
|
|
21
29
|
heading.length.times {|i| t.align_column(i, :right) }
|
22
30
|
}
|
23
31
|
end
|
24
32
|
|
33
|
+
def rows_for_entries(entries)
|
34
|
+
format_entries(entries) + [
|
35
|
+
:separator,
|
36
|
+
total_row(entries)
|
37
|
+
]
|
38
|
+
end
|
39
|
+
|
25
40
|
def heading
|
26
|
-
['Weekday', 'Date', 'Time', 'Hour(s)']
|
41
|
+
the_heading = ['Weekday', 'Date', 'Time', 'Hour(s)']
|
42
|
+
|
43
|
+
the_heading += ['Hourly Rate', 'Total']
|
44
|
+
|
45
|
+
the_heading
|
27
46
|
end
|
28
47
|
|
29
|
-
def
|
30
|
-
|
48
|
+
def total_row(entries)
|
49
|
+
padN = heading.length - 2
|
50
|
+
padN -= 2 if options[:rate]
|
51
|
+
the_row = padN.times.map { '' } + ['Total:', sprintf('%0.02f', hours_in_entries(entries))]
|
52
|
+
|
53
|
+
if options[:rate]
|
54
|
+
the_row += ['', "$#{comma_numbers(hours_in_entries(entries).to_f * options[:rate].to_f)}"]
|
55
|
+
end
|
56
|
+
|
57
|
+
the_row
|
31
58
|
end
|
32
59
|
|
60
|
+
def comma_numbers(number, delimiter = ',')
|
61
|
+
sprintf('%0.02f', number).reverse.gsub(%r{([0-9]{3}(?=([0-9])))}, "\\1#{delimiter}").reverse
|
62
|
+
end
|
63
|
+
|
33
64
|
def hours_in_entries(the_entries)
|
34
65
|
the_entries.map {|entry| hours_in_entry(entry) }.reduce(:+)
|
35
66
|
end
|
@@ -38,20 +69,46 @@ module Timesheets
|
|
38
69
|
@entries_by_week ||= entries.group_by {|entry| entry.first.strftime('%U') }.values
|
39
70
|
end
|
40
71
|
|
41
|
-
def
|
72
|
+
def entries
|
73
|
+
the_entries = super
|
74
|
+
|
75
|
+
if options['week-of']
|
76
|
+
week_num = DateTime.parse(options['week-of']).strftime("%U")
|
77
|
+
the_entries = the_entries.select {|entry| entry.first.strftime("%U") == week_num }
|
78
|
+
end
|
79
|
+
|
80
|
+
the_entries
|
81
|
+
end
|
82
|
+
|
83
|
+
def entries_by_day(entries)
|
42
84
|
entries.group_by {|entry|
|
43
|
-
|
44
|
-
}
|
45
|
-
|
85
|
+
entry.first.strftime('%B %e, %Y')
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def times_in_entry(entry)
|
90
|
+
entry.map {|time| time.strftime('%l:%M%p') }.join(' - ')
|
91
|
+
end
|
92
|
+
|
93
|
+
def times_for_entries(entries)
|
94
|
+
entries.map {|entry| times_in_entry(entry) }.join(', ')
|
95
|
+
end
|
96
|
+
|
97
|
+
def format_entries(entries)
|
98
|
+
entries_by_day(entries).map {|day, entries|
|
99
|
+
entry = [
|
46
100
|
entries.first.first.strftime('%A'),
|
47
101
|
day,
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
102
|
+
times_for_entries(entries),
|
103
|
+
sprintf('%0.02f', hours_in_entries(entries))
|
104
|
+
].flatten
|
105
|
+
|
106
|
+
if options[:rate]
|
107
|
+
entry << sprintf('$%0.02f', options[:rate])
|
108
|
+
entry << sprintf('$%0.02f', hours_in_entries(entries).to_f * options[:rate].to_f)
|
109
|
+
end
|
110
|
+
|
111
|
+
entry
|
55
112
|
}
|
56
113
|
end
|
57
114
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Timesheets
|
2
|
+
class CSVTable
|
3
|
+
def initialize(options, &block)
|
4
|
+
@heading = options[:headings]
|
5
|
+
@rows = []
|
6
|
+
block.call(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
def <<(row)
|
10
|
+
@rows << row unless row == :separator
|
11
|
+
end
|
12
|
+
|
13
|
+
def align_column(n, alignment)
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
([@heading] + @rows).map {|row| row.map {|cell| "\"#{cell}\"" }.join(",") }.join("\n")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
3
|
+
module Timesheets
|
4
|
+
class HTMLTable
|
5
|
+
def initialize(options, &block)
|
6
|
+
@heading = options[:headings]
|
7
|
+
@template = options[:template]
|
8
|
+
@rows = []
|
9
|
+
@styles = ["table.timesheets-summary-table th { text-align: left; }"]
|
10
|
+
block.call(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def <<(row)
|
14
|
+
@rows << row
|
15
|
+
end
|
16
|
+
|
17
|
+
def align_column(n, alignment)
|
18
|
+
@styles << "table.timesheets-summary-table td:nth-child(#{n+1}) { text-align: #{alignment}; }"
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
styles = @styles.join
|
23
|
+
table = [
|
24
|
+
"<table class='timesheets-summary-table'><thead>",
|
25
|
+
@heading.map {|cell| "<th>#{cell}</th>" },
|
26
|
+
"</thead><tbody>",
|
27
|
+
@rows.map {|row|
|
28
|
+
if row == :separator
|
29
|
+
""
|
30
|
+
else
|
31
|
+
["<tr>", row.map {|cell| "<td>#{cell}</td>" }.join, "</tr>"]
|
32
|
+
end
|
33
|
+
}.join,
|
34
|
+
"</tbody></table>"
|
35
|
+
].join
|
36
|
+
|
37
|
+
template
|
38
|
+
.gsub("{{styles}}", styles)
|
39
|
+
.gsub("{{table}}", table)
|
40
|
+
.gsub("{{today}}", Time.new.strftime('%Y-%m-%d'))
|
41
|
+
.gsub("{{invoiceID}}", (Digest::SHA2.new << template << Time.new.to_i.to_s).to_s[0...8])
|
42
|
+
.gsub("{{firstDayNice}}", @rows.first[1])
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def template
|
48
|
+
@template ? File.read(@template) : default_template
|
49
|
+
end
|
50
|
+
|
51
|
+
def default_template
|
52
|
+
<<-TEMPLATE
|
53
|
+
<!DOCTYPE html>
|
54
|
+
<html>
|
55
|
+
<head>
|
56
|
+
<style>
|
57
|
+
{{styles}}
|
58
|
+
</style>
|
59
|
+
</head>
|
60
|
+
<body>
|
61
|
+
<h1>Invoice <small>for the week of {{firstDayNice}}</h1>
|
62
|
+
<p>
|
63
|
+
<strong>Date:</strong><br/>
|
64
|
+
{{today}}
|
65
|
+
</p>
|
66
|
+
<p>
|
67
|
+
<strong>Invoice ID:</strong><br/>
|
68
|
+
{{invoiceID}}
|
69
|
+
</p>
|
70
|
+
{{table}}
|
71
|
+
</body>
|
72
|
+
</html>
|
73
|
+
TEMPLATE
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/timesheets/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: timesheets
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bradley J. Spaulding
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-05-
|
11
|
+
date: 2014-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -88,6 +88,7 @@ executables:
|
|
88
88
|
extensions: []
|
89
89
|
extra_rdoc_files: []
|
90
90
|
files:
|
91
|
+
- ".editorconfig"
|
91
92
|
- ".gitignore"
|
92
93
|
- Gemfile
|
93
94
|
- LICENSE.txt
|
@@ -102,6 +103,8 @@ files:
|
|
102
103
|
- lib/timesheets/commands/status.rb
|
103
104
|
- lib/timesheets/commands/stop.rb
|
104
105
|
- lib/timesheets/commands/summary.rb
|
106
|
+
- lib/timesheets/csv_table.rb
|
107
|
+
- lib/timesheets/html_table.rb
|
105
108
|
- lib/timesheets/version.rb
|
106
109
|
- spec/spec_helper.rb
|
107
110
|
- timesheets.gemspec
|