reckon 0.4.3 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +2 -0
- data/lib/reckon/app.rb +35 -10
- data/reckon.gemspec +1 -1
- data/spec/data_fixtures/tokens.yaml +3 -1
- data/spec/reckon/app_spec.rb +2 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf1d814fe911f299cf58401c3b36734e29a172f1
|
4
|
+
data.tar.gz: 9130495690de1f73cfa3570a6e3386f38df86830
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7c3e23f96983cd79844ba37524f1b843a1dbba230198cf813b83c7c65a076506fb2c66ab663dee0970db2d42e1d3e0b03adf41ee163d08f3b38dbb163c373a6
|
7
|
+
data.tar.gz: d74ab72db15d54bc0bfe548369ec9a8b3903760bc09fbc16340a6f64c929a69d595c4840900e8a425996bc580e5924321ece84b10d3e033b193b066f2520f7c2
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -90,6 +90,8 @@ Expenses:
|
|
90
90
|
- 'MasterCard'
|
91
91
|
Rent:
|
92
92
|
- '0011223344' # Landlord bank number
|
93
|
+
Hosting:
|
94
|
+
- /hosting/i # This regexp will catch descriptions such as WebHosting or filehosting
|
93
95
|
'[Internal:Transfer]': # Virtual account
|
94
96
|
- '4433221100' # Your own account number
|
95
97
|
```
|
data/lib/reckon/app.rb
CHANGED
@@ -4,12 +4,13 @@ require 'yaml'
|
|
4
4
|
|
5
5
|
module Reckon
|
6
6
|
class App
|
7
|
-
VERSION = "Reckon 0.4.
|
8
|
-
attr_accessor :options, :accounts, :tokens, :seen, :csv_parser
|
7
|
+
VERSION = "Reckon 0.4.4"
|
8
|
+
attr_accessor :options, :accounts, :tokens, :seen, :csv_parser, :regexps
|
9
9
|
|
10
10
|
def initialize(options = {})
|
11
11
|
self.options = options
|
12
12
|
self.tokens = {}
|
13
|
+
self.regexps = {}
|
13
14
|
self.accounts = {}
|
14
15
|
self.seen = {}
|
15
16
|
self.options[:currency] ||= '$'
|
@@ -54,7 +55,7 @@ module Reckon
|
|
54
55
|
if options[:account_tokens_file]
|
55
56
|
fail "#{options[:account_tokens_file]} doesn't exist!" unless File.exists?(options[:account_tokens_file])
|
56
57
|
extract_account_tokens(YAML.load_file(options[:account_tokens_file])).each do |account, tokens|
|
57
|
-
tokens.each { |t| learn_about_account(account, t) }
|
58
|
+
tokens.each { |t| learn_about_account(account, t, true) }
|
58
59
|
end
|
59
60
|
end
|
60
61
|
return unless options[:existing_ledger_file]
|
@@ -63,13 +64,27 @@ module Reckon
|
|
63
64
|
learn_from(ledger_data)
|
64
65
|
end
|
65
66
|
|
66
|
-
def learn_about_account(account, data)
|
67
|
+
def learn_about_account(account, data, parse_regexps = false)
|
67
68
|
accounts[account] ||= 0
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
69
|
+
if parse_regexps && data.start_with?('/')
|
70
|
+
# https://github.com/tenderlove/psych/blob/master/lib/psych/visitors/to_ruby.rb
|
71
|
+
match = data.match(/^\/(.*)\/([ix]*)$/m)
|
72
|
+
fail "failed to parse regexp #{data}" unless match
|
73
|
+
options = 0
|
74
|
+
(match[2] || '').split('').each do |option|
|
75
|
+
case option
|
76
|
+
when 'x' then options |= Regexp::EXTENDED
|
77
|
+
when 'i' then options |= Regexp::IGNORECASE
|
78
|
+
end
|
79
|
+
end
|
80
|
+
regexps[Regexp.new(match[1], options)] = account
|
81
|
+
else
|
82
|
+
tokenize(data).each do |token|
|
83
|
+
tokens[token] ||= {}
|
84
|
+
tokens[token][account] ||= 0
|
85
|
+
tokens[token][account] += 1
|
86
|
+
accounts[account] += 1
|
87
|
+
end
|
73
88
|
end
|
74
89
|
end
|
75
90
|
|
@@ -92,7 +107,8 @@ module Reckon
|
|
92
107
|
seen_anything_new = true
|
93
108
|
end
|
94
109
|
|
95
|
-
possible_answers =
|
110
|
+
possible_answers = most_specific_regexp_match(row)
|
111
|
+
possible_answers = weighted_account_match( row ).map! { |a| a[:account] } if possible_answers.empty?
|
96
112
|
|
97
113
|
ledger = if row[:money] > 0
|
98
114
|
if options[:unattended]
|
@@ -151,6 +167,15 @@ module Reckon
|
|
151
167
|
options[:output_file].flush
|
152
168
|
end
|
153
169
|
|
170
|
+
def most_specific_regexp_match( row )
|
171
|
+
matches = regexps.map { |regexp, account|
|
172
|
+
if match = regexp.match(row[:description])
|
173
|
+
[account, match[0]]
|
174
|
+
end
|
175
|
+
}.compact
|
176
|
+
matches.sort_by! { |account, matched_text| matched_text.length }.map(&:first)
|
177
|
+
end
|
178
|
+
|
154
179
|
# Weigh accounts by how well they match the row
|
155
180
|
def weighted_account_match( row )
|
156
181
|
query_tokens = tokenize(row[:description])
|
data/reckon.gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = %q{reckon}
|
6
|
-
s.version = "0.4.
|
6
|
+
s.version = "0.4.4"
|
7
7
|
s.authors = ["Andrew Cantino", "BlackEdder"]
|
8
8
|
s.email = %q{andrew@iterationlabs.com}
|
9
9
|
s.homepage = %q{https://github.com/cantino/reckon}
|
data/spec/reckon/app_spec.rb
CHANGED
@@ -62,13 +62,14 @@ describe Reckon::App do
|
|
62
62
|
@output_file.string.scan('Expenses:Books').count.should == 1
|
63
63
|
end
|
64
64
|
|
65
|
-
it 'should learn from an account tokens file' do
|
65
|
+
it 'should learn from an account tokens file and parse regexps' do
|
66
66
|
@chase = Reckon::App.new(:string => BANK_CSV,
|
67
67
|
:unattended => true,
|
68
68
|
:output_file => @output_file,
|
69
69
|
:account_tokens_file => 'spec/data_fixtures/tokens.yaml')
|
70
70
|
@chase.walk_backwards
|
71
71
|
@output_file.string.scan('Expenses:Books').count.should == 1
|
72
|
+
@output_file.string.scan('Expenses:Websites').count.should == 2
|
72
73
|
end
|
73
74
|
end
|
74
75
|
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.
|
4
|
+
version: 0.4.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Cantino
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-12-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|