reckon 0.5.1 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7e9512d0b15c14548a04e80f5dd3a87f50ae9ff2654827b5970def94e174667
4
- data.tar.gz: f858c96b4b5f2a7b2c85845803109e566281c0fc945c46ba39758121701d0e9a
3
+ metadata.gz: 70bc1d3d98a4ba08a3bca57069073f165e68041a5e628475b6c6076550f6e419
4
+ data.tar.gz: 99daf95abf45fd4dd5549d08bb9f3816af596cd3790667ca224baf7d46053ed0
5
5
  SHA512:
6
- metadata.gz: 15526cfe3504d50859de7b652285b31f639b11daecd8536732b97e1f84d03814cb8040c8db830386f803de7836e11712b7069758a77bf290fab26699436440f2
7
- data.tar.gz: 99ae732793adeaa40f5c7235e690dfb80d839a31ecba52f989e0dea2d339cf012623f858c926261e4bd5eb731e2a3ff508370509d03560c64c2bd7cf4a737a7e
6
+ metadata.gz: 6c0790695ec045e5210d20de85e17da49868b396ad78e334f24fdae7511969552a72df90b649b781c8b0e7ddf046cf8f84e2b47e212c576048f86798389bb449
7
+ data.tar.gz: 18babc20d956315d9abed0cec59503f0859844a48e3cf5ee59d743dda487de762a9ab9787074e7553128d41834938f5eedeb2f92e5fda4fb1ab73939f81d0eb2
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.5.2](https://github.com/cantino/reckon/tree/v0.5.2) (2020-03-07)
4
+
5
+ [Full Changelog](https://github.com/cantino/reckon/compare/v0.5.1...v0.5.2)
6
+
7
+ **Closed issues:**
8
+
9
+ - \[BUG\] Reckon appears not to be parsing ISO standard date yyyy-mm-dd? [\#85](https://github.com/cantino/reckon/issues/85)
10
+ - \[Bug\]? Reckon fails to run on ruby 2.7.0 on Catalina [\#83](https://github.com/cantino/reckon/issues/83)
11
+ - --account-tokens issue [\#51](https://github.com/cantino/reckon/issues/51)
12
+
13
+ ## [v0.5.1](https://github.com/cantino/reckon/tree/v0.5.1) (2020-02-25)
14
+
15
+ [Full Changelog](https://github.com/cantino/reckon/compare/v0.5.0...v0.5.1)
16
+
17
+ **Closed issues:**
18
+
19
+ - Error Importing [\#64](https://github.com/cantino/reckon/issues/64)
20
+
21
+ **Merged pull requests:**
22
+
23
+ - guard against rows that don't parse dates [\#82](https://github.com/cantino/reckon/pull/82) ([benprew](https://github.com/benprew))
24
+
3
25
  ## [v0.5.0](https://github.com/cantino/reckon/tree/v0.5.0) (2020-02-19)
4
26
 
5
27
  [Full Changelog](https://github.com/cantino/reckon/compare/v0.4.4...v0.5.0)
@@ -1,4 +1,5 @@
1
1
  require 'matrix'
2
+ require 'set'
2
3
 
3
4
  # Implementation of consine similarity using TF-IDF for vectorization.
4
5
  # Used to suggest which account a transaction should be assigned to
@@ -12,24 +12,39 @@ module Reckon
12
12
  detect_columns
13
13
  end
14
14
 
15
- def row(index)
16
- csv_data[index].join(", ")
15
+ def columns
16
+ @columns ||=
17
+ begin
18
+ last_row_length = nil
19
+ csv_data.inject([]) do |memo, row|
20
+ unless row.all? { |i| i.nil? || i.length == 0 }
21
+ row.each_with_index do |entry, index|
22
+ memo[index] ||= []
23
+ memo[index] << (entry || '').strip
24
+ end
25
+ last_row_length = row.length
26
+ end
27
+ memo
28
+ end
29
+ end
17
30
  end
18
31
 
19
- def filter_csv
20
- if options[:ignore_columns]
21
- new_columns = []
22
- columns.each_with_index do |column, index|
23
- new_columns << column unless options[:ignore_columns].include?(index + 1)
24
- end
25
- @columns = new_columns
26
- end
32
+ def date_for(index)
33
+ @date_column.for(index)
34
+ end
35
+
36
+ def pretty_date_for(index)
37
+ @date_column.pretty_for( index )
27
38
  end
28
39
 
29
40
  def money_for(index)
30
41
  @money_column[index]
31
42
  end
32
43
 
44
+ def pretty_money(amount, negate = false)
45
+ Money.new( amount, @options ).pretty( negate )
46
+ end
47
+
33
48
  def pretty_money_for(index, negate = false)
34
49
  money = money_for(index)
35
50
  return 0 if money.nil?
@@ -37,20 +52,24 @@ module Reckon
37
52
  money.pretty(negate)
38
53
  end
39
54
 
40
- def pretty_money(amount, negate = false)
41
- Money.new( amount, @options ).pretty( negate )
55
+ def description_for(index)
56
+ description_column_indices.map { |i| columns[i][index] }.reject(&:empty?).join("; ").squeeze(" ").gsub(/(;\s+){2,}/, '').strip
42
57
  end
43
58
 
44
- def date_for(index)
45
- @date_column.for(index)
59
+ def row(index)
60
+ csv_data[index].join(", ")
46
61
  end
47
62
 
48
- def pretty_date_for(index)
49
- @date_column.pretty_for( index )
50
- end
63
+ private
51
64
 
52
- def description_for(index)
53
- description_column_indices.map { |i| columns[i][index] }.reject(&:empty?).join("; ").squeeze(" ").gsub(/(;\s+){2,}/, '').strip
65
+ def filter_csv
66
+ if options[:ignore_columns]
67
+ new_columns = []
68
+ columns.each_with_index do |column, index|
69
+ new_columns << column unless options[:ignore_columns].include?(index + 1)
70
+ end
71
+ @columns = new_columns
72
+ end
54
73
  end
55
74
 
56
75
  def evaluate_columns(cols)
@@ -94,48 +113,24 @@ module Reckon
94
113
  results << { :index => index, :money_score => money_score, :date_score => date_score }
95
114
  end
96
115
 
97
- return [results, found_likely_money_column]
98
- end
116
+ results.sort_by! { |n| -n[:money_score] }
99
117
 
100
- def merge_columns(a, b)
101
- output_columns = []
102
- columns.each_with_index do |column, index|
103
- if index == a
104
- new_column = MoneyColumn.new( column )
105
- .merge!( MoneyColumn.new( columns[b] ) )
106
- .map { |m| m.amount.to_s }
107
- output_columns << new_column
108
- elsif index == b
109
- # skip
110
- else
111
- output_columns << column
112
- end
118
+ # check if it looks like a 2-column file with a balance field
119
+ if results.length >= 3 && results[1][:money_score] + results[2][:money_score] >= results[0][:money_score]
120
+ results[1][:is_money_column] = true
121
+ results[2][:is_money_column] = true
122
+ else
123
+ results[0][:is_money_column] = true
113
124
  end
114
- output_columns
115
- end
116
125
 
117
- def evaluate_two_money_columns( columns, id1, id2, unmerged_results )
118
- merged_columns = merge_columns( id1, id2 )
119
- results, found_likely_money_column = evaluate_columns( merged_columns )
120
- if !found_likely_money_column
121
- new_res = results.find { |el| el[:index] == id1 }
122
- old_res1 = unmerged_results.find { |el| el[:index] == id1 }
123
- old_res2 = unmerged_results.find { |el| el[:index] == id2 }
124
- if new_res[:money_score] > old_res1[:money_score] &&
125
- new_res[:money_score] > old_res2[:money_score]
126
- found_likely_money_column = true
127
- end
128
- end
129
- [results, found_likely_money_column]
126
+ return results.sort_by { |n| n[:index] }
130
127
  end
131
128
 
132
- def found_double_money_column( id1, id2 )
133
- self.money_column_indices = [ id1, id2 ]
134
- unless settings[:testing]
135
- puts "It looks like this CSV has two seperate columns for money, one of which shows positive"
136
- puts "changes and one of which shows negative changes. If this is true, great. Otherwise,"
137
- puts "please report this issue to us so we can take a look!\n"
138
- end
129
+ def found_double_money_column(id1, id2)
130
+ self.money_column_indices = [id1, id2]
131
+ puts "It looks like this CSV has two seperate columns for money, one of which shows positive"
132
+ puts "changes and one of which shows negative changes. If this is true, great. Otherwise,"
133
+ puts "please report this issue to us so we can take a look!\n"
139
134
  end
140
135
 
141
136
  # Some csv files negative/positive amounts are indicated in separate account
@@ -165,41 +160,18 @@ module Reckon
165
160
  end
166
161
 
167
162
  def detect_columns
168
- results, found_likely_money_column = evaluate_columns(columns)
163
+ results = evaluate_columns(columns)
164
+
169
165
  if options[:money_column]
170
- found_likely_money_column = true
171
166
  self.money_column_indices = [ options[:money_column] - 1 ]
172
167
  else
173
- self.money_column_indices = [ results.max_by { |n| n[:money_score] }[:index] ]
174
- end
175
-
176
- if !found_likely_money_column
177
- found_likely_double_money_columns = false
178
- 0.upto(columns.length - 2) do |i|
179
- if MoneyColumn.new( columns[i] ).merge!( MoneyColumn.new( columns[i+1] ) )
180
- _, found_likely_double_money_columns = evaluate_columns(merge_columns(i, i+1))
181
- if found_likely_double_money_columns
182
- found_double_money_column( i, i + 1 )
183
- break
184
- end
185
- end
186
- end
187
-
188
- if !found_likely_double_money_columns
189
- 0.upto(columns.length - 2) do |i|
190
- if MoneyColumn.new( columns[i] ).merge!( MoneyColumn.new( columns[i+1] ) )
191
- # Try a more specific test
192
- _, found_likely_double_money_columns = evaluate_two_money_columns( columns, i, i+1, results )
193
- if found_likely_double_money_columns
194
- found_double_money_column( i, i + 1 )
195
- break
196
- end
197
- end
198
- end
199
- end
200
-
201
- if !found_likely_double_money_columns && !settings[:testing]
202
- puts "I didn't find a high-likelyhood money column, but I'm taking my best guess with column #{money_column_indices.first + 1}."
168
+ self.money_column_indices = results.select { |n| n[:is_money_column] }.map { |n| n[:index] }
169
+ if self.money_column_indices.length == 1
170
+ puts "Using column #{money_column_indices.first + 1} as the money column. Use --money-colum to specify a different one."
171
+ elsif self.money_column_indices.length == 2
172
+ found_double_money_column(*self.money_column_indices)
173
+ else
174
+ puts "Unable to determine a money column, use --money-column to specify the column reckon should use."
203
175
  end
204
176
  end
205
177
 
@@ -223,23 +195,6 @@ module Reckon
223
195
  self.description_column_indices = results.map { |i| i[:index] }
224
196
  end
225
197
 
226
- def columns
227
- @columns ||= begin
228
- last_row_length = nil
229
- csv_data.inject([]) do |memo, row|
230
- # fail "Input CSV must have consistent row lengths." if last_row_length && row.length != last_row_length
231
- unless row.all? { |i| i.nil? || i.length == 0 }
232
- row.each_with_index do |entry, index|
233
- memo[index] ||= []
234
- memo[index] << (entry || '').strip
235
- end
236
- last_row_length = row.length
237
- end
238
- memo
239
- end
240
- end
241
- end
242
-
243
198
  def parse(data, filename=nil)
244
199
  # Use force_encoding to convert the string to utf-8 with as few invalid characters
245
200
  # as possible.
@@ -281,15 +236,5 @@ module Reckon
281
236
  end
282
237
  m && m[1]
283
238
  end
284
-
285
- @settings = { :testing => false }
286
-
287
- def self.settings
288
- @settings
289
- end
290
-
291
- def settings
292
- self.class.settings
293
- end
294
239
  end
295
240
  end
@@ -71,12 +71,13 @@ module Reckon
71
71
 
72
72
  def Money::likelihood( entry )
73
73
  money_score = 0
74
- money_score += 20 if entry[/^[\-\+\(]{0,2}\$/]
74
+ # digits separated by , or . with no more than 2 trailing digits
75
+ money_score += 40 if entry.match(/\d+[,.]\d{2}[^\d]*$/)
75
76
  money_score += 10 if entry[/^\$?\-?\$?\d+[\.,\d]*?[\.,]\d\d$/]
76
77
  money_score += 10 if entry[/\d+[\.,\d]*?[\.,]\d\d$/]
77
78
  money_score += entry.gsub(/[^\d\.\-\+,\(\)]/, '').length if entry.length < 7
78
- money_score -= entry.length if entry.length > 8
79
- money_score -= 20 if entry !~ /^[\$\+\.\-,\d\(\)]+$/
79
+ money_score -= entry.length if entry.length > 12
80
+ money_score -= 20 if (entry !~ /^[\$\+\.\-,\d\(\)]+$/) && entry.length > 0
80
81
  money_score
81
82
  end
82
83
  end
@@ -1,3 +1,3 @@
1
1
  module Reckon
2
- VERSION = "0.5.1"
2
+ VERSION = "0.5.2"
3
3
  end
@@ -0,0 +1,8 @@
1
+ 01/09/2015,05354 SUBWAY,8.19,,1000.00
2
+ 02/18/2015,WENDY'S #6338,8.55,,1000.00
3
+ 02/25/2015,WENDY'S #6338,8.55,,1000.00
4
+ 02/25/2015,WENDY'S #6338,9.14,,1000.00
5
+ 02/27/2015,WENDY'S #6338,5.85,,1000.00
6
+ 03/09/2015,WENDY'S #6338,17.70,,1000.00
7
+ 03/16/2015,WENDY'S #6338,11.15,,1000.00
8
+ 03/23/2015,WENDY'S,10.12,,1000.00
@@ -0,0 +1,9 @@
1
+ Expenses:
2
+ Dining:
3
+ Coffee:
4
+ - 'STARBUCKS'
5
+ - 'TIM HORTON'
6
+ Resturant:
7
+ - 'WENDY''S'
8
+ - 'SUBWAY'
9
+ - 'BARAKAT'
@@ -0,0 +1,2 @@
1
+ Visa, 4514010000000000, 2020-02-20, , GOJEK SINGAPORE, 8.10 SGD @ .976500000000, -7.91, D
2
+ Visa, 4514010000000000, 2020-02-20, , GOJEK SINGAPORE, 6.00 SGD @ .976600000000, -5.86, D
@@ -121,6 +121,22 @@ describe Reckon::App do
121
121
  end
122
122
  end
123
123
 
124
+ context 'Issue #51 - regression test' do
125
+ it 'should assign correct accounts with tokens' do
126
+ output = StringIO.new
127
+ Reckon::App.new(
128
+ file: fixture_path('51-sample.csv'),
129
+ unattended: true,
130
+ account_tokens_file: fixture_path('51-tokens.yml'),
131
+ ignore_columns: [5],
132
+ bank_account: 'Assets:Chequing',
133
+ output_file: output
134
+ ).walk_backwards
135
+ expect(output.string).not_to include('Income:Unknown')
136
+ expect(output.string.scan('Expenses:Dining:Resturant').size).to eq(8)
137
+ end
138
+ end
139
+
124
140
  #DATA
125
141
  BANK_CSV = (<<-CSV).strip
126
142
  DEBIT,20091224120000[0:GMT],"HOST 037196321563 MO 12/22SLICEHOST",-85.00
@@ -5,38 +5,29 @@ require_relative "../spec_helper"
5
5
  require 'rubygems'
6
6
  require_relative '../../lib/reckon'
7
7
 
8
- Reckon::CSVParser.settings[:testing] = true
9
-
10
8
  describe Reckon::CSVParser do
11
- before do
12
- @chase = Reckon::CSVParser.new(file: fixture_path('chase.csv'))
13
- @some_other_bank = Reckon::CSVParser.new(file: fixture_path('some_other.csv'))
14
- @two_money_columns = Reckon::CSVParser.new(file: fixture_path('two_money_columns.csv'))
15
- @suntrust_csv = Reckon::CSVParser.new(file: fixture_path('suntrust.csv'))
16
- @simple_csv = Reckon::CSVParser.new(file: fixture_path('simple.csv'))
17
- @nationwide = Reckon::CSVParser.new(file: fixture_path('nationwide.csv'), csv_separator: ',', suffixed: true, currency: "POUND")
18
- @german_date = Reckon::CSVParser.new(file: fixture_path('german_date_example.csv'))
19
- @danish_kroner_nordea = Reckon::CSVParser.new(file: fixture_path('danish_kroner_nordea_example.csv'), csv_separator: ';', comma_separates_cents: true)
20
- @yyyymmdd_date = Reckon::CSVParser.new(file: fixture_path('yyyymmdd_date_example.csv'))
21
- @spanish_date = Reckon::CSVParser.new(file: fixture_path('spanish_date_example.csv'), date_format: '%d/%m/%Y')
22
- @english_date = Reckon::CSVParser.new(file: fixture_path('english_date_example.csv'))
23
- @ing_csv = Reckon::CSVParser.new(file: fixture_path('ing.csv'), comma_separates_cents: true )
24
- @austrian_csv = Reckon::CSVParser.new(file: fixture_path('austrian_example.csv'), comma_separates_cents: true, csv_separator: ';' )
25
- @french_csv = Reckon::CSVParser.new(file: fixture_path('french_example.csv'), csv_separator: ';', comma_separates_cents: true)
26
- @broker_canada = Reckon::CSVParser.new(file: fixture_path('broker_canada_example.csv'))
27
- @intuit_mint = Reckon::CSVParser.new(file: fixture_path('intuit_mint_example.csv'))
28
- end
29
-
30
- it "should be in testing mode" do
31
- @chase.settings[:testing].should be true
32
- Reckon::CSVParser.settings[:testing].should be true
33
- end
9
+ let(:chase) { Reckon::CSVParser.new(file: fixture_path('chase.csv')) }
10
+ let(:some_other_bank) { Reckon::CSVParser.new(file: fixture_path('some_other.csv')) }
11
+ let(:two_money_columns) { Reckon::CSVParser.new(file: fixture_path('two_money_columns.csv')) }
12
+ let(:suntrust_csv) { Reckon::CSVParser.new(file: fixture_path('suntrust.csv')) }
13
+ let(:simple_csv) { Reckon::CSVParser.new(file: fixture_path('simple.csv')) }
14
+ let(:nationwide) { Reckon::CSVParser.new(file: fixture_path('nationwide.csv'), csv_separator: ',', suffixed: true, currency: "POUND") }
15
+ let(:german_date) { Reckon::CSVParser.new(file: fixture_path('german_date_example.csv')) }
16
+ let(:danish_kroner_nordea) { Reckon::CSVParser.new(file: fixture_path('danish_kroner_nordea_example.csv'), csv_separator: ';', comma_separates_cents: true) }
17
+ let(:yyyymmdd_date) { Reckon::CSVParser.new(file: fixture_path('yyyymmdd_date_example.csv')) }
18
+ let(:spanish_date) { Reckon::CSVParser.new(file: fixture_path('spanish_date_example.csv'), date_format: '%d/%m/%Y') }
19
+ let(:english_date) { Reckon::CSVParser.new(file: fixture_path('english_date_example.csv')) }
20
+ let(:ing_csv) { Reckon::CSVParser.new(file: fixture_path('ing.csv'), comma_separates_cents: true ) }
21
+ let(:austrian_csv) { Reckon::CSVParser.new(file: fixture_path('austrian_example.csv'), comma_separates_cents: true, csv_separator: ';' ) }
22
+ let(:french_csv) { Reckon::CSVParser.new(file: fixture_path('french_example.csv'), csv_separator: ';', comma_separates_cents: true) }
23
+ let(:broker_canada) { Reckon::CSVParser.new(file: fixture_path('broker_canada_example.csv')) }
24
+ let(:intuit_mint) { Reckon::CSVParser.new(file: fixture_path('intuit_mint_example.csv')) }
34
25
 
35
26
  describe "parse" do
36
27
  it "should use binary encoding if none specified and chardet fails" do
37
28
  allow(CharDet).to receive(:detect).and_return({'encoding' => nil})
38
29
  app = Reckon::CSVParser.new(file: fixture_path("extratofake.csv"))
39
- expect(app.try_encoding("foobarbaz")).to eq("BINARY")
30
+ expect(app.send(:try_encoding, "foobarbaz")).to eq("BINARY")
40
31
  end
41
32
 
42
33
  it "should work with foreign character encodings" do
@@ -76,8 +67,8 @@ describe Reckon::CSVParser do
76
67
 
77
68
  describe "columns" do
78
69
  it "should return the csv transposed" do
79
- @simple_csv.columns.should == [["entry1", "entry4"], ["entry2", "entry5"], ["entry3", "entry6"]]
80
- @chase.columns.length.should == 4
70
+ simple_csv.columns.should == [["entry1", "entry4"], ["entry2", "entry5"], ["entry3", "entry6"]]
71
+ chase.columns.length.should == 4
81
72
  end
82
73
 
83
74
  it "should be ok with empty lines" do
@@ -88,46 +79,44 @@ describe Reckon::CSVParser do
88
79
  end
89
80
 
90
81
  describe "detect_columns" do
91
- before do
92
- @harder_date_example_csv = Reckon::CSVParser.new(file: fixture_path('harder_date_example.csv'))
93
- end
82
+ let(:harder_date_example_csv) { Reckon::CSVParser.new(file: fixture_path('harder_date_example.csv')) }
94
83
 
95
84
  it "should detect the money column" do
96
- @chase.money_column_indices.should == [3]
97
- @some_other_bank.money_column_indices.should == [3]
98
- @two_money_columns.money_column_indices.should == [3, 4]
99
- @suntrust_csv.money_column_indices.should == [3, 4]
100
- @nationwide.money_column_indices.should == [3, 4]
101
- @harder_date_example_csv.money_column_indices.should == [1]
102
- @danish_kroner_nordea.money_column_indices.should == [3]
103
- @yyyymmdd_date.money_column_indices.should == [3]
104
- @ing_csv.money_column_indices.should == [6]
105
- @austrian_csv.money_column_indices.should == [4]
106
- @french_csv.money_column_indices.should == [4]
107
- @broker_canada.money_column_indices.should == [8]
108
- @intuit_mint.money_column_indices.should == [3]
85
+ chase.money_column_indices.should == [3]
86
+ some_other_bank.money_column_indices.should == [3]
87
+ two_money_columns.money_column_indices.should == [3, 4]
88
+ suntrust_csv.money_column_indices.should == [3, 4]
89
+ nationwide.money_column_indices.should == [3, 4]
90
+ harder_date_example_csv.money_column_indices.should == [1]
91
+ danish_kroner_nordea.money_column_indices.should == [3]
92
+ yyyymmdd_date.money_column_indices.should == [3]
93
+ ing_csv.money_column_indices.should == [6]
94
+ austrian_csv.money_column_indices.should == [4]
95
+ french_csv.money_column_indices.should == [4]
96
+ broker_canada.money_column_indices.should == [8]
97
+ intuit_mint.money_column_indices.should == [3]
109
98
  end
110
99
 
111
100
  it "should detect the date column" do
112
- @chase.date_column_index.should == 1
113
- @some_other_bank.date_column_index.should == 1
114
- @two_money_columns.date_column_index.should == 0
115
- @harder_date_example_csv.date_column_index.should == 0
116
- @danish_kroner_nordea.date_column_index.should == 0
117
- @yyyymmdd_date.date_column_index.should == 1
118
- @french_csv.date_column_index.should == 1
119
- @broker_canada.date_column_index.should == 0
120
- @intuit_mint.date_column_index.should == 0
101
+ chase.date_column_index.should == 1
102
+ some_other_bank.date_column_index.should == 1
103
+ two_money_columns.date_column_index.should == 0
104
+ harder_date_example_csv.date_column_index.should == 0
105
+ danish_kroner_nordea.date_column_index.should == 0
106
+ yyyymmdd_date.date_column_index.should == 1
107
+ french_csv.date_column_index.should == 1
108
+ broker_canada.date_column_index.should == 0
109
+ intuit_mint.date_column_index.should == 0
121
110
  Reckon::CSVParser.new(:string => '2014-01-13,"22211100000",-10').date_column_index.should == 0
122
111
  end
123
112
 
124
113
  it "should consider all other columns to be description columns" do
125
- @chase.description_column_indices.should == [0, 2]
126
- @some_other_bank.description_column_indices.should == [0, 2]
127
- @two_money_columns.description_column_indices.should == [1, 2, 5]
128
- @harder_date_example_csv.description_column_indices.should == [2, 3, 4, 5, 6, 7]
129
- @danish_kroner_nordea.description_column_indices.should == [1, 2, 4]
130
- @yyyymmdd_date.description_column_indices.should == [0, 2]
114
+ chase.description_column_indices.should == [0, 2]
115
+ some_other_bank.description_column_indices.should == [0, 2]
116
+ two_money_columns.description_column_indices.should == [1, 2, 5]
117
+ harder_date_example_csv.description_column_indices.should == [2, 3, 4, 5, 6, 7]
118
+ danish_kroner_nordea.description_column_indices.should == [1, 2, 4]
119
+ yyyymmdd_date.description_column_indices.should == [0, 2]
131
120
  end
132
121
  end
133
122
 
@@ -143,36 +132,36 @@ describe Reckon::CSVParser do
143
132
 
144
133
  describe "money_for" do
145
134
  it "should return the appropriate fields" do
146
- @chase.money_for(1).should == -20
147
- @chase.money_for(4).should == 1558.52
148
- @chase.money_for(7).should == -116.22
149
- @some_other_bank.money_for(1).should == -20
150
- @some_other_bank.money_for(4).should == 1558.52
151
- @some_other_bank.money_for(7).should == -116.22
152
- @two_money_columns.money_for(0).should == -76
153
- @two_money_columns.money_for(1).should == 327.49
154
- @two_money_columns.money_for(2).should == -800
155
- @two_money_columns.money_for(3).should == -88.55
156
- @two_money_columns.money_for(4).should == 88.55
157
- @nationwide.money_for(0).should == 500.00
158
- @nationwide.money_for(1).should == -20.00
159
- @danish_kroner_nordea.money_for(0).should == -48.00
160
- @danish_kroner_nordea.money_for(1).should == -79.00
161
- @danish_kroner_nordea.money_for(2).should == 497.90
162
- @danish_kroner_nordea.money_for(3).should == -995.00
163
- @danish_kroner_nordea.money_for(4).should == -3452.90
164
- @danish_kroner_nordea.money_for(5).should == -655.00
165
- @yyyymmdd_date.money_for(0).should == -123.45
166
- @ing_csv.money_for(0).should == -136.13
167
- @ing_csv.money_for(1).should == 375.00
168
- @austrian_csv.money_for(0).should == -18.00
169
- @austrian_csv.money_for(2).should == 120.00
170
- @french_csv.money_for(0).should == -10.00
171
- @french_csv.money_for(1).should == -5.76
172
- @broker_canada.money_for(0).should == 12.55
173
- @broker_canada.money_for(1).should == -81.57
174
- @intuit_mint.money_for(0).should == 0.01
175
- @intuit_mint.money_for(1).should == -331.63
135
+ chase.money_for(1).should == -20
136
+ chase.money_for(4).should == 1558.52
137
+ chase.money_for(7).should == -116.22
138
+ some_other_bank.money_for(1).should == -20
139
+ some_other_bank.money_for(4).should == 1558.52
140
+ some_other_bank.money_for(7).should == -116.22
141
+ two_money_columns.money_for(0).should == -76
142
+ two_money_columns.money_for(1).should == 327.49
143
+ two_money_columns.money_for(2).should == -800
144
+ two_money_columns.money_for(3).should == -88.55
145
+ two_money_columns.money_for(4).should == 88.55
146
+ nationwide.money_for(0).should == 500.00
147
+ nationwide.money_for(1).should == -20.00
148
+ danish_kroner_nordea.money_for(0).should == -48.00
149
+ danish_kroner_nordea.money_for(1).should == -79.00
150
+ danish_kroner_nordea.money_for(2).should == 497.90
151
+ danish_kroner_nordea.money_for(3).should == -995.00
152
+ danish_kroner_nordea.money_for(4).should == -3452.90
153
+ danish_kroner_nordea.money_for(5).should == -655.00
154
+ yyyymmdd_date.money_for(0).should == -123.45
155
+ ing_csv.money_for(0).should == -136.13
156
+ ing_csv.money_for(1).should == 375.00
157
+ austrian_csv.money_for(0).should == -18.00
158
+ austrian_csv.money_for(2).should == 120.00
159
+ french_csv.money_for(0).should == -10.00
160
+ french_csv.money_for(1).should == -5.76
161
+ broker_canada.money_for(0).should == 12.55
162
+ broker_canada.money_for(1).should == -81.57
163
+ intuit_mint.money_for(0).should == 0.01
164
+ intuit_mint.money_for(1).should == -331.63
176
165
  end
177
166
 
178
167
  it "should handle the comma_separates_cents option correctly" do
@@ -200,59 +189,58 @@ describe Reckon::CSVParser do
200
189
 
201
190
  describe "date_for" do
202
191
  it "should return a parsed date object" do
203
- @chase.date_for(1).year.should == Time.parse("2009/12/24").year
204
- @chase.date_for(1).month.should == Time.parse("2009/12/24").month
205
- @chase.date_for(1).day.should == Time.parse("2009/12/24").day
206
- @some_other_bank.date_for(1).year.should == Time.parse("2010/12/24").year
207
- @some_other_bank.date_for(1).month.should == Time.parse("2010/12/24").month
208
- @some_other_bank.date_for(1).day.should == Time.parse("2010/12/24").day
209
- @german_date.date_for(1).year.should == Time.parse("2009/12/24").year
210
- @german_date.date_for(1).month.should == Time.parse("2009/12/24").month
211
- @german_date.date_for(1).day.should == Time.parse("2009/12/24").day
212
- @danish_kroner_nordea.date_for(0).year.should == Time.parse("2012/11/16").year
213
- @danish_kroner_nordea.date_for(0).month.should == Time.parse("2012/11/16").month
214
- @danish_kroner_nordea.date_for(0).day.should == Time.parse("2012/11/16").day
215
- @yyyymmdd_date.date_for(0).year.should == Time.parse("2012/12/31").year
216
- @yyyymmdd_date.date_for(0).month.should == Time.parse("2012/12/31").month
217
- @yyyymmdd_date.date_for(0).day.should == Time.parse("2012/12/31").day
218
- @spanish_date.date_for(1).year.should == Time.parse("2009/12/02").year
219
- @spanish_date.date_for(1).month.should == Time.parse("2009/12/02").month
220
- @spanish_date.date_for(1).day.should == Time.parse("2009/12/02").day
221
- @english_date.date_for(1).year.should == Time.parse("2009/12/24").year
222
- @english_date.date_for(1).month.should == Time.parse("2009/12/24").month
223
- @english_date.date_for(1).day.should == Time.parse("2009/12/24").day
224
- @nationwide.date_for(1).month.should == 10
225
- @ing_csv.date_for(1).month.should == Time.parse("2012/11/12").month
226
- @ing_csv.date_for(1).day.should == Time.parse("2012/11/12").day
227
- @broker_canada.date_for(5).year.should == 2014
228
- @broker_canada.date_for(5).month.should == 1
229
- @broker_canada.date_for(5).day.should == 7
230
- @intuit_mint.date_for(1).year.should == 2014
231
- @intuit_mint.date_for(1).month.should == 2
232
- @intuit_mint.date_for(1).day.should == 3
192
+ chase.date_for(1).year.should == Time.parse("2009/12/24").year
193
+ chase.date_for(1).month.should == Time.parse("2009/12/24").month
194
+ chase.date_for(1).day.should == Time.parse("2009/12/24").day
195
+ some_other_bank.date_for(1).year.should == Time.parse("2010/12/24").year
196
+ some_other_bank.date_for(1).month.should == Time.parse("2010/12/24").month
197
+ some_other_bank.date_for(1).day.should == Time.parse("2010/12/24").day
198
+ german_date.date_for(1).year.should == Time.parse("2009/12/24").year
199
+ german_date.date_for(1).month.should == Time.parse("2009/12/24").month
200
+ german_date.date_for(1).day.should == Time.parse("2009/12/24").day
201
+ danish_kroner_nordea.date_for(0).year.should == Time.parse("2012/11/16").year
202
+ danish_kroner_nordea.date_for(0).month.should == Time.parse("2012/11/16").month
203
+ danish_kroner_nordea.date_for(0).day.should == Time.parse("2012/11/16").day
204
+ yyyymmdd_date.date_for(0).year.should == Time.parse("2012/12/31").year
205
+ yyyymmdd_date.date_for(0).month.should == Time.parse("2012/12/31").month
206
+ yyyymmdd_date.date_for(0).day.should == Time.parse("2012/12/31").day
207
+ spanish_date.date_for(1).year.should == Time.parse("2009/12/02").year
208
+ spanish_date.date_for(1).month.should == Time.parse("2009/12/02").month
209
+ spanish_date.date_for(1).day.should == Time.parse("2009/12/02").day
210
+ english_date.date_for(1).year.should == Time.parse("2009/12/24").year
211
+ english_date.date_for(1).month.should == Time.parse("2009/12/24").month
212
+ english_date.date_for(1).day.should == Time.parse("2009/12/24").day
213
+ nationwide.date_for(1).month.should == 10
214
+ ing_csv.date_for(1).month.should == Time.parse("2012/11/12").month
215
+ ing_csv.date_for(1).day.should == Time.parse("2012/11/12").day
216
+ broker_canada.date_for(5).year.should == 2014
217
+ broker_canada.date_for(5).month.should == 1
218
+ broker_canada.date_for(5).day.should == 7
219
+ intuit_mint.date_for(1).year.should == 2014
220
+ intuit_mint.date_for(1).month.should == 2
221
+ intuit_mint.date_for(1).day.should == 3
233
222
  end
234
223
  end
235
224
 
236
225
  describe "description_for" do
237
226
  it "should return the combined fields that are not money for date fields" do
238
- @chase.description_for(1).should == "CHECK; CHECK 2656"
239
- @chase.description_for(7).should == "CREDIT; PAYPAL TRANSFER PPD ID: PAYPALSDSL"
227
+ chase.description_for(1).should == "CHECK; CHECK 2656"
228
+ chase.description_for(7).should == "CREDIT; PAYPAL TRANSFER PPD ID: PAYPALSDSL"
240
229
  end
241
230
 
242
231
  it "should not append empty description column" do
243
232
  parser = Reckon::CSVParser.new(:string => '01/09/2015,05354 SUBWAY,8.19,,',:date_format => '%d/%m/%Y')
244
- parser.description_column_indices.should == [1, 4]
245
233
  parser.description_for(0).should == '05354 SUBWAY'
246
234
  end
247
235
  end
248
236
 
249
237
  describe "pretty_money_for" do
250
238
  it "work with negative and positive numbers" do
251
- @some_other_bank.pretty_money_for(1).should == "-$20.00"
252
- @some_other_bank.pretty_money_for(4).should == " $1558.52"
253
- @some_other_bank.pretty_money_for(7).should == "-$116.22"
254
- @some_other_bank.pretty_money_for(5).should == " $0.23"
255
- @some_other_bank.pretty_money_for(6).should == "-$0.96"
239
+ some_other_bank.pretty_money_for(1).should == "-$20.00"
240
+ some_other_bank.pretty_money_for(4).should == " $1558.52"
241
+ some_other_bank.pretty_money_for(7).should == "-$116.22"
242
+ some_other_bank.pretty_money_for(5).should == " $0.23"
243
+ some_other_bank.pretty_money_for(6).should == "-$0.96"
256
244
  end
257
245
 
258
246
  it "work with other currencies such as €" do
@@ -274,8 +262,15 @@ describe Reckon::CSVParser do
274
262
  end
275
263
 
276
264
  it "should work with merge columns" do
277
- @nationwide.pretty_money_for(0).should == " 500.00 POUND"
278
- @nationwide.pretty_money_for(1).should == "-20.00 POUND"
265
+ nationwide.pretty_money_for(0).should == " 500.00 POUND"
266
+ nationwide.pretty_money_for(1).should == "-20.00 POUND"
267
+ end
268
+ end
269
+
270
+ describe '85 regression test' do
271
+ it 'should detect correct date column' do
272
+ p = Reckon::CSVParser.new(file:fixture_path('85-date-example.csv'))
273
+ expect(p.date_column_index).to eq(2)
279
274
  end
280
275
  end
281
276
  end
@@ -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" ).should == 2.00
12
- Reckon::Money::from_s( "-$1025.67" ).should == -1025.67
13
- Reckon::Money::from_s( "$-1025.67" ).should == -1025.67
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( "$2,00", :comma_separates_cents => true ).should == 2.00
18
- Reckon::Money::from_s( "-$1025,67", :comma_separates_cents => true ).should == -1025.67
19
- Reckon::Money::from_s( "$-1025,67", :comma_separates_cents => true ).should == -1025.67
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( "" ).should == 0
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( "$2.000,00", :comma_separates_cents => true ).should == 2000.00
28
- Reckon::Money::from_s( "-$1,025.67" ).should == -1025.67
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( "1A1" ).should == 1
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( "1A2.00" ).should == 2
37
- Reckon::Money::from_s( "2.00A1" ).should == 2
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( "AB1.00C" ).should == 1
42
- Reckon::Money::from_s( "AB0C" ).should == 0
43
- Reckon::Money::from_s( "AB-2.00C" ).should == -2
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( "BAC" ).should == nil
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( -20.00 ).pretty.should == "-$20.00"
54
- Reckon::Money.new( 1558.52 ).pretty.should == " $1558.52"
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( -20.00, :currency => "€", :suffixed => false ).pretty.should == "-€20.00"
59
- Reckon::Money.new( 1558.52, :currency => "€", :suffixed => false ).pretty.should == " €1558.52"
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.should == "-20.00 SEK"
64
- Reckon::Money.new( 1558.52, :currency => "SEK", :suffixed => true ).pretty.should == " 1558.52 SEK"
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" ).should == 45
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( 2.0 ).should == Reckon::Money.new( 2.0 )
77
- Reckon::Money.new( 1.0 ).should <= Reckon::Money.new( 2.0 )
78
- Reckon::Money.new( 3.0 ).should > Reckon::Money.new( 2.0 )
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( 2.0 ).should == 2.0
82
- Reckon::Money.new( 1.0 ).should <= 2.0
83
- Reckon::Money.new( 3.0 ).should > 2.0
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
@@ -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.5.1
4
+ version: 0.5.2
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-02-25 00:00:00.000000000 Z
13
+ date: 2020-03-07 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec
@@ -140,9 +140,12 @@ files:
140
140
  - lib/reckon/money.rb
141
141
  - lib/reckon/version.rb
142
142
  - reckon.gemspec
143
+ - spec/data_fixtures/51-sample.csv
144
+ - spec/data_fixtures/51-tokens.yml
143
145
  - spec/data_fixtures/73-sample.csv
144
146
  - spec/data_fixtures/73-tokens.yml
145
147
  - spec/data_fixtures/73-transactions.ledger
148
+ - spec/data_fixtures/85-date-example.csv
146
149
  - spec/data_fixtures/austrian_example.csv
147
150
  - spec/data_fixtures/bom_utf8_file.csv
148
151
  - spec/data_fixtures/broker_canada_example.csv
@@ -193,8 +196,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
193
196
  - !ruby/object:Gem::Version
194
197
  version: '0'
195
198
  requirements: []
196
- rubyforge_project:
197
- rubygems_version: 2.7.6.2
199
+ rubygems_version: 3.0.6
198
200
  signing_key:
199
201
  specification_version: 4
200
202
  summary: Utility for interactively converting and labeling CSV files for the Ledger