reckon 0.5.0 → 0.6.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/.gitignore +3 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +55 -1
- data/Gemfile.lock +1 -5
- data/README.md +1 -1
- data/lib/reckon.rb +7 -9
- data/lib/reckon/app.rb +140 -69
- data/lib/reckon/cosine_similarity.rb +92 -89
- data/lib/reckon/csv_parser.rb +70 -113
- data/lib/reckon/date_column.rb +60 -0
- data/lib/reckon/ledger_parser.rb +11 -1
- data/lib/reckon/logger.rb +4 -0
- data/lib/reckon/money.rb +4 -59
- data/lib/reckon/version.rb +3 -0
- data/reckon.gemspec +3 -3
- data/spec/data_fixtures/51-sample.csv +8 -0
- data/spec/data_fixtures/51-tokens.yml +9 -0
- data/spec/data_fixtures/85-date-example.csv +2 -0
- data/spec/data_fixtures/test_money_column.csv +3 -0
- data/spec/reckon/app_spec.rb +32 -2
- data/spec/reckon/csv_parser_spec.rb +129 -129
- data/spec/reckon/date_column_spec.rb +12 -13
- data/spec/reckon/ledger_parser_spec.rb +42 -5
- data/spec/reckon/money_spec.rb +42 -29
- data/spec/spec_helper.rb +19 -0
- metadata +12 -19
@@ -20,23 +20,22 @@ describe Reckon::DateColumn do
|
|
20
20
|
end
|
21
21
|
describe "for" do
|
22
22
|
it "should detect the date" do
|
23
|
-
Reckon::DateColumn.new(
|
24
|
-
|
25
|
-
Reckon::DateColumn.new(
|
26
|
-
|
27
|
-
Reckon::DateColumn.new(
|
28
|
-
|
29
|
-
Reckon::DateColumn.new( ["2013-11-21"] ).for( 0 )
|
30
|
-
|
23
|
+
expect(Reckon::DateColumn.new(%w[13/12/2013]).for(0))
|
24
|
+
.to eq(Date.new(2013, 12, 13))
|
25
|
+
expect(Reckon::DateColumn.new(%w[01/14/2013]).for(0))
|
26
|
+
.to eq(Date.new(2013, 1, 14))
|
27
|
+
expect(Reckon::DateColumn.new(%w[13/12/2013 21/11/2013]).for(1))
|
28
|
+
.to eq(Date.new(2013, 11, 21))
|
29
|
+
expect(Reckon::DateColumn.new( ["2013-11-21"] ).for( 0 ))
|
30
|
+
.to eq(Date.new(2013, 11, 21))
|
31
31
|
|
32
32
|
end
|
33
33
|
|
34
34
|
it "should correctly use endian_precedence" do
|
35
|
-
Reckon::DateColumn.new(
|
36
|
-
|
37
|
-
Reckon::DateColumn.new(
|
38
|
-
|
35
|
+
expect(Reckon::DateColumn.new(%w[01/02/2013 01/14/2013]).for(0))
|
36
|
+
.to eq(Date.new(2013, 1, 2))
|
37
|
+
expect(Reckon::DateColumn.new(%w[01/02/2013 14/01/2013]).for(0))
|
38
|
+
.to eq(Date.new(2013, 2, 1))
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
42
|
-
|
@@ -20,14 +20,16 @@ describe Reckon::LedgerParser do
|
|
20
20
|
formats = ["%Y/%m/%d", "%Y-%m-%d"]
|
21
21
|
types = [' ! ', ' * ', ' ']
|
22
22
|
delimiters = [" ", "\t", "\t\t"]
|
23
|
+
comment_chars = ';#%*|'
|
23
24
|
currency_delimiters = delimiters + ['']
|
24
25
|
currencies = ['', '$', '£']
|
25
26
|
property_of do
|
26
27
|
Rantly do
|
27
28
|
description = Proc.new do
|
28
|
-
sized(15){string}.tr(%q{'`:*\\},'').gsub(/\s+/, ' ').gsub(/^[!;<\[( ]+/, '')
|
29
|
+
sized(15){string}.tr(%q{'`:*\\},'').gsub(/\s+/, ' ').gsub(/^[!;<\[( #{comment_chars}]+/, '')
|
29
30
|
end
|
30
31
|
currency = choose(*currencies) # to be consistent within the transaction
|
32
|
+
single_line_comments = ";#|%*".split('').map { |n| "#{n} #{call(description)}" }
|
31
33
|
comments = ['', '; ', "\t;#{call(description)}", " ; #{call(description)}"]
|
32
34
|
date = Time.at(range(0, 1_581_389_644)).strftime(choose(*formats))
|
33
35
|
codes = [' ', " (#{string(:alnum).tr('()', '')}) "]
|
@@ -48,23 +50,58 @@ describe Reckon::LedgerParser do
|
|
48
50
|
ledger += "#{call(account_line)}\n"
|
49
51
|
end
|
50
52
|
ledger += "#{call(account)}\n"
|
53
|
+
ledger += choose(*single_line_comments) + "\n"
|
51
54
|
ledger
|
52
55
|
end
|
53
56
|
end.check(1000) do |s|
|
54
57
|
filter_format = lambda { |n| [n['date'], n['desc'], n['name'], sprintf("%.02f", n['amount'])] }
|
55
58
|
headers = %w[date code desc name currency amount type commend]
|
56
59
|
safe_s = Shellwords.escape(s)
|
57
|
-
ledger_csv = `echo #{safe_s} | ledger csv --date-format '%Y-%m-%d' -f - `
|
58
|
-
ledger_parser_csv = Reckon::LedgerParser.new(s, date_format: '%Y/%m/%d').to_csv.join("\n")
|
59
60
|
|
60
|
-
|
61
|
-
actual = CSV.parse(
|
61
|
+
lp_csv = Reckon::LedgerParser.new(s, date_format: '%Y/%m/%d').to_csv.join("\n")
|
62
|
+
actual = CSV.parse(lp_csv, headers: headers).map(&filter_format)
|
63
|
+
|
64
|
+
ledger_csv = `echo #{safe_s} | ledger csv --date-format '%Y/%m/%d' -f - `
|
65
|
+
expected = CSV.parse(ledger_csv.gsub('\"', '""'), headers: headers).map(&filter_format)
|
62
66
|
expected.length.times do |i|
|
63
67
|
expect(actual[i]).to eq(expected[i])
|
64
68
|
end
|
65
69
|
end
|
66
70
|
end
|
67
71
|
|
72
|
+
it 'should filter block comments' do
|
73
|
+
ledger = <<HERE
|
74
|
+
1970/11/01 Dinner should show up
|
75
|
+
Assets:Checking -123.00
|
76
|
+
Expenses:Restaurants
|
77
|
+
|
78
|
+
comment
|
79
|
+
|
80
|
+
1970/11/01 Lunch should NOT show up
|
81
|
+
Assets:Checking -12.00
|
82
|
+
Expenses:Restaurants
|
83
|
+
|
84
|
+
end comment
|
85
|
+
HERE
|
86
|
+
l = Reckon::LedgerParser.new(ledger)
|
87
|
+
expect(l.entries.length).to eq(1)
|
88
|
+
expect(l.entries.first[:desc]).to eq('Dinner should show up')
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should transaction comments' do
|
93
|
+
ledger = <<HERE
|
94
|
+
2020-03-27 AMZN Mktp USX999H3203; Shopping; Sale
|
95
|
+
Expenses:Household $82.77
|
96
|
+
Liabilities:ChaseSapphire -$81.77
|
97
|
+
# END FINANCE SCRIPT OUTPUT Thu 02 Apr 2020 12:05:54 PM EDT
|
98
|
+
HERE
|
99
|
+
l = Reckon::LedgerParser.new(ledger)
|
100
|
+
expect(l.entries.first[:accounts].map { |n| n[:name] }).to eq(['Expenses:Household', 'Liabilities:ChaseSapphire'])
|
101
|
+
expect(l.entries.first[:accounts].size).to eq(2)
|
102
|
+
expect(l.entries.length).to eq(1)
|
103
|
+
end
|
104
|
+
|
68
105
|
it "should ignore non-standard entries" do
|
69
106
|
@ledger.entries.length.should == 7
|
70
107
|
end
|
data/spec/reckon/money_spec.rb
CHANGED
@@ -8,79 +8,92 @@ require 'reckon'
|
|
8
8
|
describe Reckon::Money do
|
9
9
|
describe "from_s" do
|
10
10
|
it "should handle currency indicators" do
|
11
|
-
Reckon::Money::from_s( "$2.00" ).
|
12
|
-
Reckon::Money::from_s(
|
13
|
-
Reckon::Money::from_s(
|
11
|
+
expect(Reckon::Money::from_s( "$2.00" )).to eq(2.00)
|
12
|
+
expect(Reckon::Money::from_s("-$1025.67")).to eq(-1025.67)
|
13
|
+
expect(Reckon::Money::from_s("$-1025.67")).to eq(-1025.67)
|
14
14
|
end
|
15
15
|
|
16
16
|
it "should handle the comma_separates_cents option correctly" do
|
17
|
-
Reckon::Money::from_s(
|
18
|
-
Reckon::Money::from_s(
|
19
|
-
Reckon::Money::from_s(
|
17
|
+
expect(Reckon::Money::from_s("$2,00", :comma_separates_cents => true)).to eq(2.00)
|
18
|
+
expect(Reckon::Money::from_s("-$1025,67", :comma_separates_cents => true )).to eq(-1025.67)
|
19
|
+
expect(Reckon::Money::from_s("$-1025,67", :comma_separates_cents => true )).to eq(-1025.67)
|
20
20
|
end
|
21
21
|
|
22
22
|
it "should return 0 for an empty string" do
|
23
|
-
Reckon::Money::from_s(
|
23
|
+
expect(Reckon::Money::from_s("")).to eq(0)
|
24
24
|
end
|
25
25
|
|
26
26
|
it "should handle 1000 indicators correctly" do
|
27
|
-
Reckon::Money::from_s(
|
28
|
-
Reckon::Money::from_s(
|
27
|
+
expect(Reckon::Money::from_s("$2.000,00", :comma_separates_cents => true)).to eq(2000.00)
|
28
|
+
expect(Reckon::Money::from_s("-$1,025.67")).to eq(-1025.67)
|
29
29
|
end
|
30
30
|
|
31
31
|
it "should keep numbers together" do
|
32
|
-
Reckon::Money::from_s(
|
32
|
+
expect(Reckon::Money::from_s("1A1")).to eq(1)
|
33
33
|
end
|
34
34
|
|
35
35
|
it "should prefer numbers with precision of two" do
|
36
|
-
Reckon::Money::from_s(
|
37
|
-
Reckon::Money::from_s(
|
36
|
+
expect(Reckon::Money::from_s("1A2.00")).to eq(2)
|
37
|
+
expect(Reckon::Money::from_s("2.00A1")).to eq(2)
|
38
38
|
end
|
39
39
|
|
40
40
|
it "should handle arbitrary prefixes and postfixes" do
|
41
|
-
Reckon::Money::from_s(
|
42
|
-
Reckon::Money::from_s(
|
43
|
-
Reckon::Money::from_s(
|
41
|
+
expect(Reckon::Money::from_s("AB1.00C")).to eq(1)
|
42
|
+
expect(Reckon::Money::from_s("AB0C")).to eq(0)
|
43
|
+
expect(Reckon::Money::from_s("AB-2.00C")).to eq(-2)
|
44
44
|
end
|
45
45
|
|
46
46
|
it "should return nil if no numbers are found" do
|
47
|
-
Reckon::Money::from_s(
|
47
|
+
expect(Reckon::Money::from_s("BAC")).to be_nil()
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
51
|
describe "pretty" do
|
52
52
|
it "work with negative and positive numbers" do
|
53
|
-
Reckon::Money.new(
|
54
|
-
Reckon::Money.new(
|
53
|
+
expect(Reckon::Money.new(-20.00).pretty).to eq("-$20.00")
|
54
|
+
expect(Reckon::Money.new(1558.52).pretty).to eq(" $1558.52")
|
55
55
|
end
|
56
56
|
|
57
57
|
it "work with other currencies such as €" do
|
58
|
-
Reckon::Money.new(
|
59
|
-
Reckon::Money.new(
|
58
|
+
expect(Reckon::Money.new(-20.00, currency: "€", suffixed: false).pretty).to eq("-€20.00")
|
59
|
+
expect(Reckon::Money.new(1558.52, currency: "€", suffixed: false).pretty).to eq(" €1558.52")
|
60
60
|
end
|
61
61
|
|
62
62
|
it "work with suffixed currencies such as SEK" do
|
63
|
-
Reckon::Money.new( -20.00, :currency => "SEK", :suffixed => true ).pretty.
|
64
|
-
Reckon::Money.new( 1558.52, :currency => "SEK", :suffixed => true ).pretty.
|
63
|
+
expect(Reckon::Money.new( -20.00, :currency => "SEK", :suffixed => true ).pretty).to eq("-20.00 SEK")
|
64
|
+
expect(Reckon::Money.new( 1558.52, :currency => "SEK", :suffixed => true ).pretty).to eq(" 1558.52 SEK")
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
68
|
describe "likelihood" do
|
69
69
|
it "should return the likelihood that a string represents money" do
|
70
|
-
Reckon::Money::likelihood( "$20.00" ).
|
70
|
+
expect(Reckon::Money::likelihood( "$20.00" )).to eq(65)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should return neutral for empty string" do
|
74
|
+
expect(Reckon::Money::likelihood("")).to eq(0)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should recognize non-us currencies" do
|
78
|
+
expect(Reckon::Money::likelihood("£480.00")).to eq(30)
|
79
|
+
expect(Reckon::Money::likelihood("£1.480,00")).to eq(30)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should not identify date columns as money' do
|
83
|
+
expect(Reckon::Money::likelihood("22.01.2014")).to eq(0)
|
71
84
|
end
|
72
85
|
end
|
73
86
|
|
74
87
|
describe "equality" do
|
75
88
|
it "should be comparable to other money" do
|
76
|
-
Reckon::Money.new(
|
77
|
-
Reckon::Money.new(
|
78
|
-
Reckon::Money.new(
|
89
|
+
expect(Reckon::Money.new(2.0)).to eq(Reckon::Money.new(2.0))
|
90
|
+
expect(Reckon::Money.new(1.0)).to be <= Reckon::Money.new(2.0)
|
91
|
+
expect(Reckon::Money.new(3.0)).to be > Reckon::Money.new(2.0)
|
79
92
|
end
|
80
93
|
it "should be comparable to other float" do
|
81
|
-
Reckon::Money.new(
|
82
|
-
Reckon::Money.new(
|
83
|
-
Reckon::Money.new(
|
94
|
+
expect(Reckon::Money.new(2.0)).to eq(2.0)
|
95
|
+
expect(Reckon::Money.new(1.0)).to be <= 2.0
|
96
|
+
expect(Reckon::Money.new(3.0)).to be > 2.0
|
84
97
|
end
|
85
98
|
end
|
86
99
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,7 +3,26 @@ require 'rspec'
|
|
3
3
|
require 'reckon'
|
4
4
|
|
5
5
|
RSpec.configure do |config|
|
6
|
+
config.before(:all, &:silence_output)
|
7
|
+
config.after(:all, &:enable_output)
|
6
8
|
def fixture_path(file)
|
7
9
|
File.expand_path(File.join(File.dirname(__FILE__), "data_fixtures", file))
|
8
10
|
end
|
9
11
|
end
|
12
|
+
|
13
|
+
public
|
14
|
+
|
15
|
+
# Redirects stderr and stout to /dev/null.txt
|
16
|
+
def silence_output
|
17
|
+
# Store the original stderr and stdout in order to restore them later
|
18
|
+
@original_stdout = $stdout
|
19
|
+
|
20
|
+
# Redirect stderr and stdout
|
21
|
+
$stdout = File.new(File.join(File.dirname(__FILE__), 'test_log.txt'), 'w')
|
22
|
+
end
|
23
|
+
|
24
|
+
# Replace stderr and stdout so anything else is output correctly
|
25
|
+
def enable_output
|
26
|
+
$stdout = @original_stdout
|
27
|
+
@original_stdout = nil
|
28
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reckon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Cantino
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2020-
|
13
|
+
date: 2020-09-04 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rspec
|
@@ -82,20 +82,6 @@ dependencies:
|
|
82
82
|
- - ">="
|
83
83
|
- !ruby/object:Gem::Version
|
84
84
|
version: 1.5.2
|
85
|
-
- !ruby/object:Gem::Dependency
|
86
|
-
name: terminal-table
|
87
|
-
requirement: !ruby/object:Gem::Requirement
|
88
|
-
requirements:
|
89
|
-
- - ">="
|
90
|
-
- !ruby/object:Gem::Version
|
91
|
-
version: 1.4.2
|
92
|
-
type: :runtime
|
93
|
-
prerelease: false
|
94
|
-
version_requirements: !ruby/object:Gem::Requirement
|
95
|
-
requirements:
|
96
|
-
- - ">="
|
97
|
-
- !ruby/object:Gem::Version
|
98
|
-
version: 1.4.2
|
99
85
|
- !ruby/object:Gem::Dependency
|
100
86
|
name: rchardet
|
101
87
|
requirement: !ruby/object:Gem::Requirement
|
@@ -135,12 +121,18 @@ files:
|
|
135
121
|
- lib/reckon/app.rb
|
136
122
|
- lib/reckon/cosine_similarity.rb
|
137
123
|
- lib/reckon/csv_parser.rb
|
124
|
+
- lib/reckon/date_column.rb
|
138
125
|
- lib/reckon/ledger_parser.rb
|
126
|
+
- lib/reckon/logger.rb
|
139
127
|
- lib/reckon/money.rb
|
128
|
+
- lib/reckon/version.rb
|
140
129
|
- reckon.gemspec
|
130
|
+
- spec/data_fixtures/51-sample.csv
|
131
|
+
- spec/data_fixtures/51-tokens.yml
|
141
132
|
- spec/data_fixtures/73-sample.csv
|
142
133
|
- spec/data_fixtures/73-tokens.yml
|
143
134
|
- spec/data_fixtures/73-transactions.ledger
|
135
|
+
- spec/data_fixtures/85-date-example.csv
|
144
136
|
- spec/data_fixtures/austrian_example.csv
|
145
137
|
- spec/data_fixtures/bom_utf8_file.csv
|
146
138
|
- spec/data_fixtures/broker_canada_example.csv
|
@@ -160,6 +152,7 @@ files:
|
|
160
152
|
- spec/data_fixtures/some_other.csv
|
161
153
|
- spec/data_fixtures/spanish_date_example.csv
|
162
154
|
- spec/data_fixtures/suntrust.csv
|
155
|
+
- spec/data_fixtures/test_money_column.csv
|
163
156
|
- spec/data_fixtures/tokens.yaml
|
164
157
|
- spec/data_fixtures/two_money_columns.csv
|
165
158
|
- spec/data_fixtures/yyyymmdd_date_example.csv
|
@@ -172,7 +165,8 @@ files:
|
|
172
165
|
- spec/spec.opts
|
173
166
|
- spec/spec_helper.rb
|
174
167
|
homepage: https://github.com/cantino/reckon
|
175
|
-
licenses:
|
168
|
+
licenses:
|
169
|
+
- MIT
|
176
170
|
metadata: {}
|
177
171
|
post_install_message:
|
178
172
|
rdoc_options: []
|
@@ -189,8 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
189
183
|
- !ruby/object:Gem::Version
|
190
184
|
version: '0'
|
191
185
|
requirements: []
|
192
|
-
|
193
|
-
rubygems_version: 2.7.6.2
|
186
|
+
rubygems_version: 3.1.2
|
194
187
|
signing_key:
|
195
188
|
specification_version: 4
|
196
189
|
summary: Utility for interactively converting and labeling CSV files for the Ledger
|