reckon 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b58a745c730c0dfaf022a98c19dfaf8568c0b3091548d70c9202f8f7c0d78ada
4
- data.tar.gz: 030d2036524a19c5eff981608133e8aac15f185b57a891e91a6f1fdabd3796c7
3
+ metadata.gz: a7e9512d0b15c14548a04e80f5dd3a87f50ae9ff2654827b5970def94e174667
4
+ data.tar.gz: f858c96b4b5f2a7b2c85845803109e566281c0fc945c46ba39758121701d0e9a
5
5
  SHA512:
6
- metadata.gz: 1daf76a19078f1707e847a7ac54aad5836ee3d49ce9c2f612bfefeb86f8c1bf9c9d1f97b7bbecb309f9f44f8097f8f2dbe0d6b6062bc376a62fc88f45bab1dfc
7
- data.tar.gz: 4784ac871dbdb6160dc53ef8a7f5d69f31ccc7f7f7ea876b97bf60941abb2d13a968f5229747dd74d0b574ecc12326485e1eaba7ea368a91162d36d3ef4f9f86
6
+ metadata.gz: 15526cfe3504d50859de7b652285b31f639b11daecd8536732b97e1f84d03814cb8040c8db830386f803de7836e11712b7069758a77bf290fab26699436440f2
7
+ data.tar.gz: 99ae732793adeaa40f5c7235e690dfb80d839a31ecba52f989e0dea2d339cf012623f858c926261e4bd5eb731e2a3ff508370509d03560c64c2bd7cf4a737a7e
data/.gitignore CHANGED
@@ -25,3 +25,6 @@ pkg
25
25
  .idea
26
26
  reckon_local
27
27
  private_tests
28
+
29
+ ## Bundler
30
+ vendor
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- reckon (0.5.0)
4
+ reckon (0.5.1)
5
5
  chronic (>= 0.3.0)
6
6
  highline (>= 1.5.2)
7
7
  rchardet (>= 1.8.0)
data/README.md CHANGED
@@ -8,7 +8,7 @@ Reckon automagically converts CSV files for use with the command-line accounting
8
8
 
