uur 0.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 +7 -0
- data/Changelog +5 -0
- data/LICENSE +20 -0
- data/README.md +116 -0
- data/bin/uur +5 -0
- data/lib/uur.rb +24 -0
- data/lib/uur/action_executor.rb +39 -0
- data/lib/uur/app.rb +27 -0
- data/lib/uur/args_parser.rb +83 -0
- data/lib/uur/duration_formatters/hours_and_minutes_duration_formatter.rb +12 -0
- data/lib/uur/duration_formatters/hours_in_decimal_notation_duration_formatter.rb +10 -0
- data/lib/uur/entries.rb +16 -0
- data/lib/uur/entries_file_reader.rb +16 -0
- data/lib/uur/entries_parser.rb +31 -0
- data/lib/uur/entry.rb +26 -0
- data/lib/uur/mixins/integer_mixin.rb +14 -0
- data/lib/uur/mixins/time_mixin.rb +72 -0
- data/lib/uur/printers/entries_printer.rb +22 -0
- data/lib/uur/printers/no_entries_message_printer.rb +21 -0
- data/lib/uur/printers/printer.rb +25 -0
- data/lib/uur/printers/summary_printer.rb +39 -0
- data/lib/uur/table_printing/table.rb +21 -0
- data/lib/uur/table_printing/table_printer.rb +31 -0
- data/lib/uur/table_printing/tables_printer.rb +27 -0
- data/lib/uur/time_mapper.rb +34 -0
- data/lib/uur/version.rb +3 -0
- metadata +85 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9685f20a21111d9c19d91982f06c6ca324330596
|
4
|
+
data.tar.gz: 6f415d51d2642ef46be4f15326a4b41360761d06
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9ccd7cd9337d558499c3558b6bda2fc22f457bb6c1767c41cdbc39924789c7d351a05e444c49317688026211121ab320dc14e0281595a1bc7a3a5c7caddebe59
|
7
|
+
data.tar.gz: 47a3ea9fbb6e846c925681c97d0b3e76401d6046e686cfbe5adfb596c8a37a89356f66a60dfe427f86939b51bf24db515743097adf682a652fe269e15be82340
|
data/Changelog
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Jacek Mikrut
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
uur
|
2
|
+
===
|
3
|
+
|
4
|
+
**uur** is a command-line tool that reads time log entries from a text file and then generates simple reports.
|
5
|
+
|
6
|
+
Workflow
|
7
|
+
--------
|
8
|
+
1. One adds time entries to `~/.uur/entries` file using one's favourite text editor.
|
9
|
+
2. Then at any time one can use **uur** to print reports.
|
10
|
+
|
11
|
+
Entries file
|
12
|
+
------------
|
13
|
+
|
14
|
+
The entries file should be created at `~/.uur/entries` by the user.
|
15
|
+
|
16
|
+
Each line in this file corresponds to one time entry and has the following format:
|
17
|
+
```
|
18
|
+
YYYY-MM-DD hh:mm - hh:mm Project name Entry description.
|
19
|
+
```
|
20
|
+
- the above data fields should be separated by at least one whitespace character;
|
21
|
+
- the project name should be separated from the entry description by at least two whitespace characters.
|
22
|
+
|
23
|
+
An example:
|
24
|
+
```
|
25
|
+
2013-07-17 10:15 - 10:45 Uur Writing README file content.
|
26
|
+
```
|
27
|
+
|
28
|
+
Blank lines and those that start with `#` (hash) sign are ignored.
|
29
|
+
|
30
|
+
Reports
|
31
|
+
-------
|
32
|
+
|
33
|
+
There are two types of reports: entry lists and entry summaries.
|
34
|
+
|
35
|
+
#### Entries list report
|
36
|
+
|
37
|
+
Simply prints the entries within the specified period, grouped by day.
|
38
|
+
|
39
|
+
An example:
|
40
|
+
```
|
41
|
+
> uur --list --within last-month
|
42
|
+
Mon, 10 June 2013
|
43
|
+
09:15 - 11:45 2h30m Project A Working on task A1
|
44
|
+
11:45 - 12:15 30m Project B Working on task B1
|
45
|
+
15:00 - 16:00 1h00m Project B Completed task A1
|
46
|
+
Total: 4h00m
|
47
|
+
|
48
|
+
Tue, 11 June 2013
|
49
|
+
08:45 - 12:00 3h15m Project A Completed task B1
|
50
|
+
Total: 3h15m
|
51
|
+
|
52
|
+
...
|
53
|
+
```
|
54
|
+
|
55
|
+
#### Entries summary report
|
56
|
+
|
57
|
+
Prints the sum of hours spent on each of the projects within the specified period grouped by the specified interval.
|
58
|
+
|
59
|
+
An example:
|
60
|
+
```
|
61
|
+
> uur --summary monthly --within this-year
|
62
|
+
January 2013
|
63
|
+
Project A 12h15m
|
64
|
+
Project B 120h30m
|
65
|
+
Project C 6h00m
|
66
|
+
Total: 138h45m
|
67
|
+
|
68
|
+
February 2013
|
69
|
+
Project B 140h00m
|
70
|
+
Project C 8h30m
|
71
|
+
Total: 148h30m
|
72
|
+
|
73
|
+
...
|
74
|
+
```
|
75
|
+
|
76
|
+
Usage
|
77
|
+
-----
|
78
|
+
|
79
|
+
```
|
80
|
+
uur [options]
|
81
|
+
|
82
|
+
Reports:
|
83
|
+
-l, --list List entries
|
84
|
+
-s, --summary [daily|weekly|monthly|yearly|all] Print summary
|
85
|
+
|
86
|
+
Filtering:
|
87
|
+
-w, --within today|yesterday|this-week| Time period (default: today)
|
88
|
+
last-week|this-month|last-month|
|
89
|
+
this-year|last-year
|
90
|
+
|
91
|
+
Common options:
|
92
|
+
-d, --duration-format hours-minutes|hours-decimal Duration time format (default: hours-minutes)
|
93
|
+
|
94
|
+
Others:
|
95
|
+
-h, --help Show this help message
|
96
|
+
--version Show version
|
97
|
+
```
|
98
|
+
|
99
|
+
Installation
|
100
|
+
------------
|
101
|
+
|
102
|
+
```
|
103
|
+
gem install uur
|
104
|
+
mkdir ~/.uur
|
105
|
+
touch ~/.uur/entries
|
106
|
+
```
|
107
|
+
|
108
|
+
Author
|
109
|
+
------
|
110
|
+
|
111
|
+
[Jacek Mikrut](https://github.com/jacekmikrut)
|
112
|
+
|
113
|
+
License
|
114
|
+
-------
|
115
|
+
|
116
|
+
License is included in the LICENSE file.
|
data/bin/uur
ADDED
data/lib/uur.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "uur/version"
|
2
|
+
|
3
|
+
require "uur/mixins/integer_mixin"
|
4
|
+
require "uur/mixins/time_mixin"
|
5
|
+
require "uur/time_mapper"
|
6
|
+
require "uur/duration_formatters/hours_and_minutes_duration_formatter"
|
7
|
+
require "uur/duration_formatters/hours_in_decimal_notation_duration_formatter"
|
8
|
+
require "uur/entry"
|
9
|
+
require "uur/entries"
|
10
|
+
require "uur/entries_file_reader"
|
11
|
+
require "uur/entries_parser"
|
12
|
+
require "uur/table_printing/table"
|
13
|
+
require "uur/table_printing/table_printer"
|
14
|
+
require "uur/table_printing/tables_printer"
|
15
|
+
require "uur/printers/printer"
|
16
|
+
require "uur/printers/entries_printer"
|
17
|
+
require "uur/printers/summary_printer"
|
18
|
+
require "uur/printers/no_entries_message_printer"
|
19
|
+
require "uur/args_parser"
|
20
|
+
require "uur/action_executor"
|
21
|
+
require "uur/app"
|
22
|
+
|
23
|
+
module Uur
|
24
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Uur
|
2
|
+
class ActionExecutor
|
3
|
+
|
4
|
+
def initialize(options, entries)
|
5
|
+
@options = options
|
6
|
+
@entries = entries
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute
|
10
|
+
if selected_entries.none?
|
11
|
+
Printers::NoEntriesMessagePrinter.new(@options[:within]).print
|
12
|
+
|
13
|
+
elsif @options[:print_summary]
|
14
|
+
Printers::SummaryPrinter.new(duration_formatter).print(selected_entries, @options[:summary_interval])
|
15
|
+
|
16
|
+
elsif @options[:print_list]
|
17
|
+
Printers::EntriesPrinter.new(duration_formatter).print(selected_entries)
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def selected_entries
|
25
|
+
@selected_entries ||= @entries.within(@options[:since], @options[:to])
|
26
|
+
end
|
27
|
+
|
28
|
+
def duration_formatter
|
29
|
+
case @options[:duration_format]
|
30
|
+
when "hours-minutes"
|
31
|
+
DurationFormatters::HoursAndMinutesDurationFormatter.new
|
32
|
+
when "hours-decimal"
|
33
|
+
DurationFormatters::HoursInDecimalNotationDurationFormatter.new
|
34
|
+
else
|
35
|
+
DurationFormatters::HoursAndMinutesDurationFormatter.new
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/uur/app.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Uur
|
2
|
+
class App
|
3
|
+
|
4
|
+
def run
|
5
|
+
ActionExecutor.new(ArgsParser.new.parse(ARGV), entries).execute
|
6
|
+
rescue EntriesFileReader::CannotReadException => exception
|
7
|
+
$stdout.puts exception.message
|
8
|
+
$stdout.puts "Please make sure the file exists and is readable."
|
9
|
+
end
|
10
|
+
|
11
|
+
def data_dir
|
12
|
+
@data_dir ||= File.expand_path("~/.uur")
|
13
|
+
end
|
14
|
+
|
15
|
+
def entries_file_path
|
16
|
+
@entries_file_path ||= File.join(data_dir, "entries")
|
17
|
+
end
|
18
|
+
|
19
|
+
def entries_file_content
|
20
|
+
@entries_file_content ||= EntriesFileReader.new(entries_file_path).read
|
21
|
+
end
|
22
|
+
|
23
|
+
def entries
|
24
|
+
@entries ||= EntriesParser.new.parse(entries_file_content.lines)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require "optparse"
|
2
|
+
|
3
|
+
module Uur
|
4
|
+
class ArgsParser
|
5
|
+
|
6
|
+
def parse(args)
|
7
|
+
options = {}
|
8
|
+
|
9
|
+
OptionParser.new do |opts|
|
10
|
+
opts.banner = "Usage: uur [options]"
|
11
|
+
|
12
|
+
opts.separator ""
|
13
|
+
opts.separator "Reports:"
|
14
|
+
|
15
|
+
opts.on "-l", "--list", "List entries" do
|
16
|
+
options[:print_list] = true
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.on "-s", "--summary [daily|weekly|monthly|yearly|all]", "Print summary" do |value|
|
20
|
+
options[:print_summary] = true
|
21
|
+
options[:summary_interval] = value
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.separator ""
|
25
|
+
opts.separator "Filtering:"
|
26
|
+
|
27
|
+
opts.on "-w", "--within today|yesterday|this-week|last-week|this-month|last-month|this-year|last-year", String, "Time period (default: today)" do |value|
|
28
|
+
options[:within] = value
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.separator ""
|
32
|
+
opts.separator "Common options:"
|
33
|
+
|
34
|
+
opts.on "-d", "--duration-format hours-minutes|hours-decimal", String, "Duration time format (default: hours-minutes)" do |value|
|
35
|
+
options[:duration_format] = value
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.separator ""
|
39
|
+
opts.separator "Others:"
|
40
|
+
|
41
|
+
opts.on_tail "-h", "--help", "Show this help message" do
|
42
|
+
puts opts
|
43
|
+
exit
|
44
|
+
end
|
45
|
+
|
46
|
+
opts.on_tail "--version", "Show version" do
|
47
|
+
puts "uur version #{Uur::VERSION}"
|
48
|
+
exit
|
49
|
+
end
|
50
|
+
|
51
|
+
end.parse!(args)
|
52
|
+
|
53
|
+
options[:within ] ||= "today" if options[:summary_interval].nil?
|
54
|
+
options[:duration_format] ||= "hours-minutes"
|
55
|
+
options[:print_list ] = true if !options[:print_list] && !options[:print_summary]
|
56
|
+
|
57
|
+
set_default_summary_interval(options) if options[:print_summary] && options[:summary_interval].nil?
|
58
|
+
|
59
|
+
if options[:within]
|
60
|
+
time_mapper = TimeMapper.new(Time.now)
|
61
|
+
options[:since] = time_mapper.since(options[:within])
|
62
|
+
options[:to ] = time_mapper.to( options[:within])
|
63
|
+
end
|
64
|
+
|
65
|
+
options
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def set_default_summary_interval(options)
|
71
|
+
options[:summary_interval] = case options[:within]
|
72
|
+
when "today", "yesterday"
|
73
|
+
"daily"
|
74
|
+
when "this-week", "last-week"
|
75
|
+
"weekly"
|
76
|
+
when "this-month", "last-month"
|
77
|
+
"monthly"
|
78
|
+
when "this-year", "last-year"
|
79
|
+
"yearly"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/uur/entries.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module Uur
|
2
|
+
class Entries < Array
|
3
|
+
|
4
|
+
def within(since, to)
|
5
|
+
self.class.new(select { |entry| (since.nil? || entry.starts_at >= since) && (to.nil? || entry.ends_at <= to) })
|
6
|
+
end
|
7
|
+
|
8
|
+
def group_by(&block)
|
9
|
+
each_with_object(Hash.new { |hash, key| hash[key] = Entries.new }) { |entry, grouped| grouped[block.call(entry)] << entry }
|
10
|
+
end
|
11
|
+
|
12
|
+
def duration
|
13
|
+
inject(0) { |total, entry| total + entry.duration }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Uur
|
2
|
+
class EntriesFileReader
|
3
|
+
|
4
|
+
CannotReadException = Class.new(Exception)
|
5
|
+
|
6
|
+
def initialize(entries_file_path)
|
7
|
+
@entries_file_path = entries_file_path
|
8
|
+
end
|
9
|
+
|
10
|
+
def read
|
11
|
+
File.read(@entries_file_path)
|
12
|
+
rescue
|
13
|
+
raise CannotReadException, "Cannot read the entries file: #{@entries_file_path}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "time"
|
2
|
+
|
3
|
+
module Uur
|
4
|
+
class EntriesParser
|
5
|
+
|
6
|
+
REGEXP = /^\s*(?<date>[\d-]+)\s+(?<start_time>[\d:]+)\s*-\s*(?<end_time>[\d:]+)\s+(?<project_name>.*?)(?=\s\s|$)(?:\s\s+(?<description>.+)$)?/
|
7
|
+
|
8
|
+
def parse(lines)
|
9
|
+
reject_blank_or_commented_lines(lines).each_with_object(Entries.new) do |line, entries|
|
10
|
+
entries << map_data_to_entry(line.match(REGEXP))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def reject_blank_or_commented_lines(lines)
|
17
|
+
lines.reject do |line|
|
18
|
+
line.strip == "" or line =~ /^\s*#/
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def map_data_to_entry(data)
|
23
|
+
Entry.new(
|
24
|
+
starts_at: Time.strptime("#{data[:date]} #{data[:start_time]}", '%Y-%m-%d %H:%M'),
|
25
|
+
ends_at: Time.strptime("#{data[:date]} #{data[ :end_time]}", '%Y-%m-%d %H:%M'),
|
26
|
+
project_name: data[:project_name],
|
27
|
+
description: (data[:description] || "").rstrip,
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/uur/entry.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
|
3
|
+
module Uur
|
4
|
+
class Entry < OpenStruct
|
5
|
+
|
6
|
+
def day
|
7
|
+
starts_at.beginning_of_day
|
8
|
+
end
|
9
|
+
|
10
|
+
def week
|
11
|
+
starts_at.beginning_of_week
|
12
|
+
end
|
13
|
+
|
14
|
+
def month
|
15
|
+
starts_at.beginning_of_month
|
16
|
+
end
|
17
|
+
|
18
|
+
def year
|
19
|
+
starts_at.beginning_of_year
|
20
|
+
end
|
21
|
+
|
22
|
+
def duration
|
23
|
+
(ends_at - starts_at).to_i
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Uur
|
2
|
+
module Mixins
|
3
|
+
module TimeMixin
|
4
|
+
|
5
|
+
def beginning_of_day
|
6
|
+
Time.new(year, month, day)
|
7
|
+
end
|
8
|
+
|
9
|
+
def end_of_day
|
10
|
+
Time.new(year, month, day, 23, 59, 59)
|
11
|
+
end
|
12
|
+
|
13
|
+
def beginning_of_previous_day
|
14
|
+
beginning_of_day - 1.day
|
15
|
+
end
|
16
|
+
|
17
|
+
def end_of_previous_day
|
18
|
+
end_of_day - 1.day
|
19
|
+
end
|
20
|
+
|
21
|
+
def beginning_of_week
|
22
|
+
beginning_of_day - (wday == 0 ? 6 : wday - 1).days
|
23
|
+
end
|
24
|
+
|
25
|
+
def end_of_week
|
26
|
+
(beginning_of_week + 7.days) - 1
|
27
|
+
end
|
28
|
+
|
29
|
+
def beginning_of_previous_week
|
30
|
+
beginning_of_week - 7.days
|
31
|
+
end
|
32
|
+
|
33
|
+
def end_of_previous_week
|
34
|
+
end_of_week - 7.days
|
35
|
+
end
|
36
|
+
|
37
|
+
def beginning_of_month
|
38
|
+
Time.new(year, month)
|
39
|
+
end
|
40
|
+
|
41
|
+
def end_of_month
|
42
|
+
Time.new(year + (month == 12 ? 1 : 0), month % 12 + 1) - 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def beginning_of_previous_month
|
46
|
+
Time.new(end_of_previous_month.year, end_of_previous_month.month)
|
47
|
+
end
|
48
|
+
|
49
|
+
def end_of_previous_month
|
50
|
+
Time.new(year, month) - 1
|
51
|
+
end
|
52
|
+
|
53
|
+
def beginning_of_year
|
54
|
+
Time.new(year)
|
55
|
+
end
|
56
|
+
|
57
|
+
def end_of_year
|
58
|
+
Time.new(year + 1) - 1
|
59
|
+
end
|
60
|
+
|
61
|
+
def beginning_of_previous_year
|
62
|
+
Time.new(year - 1)
|
63
|
+
end
|
64
|
+
|
65
|
+
def end_of_previous_year
|
66
|
+
Time.new(year) - 1
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
Time.send(:include, Uur::Mixins::TimeMixin)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Uur
|
2
|
+
module Printers
|
3
|
+
class EntriesPrinter < Printer
|
4
|
+
|
5
|
+
def print(entries)
|
6
|
+
print_tables entries, :day, proc { |time| time.strftime("%a, %d %B %Y") }
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def alignments
|
12
|
+
[:left, :right, :left, :left]
|
13
|
+
end
|
14
|
+
|
15
|
+
def rows(entries)
|
16
|
+
entries.map do |entry|
|
17
|
+
["#{entry.starts_at.strftime("%H:%M")} - #{entry.ends_at.strftime("%H:%M")}", "#{duration(entry.duration)}", "#{entry.project_name}", "#{entry.description}"]
|
18
|
+
end.push([" Total:", duration(entries.duration), "", ""])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Uur
|
2
|
+
module Printers
|
3
|
+
class NoEntriesMessagePrinter
|
4
|
+
|
5
|
+
def initialize(within_option, out=$stdout)
|
6
|
+
@within_option = within_option
|
7
|
+
@out = out
|
8
|
+
end
|
9
|
+
|
10
|
+
def print
|
11
|
+
@out.puts "No entries within #{within}."
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def within
|
17
|
+
@within ||= @within_option.gsub(/-/, " ")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Uur
|
2
|
+
module Printers
|
3
|
+
class Printer
|
4
|
+
|
5
|
+
def initialize(duration_formatter, out=$stdout)
|
6
|
+
@duration_formatter = duration_formatter
|
7
|
+
@out = out
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def duration(duration)
|
13
|
+
@duration_formatter.format(duration)
|
14
|
+
end
|
15
|
+
|
16
|
+
def print_tables(entries, interval, header_proc)
|
17
|
+
Uur::TablePrinting::TablesPrinter.new(tables(entries, interval, header_proc), alignments, @out).print
|
18
|
+
end
|
19
|
+
|
20
|
+
def tables(entries, interval, header_proc)
|
21
|
+
entries.group_by(&interval).map { |date, entries| Uur::TablePrinting::Table.new(header_proc.call(date), rows(entries)) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Uur
|
2
|
+
module Printers
|
3
|
+
class SummaryPrinter < Printer
|
4
|
+
|
5
|
+
def print(entries, interval=nil)
|
6
|
+
|
7
|
+
case interval
|
8
|
+
when "daily"
|
9
|
+
print_tables entries, :day, proc { |time| time.strftime("%a, %d %B %Y") }
|
10
|
+
|
11
|
+
when "weekly"
|
12
|
+
print_tables entries, :week, proc { |time| "#{time.beginning_of_week.strftime("%d %B %Y")} - #{time.end_of_week.strftime("%d %B %Y")}" }
|
13
|
+
|
14
|
+
when "monthly"
|
15
|
+
print_tables entries, :month, proc { |time| time.strftime("%B %Y") }
|
16
|
+
|
17
|
+
when "yearly"
|
18
|
+
print_tables entries, :year, proc { |time| time.strftime("Year %Y") }
|
19
|
+
|
20
|
+
else
|
21
|
+
print_tables entries, :nil, proc { nil }
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def alignments
|
29
|
+
[:left, :right]
|
30
|
+
end
|
31
|
+
|
32
|
+
def rows(entries)
|
33
|
+
entries.group_by(&:project_name).sort.map do |project_name, project_entries|
|
34
|
+
[project_name, duration(project_entries.duration)]
|
35
|
+
end.push(["Total:", duration(entries.duration)])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Uur
|
2
|
+
module TablePrinting
|
3
|
+
class Table < Array
|
4
|
+
|
5
|
+
def initialize(header, *args)
|
6
|
+
@header = header
|
7
|
+
super(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :header
|
11
|
+
|
12
|
+
def ==(other)
|
13
|
+
header == other.header && super
|
14
|
+
end
|
15
|
+
|
16
|
+
def inspect
|
17
|
+
"Header: #{header.inspect}, content: #{super}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Uur
|
2
|
+
module TablePrinting
|
3
|
+
class TablePrinter
|
4
|
+
|
5
|
+
def initialize(table, widths, alignments, out=$stdout)
|
6
|
+
@table = table
|
7
|
+
@widths = widths
|
8
|
+
@alignments = alignments
|
9
|
+
@out = out
|
10
|
+
end
|
11
|
+
|
12
|
+
def print
|
13
|
+
@out.puts formatted_table.header unless formatted_table.header.nil?
|
14
|
+
@out.puts formatted_table.map { |row_cells| row_cells.join(' ').rstrip }
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :table, :widths, :alignments
|
20
|
+
|
21
|
+
def formatted_table
|
22
|
+
@formatted_table ||=
|
23
|
+
Table.new(table.header, table.map do |cells_in_row|
|
24
|
+
cells_in_row.each_with_index.map do |cell, column_index|
|
25
|
+
cell.send({ :left => :ljust, :right => :rjust }[alignments[column_index] || :left], widths[column_index] || 0)
|
26
|
+
end
|
27
|
+
end)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Uur
|
2
|
+
module TablePrinting
|
3
|
+
class TablesPrinter
|
4
|
+
|
5
|
+
def initialize(tables, alignments, out=$stdout)
|
6
|
+
@tables = tables
|
7
|
+
@alignments = alignments
|
8
|
+
@out = out
|
9
|
+
end
|
10
|
+
|
11
|
+
def print
|
12
|
+
tables.each do |table|
|
13
|
+
TablePrinter.new(table, widths, alignments, @out).print
|
14
|
+
@out.puts
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :tables, :alignments
|
21
|
+
|
22
|
+
def widths
|
23
|
+
@widths ||= tables.flatten(1).transpose.map { |rows_in_column| rows_in_column.map(&:length).max }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Uur
|
2
|
+
class TimeMapper
|
3
|
+
|
4
|
+
def initialize(now)
|
5
|
+
@now = now
|
6
|
+
end
|
7
|
+
|
8
|
+
def since(time)
|
9
|
+
case time
|
10
|
+
when "today" then @now.beginning_of_day
|
11
|
+
when "yesterday" then @now.beginning_of_previous_day
|
12
|
+
when "this-week" then @now.beginning_of_week
|
13
|
+
when "last-week" then @now.beginning_of_previous_week
|
14
|
+
when "this-month" then @now.beginning_of_month
|
15
|
+
when "last-month" then @now.beginning_of_previous_month
|
16
|
+
when "this-year" then @now.beginning_of_year
|
17
|
+
when "last-year" then @now.beginning_of_previous_year
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def to(time)
|
22
|
+
case time
|
23
|
+
when "today" then @now.end_of_day
|
24
|
+
when "yesterday" then @now.end_of_previous_day
|
25
|
+
when "this-week" then @now.end_of_week
|
26
|
+
when "last-week" then @now.end_of_previous_week
|
27
|
+
when "this-month" then @now.end_of_month
|
28
|
+
when "last-month" then @now.end_of_previous_month
|
29
|
+
when "this-year" then @now.end_of_year
|
30
|
+
when "last-year" then @now.end_of_previous_year
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/uur/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: uur
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jacek Mikrut
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2013-07-17 00:00:00 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
prerelease: false
|
17
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: "2.0"
|
22
|
+
type: :development
|
23
|
+
version_requirements: *id001
|
24
|
+
description: Generates simple reports from time log entries stored in human-readable text file.
|
25
|
+
email: jacekmikrut.software@gmail.com
|
26
|
+
executables:
|
27
|
+
- uur
|
28
|
+
extensions: []
|
29
|
+
|
30
|
+
extra_rdoc_files: []
|
31
|
+
|
32
|
+
files:
|
33
|
+
- lib/uur/args_parser.rb
|
34
|
+
- lib/uur/entries_file_reader.rb
|
35
|
+
- lib/uur/action_executor.rb
|
36
|
+
- lib/uur/app.rb
|
37
|
+
- lib/uur/duration_formatters/hours_in_decimal_notation_duration_formatter.rb
|
38
|
+
- lib/uur/duration_formatters/hours_and_minutes_duration_formatter.rb
|
39
|
+
- lib/uur/mixins/integer_mixin.rb
|
40
|
+
- lib/uur/mixins/time_mixin.rb
|
41
|
+
- lib/uur/time_mapper.rb
|
42
|
+
- lib/uur/entries.rb
|
43
|
+
- lib/uur/entries_parser.rb
|
44
|
+
- lib/uur/entry.rb
|
45
|
+
- lib/uur/table_printing/table_printer.rb
|
46
|
+
- lib/uur/table_printing/table.rb
|
47
|
+
- lib/uur/table_printing/tables_printer.rb
|
48
|
+
- lib/uur/printers/summary_printer.rb
|
49
|
+
- lib/uur/printers/entries_printer.rb
|
50
|
+
- lib/uur/printers/no_entries_message_printer.rb
|
51
|
+
- lib/uur/printers/printer.rb
|
52
|
+
- lib/uur/version.rb
|
53
|
+
- lib/uur.rb
|
54
|
+
- README.md
|
55
|
+
- LICENSE
|
56
|
+
- Changelog
|
57
|
+
- bin/uur
|
58
|
+
homepage: http://github.com/jacekmikrut/uur
|
59
|
+
licenses: []
|
60
|
+
|
61
|
+
metadata: {}
|
62
|
+
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- &id002
|
71
|
+
- ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- *id002
|
77
|
+
requirements: []
|
78
|
+
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 2.0.5
|
81
|
+
signing_key:
|
82
|
+
specification_version: 4
|
83
|
+
summary: Command line time log reporting tool.
|
84
|
+
test_files: []
|
85
|
+
|