9
9
  Assuming you have Ruby and [Rubygems](http://rubygems.org/pages/download) installed on your system, simply run
10
10
 
11
- (sudo) gem install reckon
11
+ gem install --user reckon
12
12
 
13
13
  ## Example Usage
14
14
 
@@ -10,12 +10,13 @@ require 'terminal-table'
10
10
  require 'time'
11
11
  require 'logger'
12
12
 
13
- LOGGER = Logger.new(STDOUT)
14
- LOGGER.level = Logger::ERROR
13
+ LOGGER = Logger.new(STDERR)
14
+ LOGGER.level = Logger::WARN
15
15
 
16
16
  require_relative 'reckon/version'
17
17
  require_relative 'reckon/cosine_similarity'
18
- require File.expand_path(File.join(File.dirname(__FILE__), "reckon", "app"))
19
- require File.expand_path(File.join(File.dirname(__FILE__), "reckon", "ledger_parser"))
20
- require File.expand_path(File.join(File.dirname(__FILE__), "reckon", "csv_parser"))
21
- require File.expand_path(File.join(File.dirname(__FILE__), "reckon", "money"))
18
+ require_relative 'reckon/date_column'
19
+ require_relative 'reckon/money'
20
+ require_relative 'reckon/ledger_parser'
21
+ require_relative 'reckon/csv_parser'
22
+ require_relative 'reckon/app'
@@ -156,6 +156,10 @@ module Reckon
156
156
  def each_row_backwards
157
157
  rows = []
158
158
  (0...@csv_parser.columns.first.length).to_a.each do |index|
159
+ if @csv_parser.date_for(index).nil?
160
+ LOGGER.warn("Skipping row: '#{@csv_parser.row(index)}' that doesn't have a valid date")
161
+ next
162
+ end
159
163
  rows << { :date => @csv_parser.date_for(index),
160
164
  :pretty_date => @csv_parser.pretty_date_for(index),
161
165
  :pretty_money => @csv_parser.pretty_money_for(index),
@@ -163,9 +167,7 @@ module Reckon
163
167
  :money => @csv_parser.money_for(index),
164
168
  :description => @csv_parser.description_for(index) }
165
169
  end
166
- rows.sort { |a, b| a[:date] <=> b[:date] }.each do |row|
167
- yield row
168
- end
170
+ rows.sort_by { |n| n[:date] }.each {|row| yield row }
169
171
  end
170
172
 
171
173
  def most_specific_regexp_match( row )
@@ -12,6 +12,10 @@ module Reckon
12
12
  detect_columns
13
13
  end
14
14
 
15
+ def row(index)
16
+ csv_data[index].join(", ")
17
+ end
18
+
15
19
  def filter_csv
16
20
  if options[:ignore_columns]
17
21
  new_columns = []
@@ -27,7 +31,10 @@ module Reckon
27
31
  end
28
32
 
29
33
  def pretty_money_for(index, negate = false)
30
- money_for( index ).pretty( negate )
34
+ money = money_for(index)
35
+ return 0 if money.nil?
36
+
37
+ money.pretty(negate)
31
38
  end
32
39
 
33
40
  def pretty_money(amount, negate = false)
@@ -35,7 +42,7 @@ module Reckon
35
42
  end
36
43
 
37
44
  def date_for(index)
38
- @date_column.for( index )
45
+ @date_column.for(index)
39
46
  end
40
47
 
41
48
  def pretty_date_for(index)
@@ -0,0 +1,60 @@
1
+ module Reckon
2
+ class DateColumn < Array
3
+ attr_accessor :endian_precedence
4
+ def initialize( arr = [], options = {} )
5
+ arr.each do |value|
6
+ if options[:date_format]
7
+ begin
8
+ value = Date.strptime(value, options[:date_format])
9
+ rescue
10
+ puts "I'm having trouble parsing #{value} with the desired format: #{options[:date_format]}"
11
+ exit 1
12
+ end
13
+ else
14
+ value = [$1, $2, $3].join("/") if value =~ /^(\d{4})(\d{2})(\d{2})\d+\[\d+\:GMT\]$/ # chase format
15
+ value = [$3, $2, $1].join("/") if value =~ /^(\d{2})\.(\d{2})\.(\d{4})$/ # german format
16
+ value = [$3, $2, $1].join("/") if value =~ /^(\d{2})\-(\d{2})\-(\d{4})$/ # nordea format
17
+ value = [$1, $2, $3].join("/") if value =~ /^(\d{4})\-(\d{2})\-(\d{2})$/ # yyyy-mm-dd format
18
+ value = [$1, $2, $3].join("/") if value =~ /^(\d{4})(\d{2})(\d{2})/ # yyyymmdd format
19
+
20
+
21
+ unless @endian_precedence # Try to detect endian_precedence
22
+ reg_match = value.match( /^(\d\d)\/(\d\d)\/\d\d\d?\d?/ )
23
+ # If first one is not \d\d/\d\d/\d\d\d?\d set it to default
24
+ if !reg_match
25
+ @endian_precedence = [:middle, :little]
26
+ elsif reg_match[1].to_i > 12
27
+ @endian_precedence = [:little]
28
+ elsif reg_match[2].to_i > 12
29
+ @endian_precedence = [:middle]
30
+ end
31
+ end
32
+ end
33
+ self.push( value )
34
+ end
35
+ # if endian_precedence still nil, raise error
36
+ unless @endian_precedence || options[:date_format]
37
+ raise( "Unable to determine date format. Please specify using --date-format" )
38
+ end
39
+ end
40
+
41
+ def for( index )
42
+ value = self.at( index )
43
+ guess = Chronic.parse(value, :context => :past,
44
+ :endian_precedence => @endian_precedence )
45
+ if guess.to_i < 953236800 && value =~ /\//
46
+ guess = Chronic.parse((value.split("/")[0...-1] + [(2000 + value.split("/").last.to_i).to_s]).join("/"), :context => :past,
47
+ :endian_precedence => @endian_precedence)
48
+ end
49
+ guess && guess.to_date
50
+ end
51
+
52
+ def pretty_for(index)
53
+ date = self.for(index)
54
+ return "" if date.nil?
55
+
56
+ date.iso8601
57
+ end
58
+
59
+ end
60
+ end
@@ -112,60 +112,4 @@ module Reckon
112
112
  self
113
113
  end
114
114
  end
115
-
116
- class DateColumn < Array
117
- attr_accessor :endian_precedence
118
- def initialize( arr = [], options = {} )
119
- arr.each do |value|
120
- if options[:date_format]
121
- begin
122
- value = Date.strptime(value, options[:date_format])
123
- rescue
124
- puts "I'm having trouble parsing #{value} with the desired format: #{options[:date_format]}"
125
- exit 1
126
- end
127
- else
128
- value = [$1, $2, $3].join("/") if value =~ /^(\d{4})(\d{2})(\d{2})\d+\[\d+\:GMT\]$/ # chase format
129
- value = [$3, $2, $1].join("/") if value =~ /^(\d{2})\.(\d{2})\.(\d{4})$/ # german format
130
- value = [$3, $2, $1].join("/") if value =~ /^(\d{2})\-(\d{2})\-(\d{4})$/ # nordea format
131
- value = [$1, $2, $3].join("/") if value =~ /^(\d{4})\-(\d{2})\-(\d{2})$/ # yyyy-mm-dd format
132
- value = [$1, $2, $3].join("/") if value =~ /^(\d{4})(\d{2})(\d{2})/ # yyyymmdd format
133
-
134
-
135
- unless @endian_precedence # Try to detect endian_precedence
136
- reg_match = value.match( /^(\d\d)\/(\d\d)\/\d\d\d?\d?/ )
137
- # If first one is not \d\d/\d\d/\d\d\d?\d set it to default
138
- if !reg_match
139
- @endian_precedence = [:middle, :little]
140
- elsif reg_match[1].to_i > 12
141
- @endian_precedence = [:little]
142
- elsif reg_match[2].to_i > 12
143
- @endian_precedence = [:middle]
144
- end
145
- end
146
- end
147
- self.push( value )
148
- end
149
- # if endian_precedence still nil, raise error
150
- unless @endian_precedence || options[:date_format]
151
- raise( "Unable to determine date format. Please specify using --date-format" )
152
- end
153
- end
154
-
155
- def for( index )
156
- value = self.at( index )
157
- guess = Chronic.parse(value, :context => :past,
158
- :endian_precedence => @endian_precedence )
159
- if guess.to_i < 953236800 && value =~ /\//
160
- guess = Chronic.parse((value.split("/")[0...-1] + [(2000 + value.split("/").last.to_i).to_s]).join("/"), :context => :past,
161
- :endian_precedence => @endian_precedence)
162
- end
163
- guess
164
- end
165
-
166
- def pretty_for(index)
167
- self.for(index).to_date.iso8601
168
- end
169
-
170
- end
171
115
  end
@@ -0,0 +1,3 @@
1
+ module Reckon
2
+ VERSION = "0.5.1"
3
+ end
@@ -1,5 +1,5 @@
1
1
  $:.push File.expand_path("../lib", __FILE__)
2
- require 'reckon/version'
2
+ require_relative 'lib/reckon/version'
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = %q{reckon}
@@ -9,6 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.homepage = %q{https://github.com/cantino/reckon}
10
10
  s.description = %q{Reckon automagically converts CSV files for use with the command-line accounting tool Ledger. It also helps you to select the correct accounts associated with the CSV data using Bayesian machine learning.}
11
11
  s.summary = %q{Utility for interactively converting and labeling CSV files for the Ledger accounting tool.}
12
+ s.licenses = ['MIT']
12
13
 
13
14
  s.files = `git ls-files`.split("\n")
14
15
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -0,0 +1,3 @@
1
+ "Date","Note","Amount"
2
+ "2012/3/22","DEPOSIT","50.00"
3
+ "2012/3/23","TRANSFER TO SAVINGS","-10.00"
@@ -107,6 +107,20 @@ describe Reckon::App do
107
107
  end
108
108
  end
109
109
 
110
+ context "Issue #64 - regression test" do
111
+ it 'should work for simple file' do
112
+ rows = []
113
+ app = Reckon::App.new(file: fixture_path('test_money_column.csv'))
114
+ expect { app.each_row_backwards { |n| rows << n } }
115
+ .to output(/Skipping row: 'Date, Note, Amount'/).to_stderr_from_any_process
116
+ expect(rows.length).to eq(2)
117
+ expect(rows[0][:pretty_date]).to eq('2012-03-22')
118
+ expect(rows[0][:pretty_money]).to eq(' $50.00')
119
+ expect(rows[1][:pretty_date]).to eq('2012-03-23')
120
+ expect(rows[1][:pretty_money]).to eq('-$10.00')
121
+ end
122
+ end
123
+
110
124
  #DATA
111
125
  BANK_CSV = (<<-CSV).strip
112
126
  DEBIT,20091224120000[0:GMT],"HOST 037196321563 MO 12/22SLICEHOST",-85.00
@@ -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( ["13/12/2013"] ).for( 0 ).should ==
24
- Time.new( 2013, 12, 13, 12 )
25
- Reckon::DateColumn.new( ["01/14/2013"] ).for( 0 ).should ==
26
- Time.new( 2013, 01, 14, 12 )
27
- Reckon::DateColumn.new( ["13/12/2013", "21/11/2013"] ).for( 1 ).should ==
28
- Time.new( 2013, 11, 21, 12 )
29
- Reckon::DateColumn.new( ["2013-11-21"] ).for( 0 ).should ==
30
- Time.new( 2013, 11, 21, 12 )
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( ["01/02/2013", "01/14/2013"] ).for(0).should ==
36
- Time.new( 2013, 01, 02, 12 )
37
- Reckon::DateColumn.new( ["01/02/2013", "14/01/2013"] ).for(0).should ==
38
- Time.new( 2013, 02, 01, 12 )
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
-
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.0
4
+ version: 0.5.1
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-19 00:00:00.000000000 Z
13
+ date: 2020-02-25 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec
@@ -135,8 +135,10 @@ files:
135
135
  - lib/reckon/app.rb
136
136
  - lib/reckon/cosine_similarity.rb
137
137
  - lib/reckon/csv_parser.rb
138
+ - lib/reckon/date_column.rb
138
139
  - lib/reckon/ledger_parser.rb
139
140
  - lib/reckon/money.rb
141
+ - lib/reckon/version.rb
140
142
  - reckon.gemspec
141
143
  - spec/data_fixtures/73-sample.csv
142
144
  - spec/data_fixtures/73-tokens.yml
@@ -160,6 +162,7 @@ files:
160
162
  - spec/data_fixtures/some_other.csv
161
163
  - spec/data_fixtures/spanish_date_example.csv
162
164
  - spec/data_fixtures/suntrust.csv
165
+ - spec/data_fixtures/test_money_column.csv
163
166
  - spec/data_fixtures/tokens.yaml
164
167
  - spec/data_fixtures/two_money_columns.csv
165
168
  - spec/data_fixtures/yyyymmdd_date_example.csv
@@ -172,7 +175,8 @@ files:
172
175
  - spec/spec.opts
173
176
  - spec/spec_helper.rb
174
177
  homepage: https://github.com/cantino/reckon
175
- licenses: []
178
+ licenses:
179
+ - MIT
176
180
  metadata: {}
177
181
  post_install_message:
178
182
  rdoc_options: []