reckon 0.7.1 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +7 -13
- data/.gitignore +3 -0
- data/CHANGELOG.md +43 -1
- data/Gemfile.lock +3 -1
- data/README.md +8 -5
- data/Rakefile +2 -2
- data/bin/reckon +3 -0
- data/lib/reckon/app.rb +6 -6
- data/lib/reckon/cosine_similarity.rb +67 -62
- data/lib/reckon/date_column.rb +3 -2
- data/lib/reckon/ledger_parser.rb +1 -1
- data/lib/reckon/money.rb +12 -5
- data/lib/reckon/options.rb +9 -1
- data/lib/reckon/version.rb +1 -1
- data/reckon.gemspec +1 -0
- data/spec/cosine_training_and_test.rb +52 -0
- data/spec/integration/another_bank_example/output.ledger +3 -3
- data/spec/integration/ask_for_account/cli_input.exp +33 -0
- data/spec/integration/ask_for_account/expected_output +11 -0
- data/spec/integration/ask_for_account/input.csv +9 -0
- data/spec/integration/ask_for_account/test_args +1 -0
- data/spec/integration/broker_canada_example/output.ledger +2 -2
- data/spec/integration/chase/account_tokens_and_regex/output.ledger +3 -3
- data/spec/integration/chase/default_account_names/output.ledger +3 -3
- data/spec/integration/chase/learn_from_existing/output.ledger +3 -3
- data/spec/integration/chase/simple/output.ledger +3 -3
- data/spec/integration/danish_kroner_nordea_example/output.ledger +1 -1
- data/spec/integration/extratofake/output.ledger +1 -1
- data/spec/integration/harder_date_example/output.ledger +2 -2
- data/spec/integration/ledger_date_format/compare_cmds +1 -0
- data/spec/integration/ledger_date_format/input.csv +3 -0
- data/spec/integration/ledger_date_format/output.ledger +12 -0
- data/spec/integration/ledger_date_format/test_args +1 -0
- data/spec/integration/test.sh +79 -27
- data/spec/reckon/app_spec.rb +1 -1
- data/spec/reckon/csv_parser_spec.rb +3 -3
- data/spec/reckon/date_column_spec.rb +12 -0
- data/spec/reckon/money_spec.rb +3 -3
- metadata +160 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08cdfad096b04db11f671775c0119fcecefa7db46134f1c43677e949628498db'
|
4
|
+
data.tar.gz: 5ed2722b2843a5147faabdd82b0027fe590ff9dd66000100e756fad6de13a116
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ebe28c482f091424d405efa1dc4b94cdc76022b114ce6f0db22a96e3ec6104a6e1efecec833af981f9c97f4c705cbbbd7f791403b5b669452abccd2caf295c8
|
7
|
+
data.tar.gz: 7af0ef7c71bcca9b189d23939ebeb93eedbe0875373818419d878461c33035a5e92359204c939ac3d8933935ad29b8a9cec319ddb84ea13626e22f51897f2fea
|
data/.github/workflows/ruby.yml
CHANGED
@@ -21,23 +21,17 @@ jobs:
|
|
21
21
|
matrix:
|
22
22
|
ruby-version:
|
23
23
|
# Current ruby stable version
|
24
|
+
- 3.1.2
|
25
|
+
# Ubuntu 22.04
|
24
26
|
- 3.0
|
25
|
-
# Ubuntu 20.
|
27
|
+
# Ubuntu 20.04
|
26
28
|
- 2.7
|
27
|
-
# Ubuntu 19.10
|
28
|
-
- 2.5
|
29
|
-
# Mac v11 Big Sur
|
30
|
-
# - 2.6?
|
31
|
-
# Mac v10.15 Catalina
|
32
|
-
- 2.6
|
33
|
-
# Mac v10.14 Mojave
|
34
|
-
- 2.3.7
|
35
29
|
steps:
|
36
30
|
- uses: actions/checkout@v2
|
31
|
+
- name: Update package
|
32
|
+
run: sudo apt-get update
|
37
33
|
- name: Install packages
|
38
|
-
run: sudo apt-get -y install ledger hledger
|
39
|
-
- name: Install bundler
|
40
|
-
run: sudo gem install -v 1.17.3 bundler
|
34
|
+
run: sudo apt-get -y install ledger hledger expect
|
41
35
|
- name: Set up Ruby
|
42
36
|
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
43
37
|
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
@@ -45,6 +39,6 @@ jobs:
|
|
45
39
|
# uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
|
46
40
|
with:
|
47
41
|
ruby-version: ${{ matrix.ruby-version }}
|
48
|
-
bundler-cache: true # runs 'bundle install' and caches installed gems
|
42
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems
|
49
43
|
- name: Run tests
|
50
44
|
run: bundle exec rake test_all
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,48 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
## [v0.
|
3
|
+
## [v0.8.1](https://github.com/cantino/reckon/tree/v0.8.1) (2022-07-02)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/cantino/reckon/compare/v0.8.0...v0.8.1)
|
6
|
+
|
7
|
+
**Closed issues:**
|
8
|
+
|
9
|
+
- Reckon files every transaction \(incoming and outgoing\) as Income:Unknown [\#116](https://github.com/cantino/reckon/issues/116)
|
10
|
+
|
11
|
+
**Merged pull requests:**
|
12
|
+
|
13
|
+
- fix bugs with test.sh [\#117](https://github.com/cantino/reckon/pull/117) ([benprew](https://github.com/benprew))
|
14
|
+
|
15
|
+
## [v0.8.0](https://github.com/cantino/reckon/tree/v0.8.0) (2021-08-08)
|
16
|
+
|
17
|
+
[Full Changelog](https://github.com/cantino/reckon/compare/v0.7.2...v0.8.0)
|
18
|
+
|
19
|
+
**Closed issues:**
|
20
|
+
|
21
|
+
- --date-format '%d/%m/%Y' not working [\#113](https://github.com/cantino/reckon/issues/113)
|
22
|
+
- Reckon behaviour does not match what is explained on README.md [\#112](https://github.com/cantino/reckon/issues/112)
|
23
|
+
- --date-format '%d/%m/%Y' not working [\#111](https://github.com/cantino/reckon/issues/111)
|
24
|
+
- --date-format '%d/ [\#110](https://github.com/cantino/reckon/issues/110)
|
25
|
+
|
26
|
+
**Merged pull requests:**
|
27
|
+
|
28
|
+
- Add ledger-date-format option to specify ledger file date format [\#114](https://github.com/cantino/reckon/pull/114) ([benprew](https://github.com/benprew))
|
29
|
+
|
30
|
+
## [v0.7.2](https://github.com/cantino/reckon/tree/v0.7.2) (2021-04-22)
|
31
|
+
|
32
|
+
[Full Changelog](https://github.com/cantino/reckon/compare/v0.7.1...v0.7.2)
|
33
|
+
|
34
|
+
**Closed issues:**
|
35
|
+
|
36
|
+
- \[feature request\] Better format for large transactions [\#108](https://github.com/cantino/reckon/issues/108)
|
37
|
+
- cosine similarity not comparing documents correctly [\#106](https://github.com/cantino/reckon/issues/106)
|
38
|
+
|
39
|
+
**Merged pull requests:**
|
40
|
+
|
41
|
+
- Add thousands separator in money output. Fixes \#108. [\#109](https://github.com/cantino/reckon/pull/109) ([benprew](https://github.com/benprew))
|
42
|
+
- Cosine similarity should use all docs tokens. not just matched tokens. [\#107](https://github.com/cantino/reckon/pull/107) ([benprew](https://github.com/benprew))
|
43
|
+
- Test getting expect working with actions [\#105](https://github.com/cantino/reckon/pull/105) ([benprew](https://github.com/benprew))
|
44
|
+
|
45
|
+
## [v0.7.1](https://github.com/cantino/reckon/tree/v0.7.1) (2021-02-07)
|
4
46
|
|
5
47
|
[Full Changelog](https://github.com/cantino/reckon/compare/v0.7.0...v0.7.1)
|
6
48
|
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
reckon (0.
|
4
|
+
reckon (0.8.1)
|
5
5
|
chronic (>= 0.3.0)
|
6
6
|
highline (>= 1.5.2)
|
7
|
+
matrix (>= 0.4.2)
|
7
8
|
rchardet (>= 1.8.0)
|
8
9
|
|
9
10
|
GEM
|
@@ -13,6 +14,7 @@ GEM
|
|
13
14
|
coderay (1.1.2)
|
14
15
|
diff-lcs (1.3)
|
15
16
|
highline (2.0.3)
|
17
|
+
matrix (0.4.2)
|
16
18
|
method_source (0.9.2)
|
17
19
|
pry (0.12.2)
|
18
20
|
coderay (~> 1.1.0)
|
data/README.md
CHANGED
@@ -58,10 +58,13 @@ Learn more:
|
|
58
58
|
--encoding 'UTF-8'
|
59
59
|
Specify an encoding for the CSV file
|
60
60
|
-c, --currency '$' Currency symbol to use - default $ (ex £, EUR)
|
61
|
-
--date-format
|
62
|
-
|
61
|
+
--date-format FORMAT
|
62
|
+
CSV file date format (see `date` for format)
|
63
|
+
--ledger-date-format FORMAT
|
64
|
+
Ledger date format (see `date` for format)
|
63
65
|
-u, --unattended Don't ask questions and guess all the accounts automatically. Use with --learn-from or --account-tokens options.
|
64
66
|
-t, --account-tokens FILE YAML file with manually-assigned tokens for each account (see README)
|
67
|
+
--table-output-file FILE
|
65
68
|
--default-into-account NAME
|
66
69
|
Default into account
|
67
70
|
--default-outof-account NAME
|
@@ -80,13 +83,13 @@ If you find CSV files that it can't parse, send me examples or pull requests!
|
|
80
83
|
You can run reckon in a non-interactive mode.
|
81
84
|
To guess the accounts reckon can use an existing ledger file or a token file with keywords.
|
82
85
|
|
83
|
-
`reckon --unattended -l 2010.dat -f bank.csv -o ledger.dat`
|
86
|
+
`reckon --unattended -a Checking -l 2010.dat -f bank.csv -o ledger.dat`
|
84
87
|
|
85
|
-
`reckon --unattended --account-tokens tokens.yaml -f bank.csv -o ledger.dat`
|
88
|
+
`reckon --unattended -a Checking --account-tokens tokens.yaml -f bank.csv -o ledger.dat`
|
86
89
|
|
87
90
|
In unattended mode, you can use STDIN to read your csv data, by specifying `-` as the argument to `-f`.
|
88
91
|
|
89
|
-
`csv_file_generator | reckon --unattended -l 2010.dat -o ledger.dat -f -`
|
92
|
+
`csv_file_generator | reckon --unattended -a Checking -l 2010.dat -o ledger.dat -f -`
|
90
93
|
|
91
94
|
### Account Tokens
|
92
95
|
|
data/Rakefile
CHANGED
@@ -17,6 +17,6 @@ task :test_all do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
task :integration_tests do
|
20
|
-
|
21
|
-
raise 'Integration tests failed'
|
20
|
+
cmd = 'prove -v ./spec/integration/test.sh'
|
21
|
+
raise 'Integration tests failed' unless system(cmd)
|
22
22
|
end
|
data/bin/reckon
CHANGED
data/lib/reckon/app.rb
CHANGED
@@ -20,10 +20,10 @@ module Reckon
|
|
20
20
|
learn!
|
21
21
|
end
|
22
22
|
|
23
|
-
def interactive_output(str)
|
23
|
+
def interactive_output(str, fh = $stdout)
|
24
24
|
return if options[:unattended]
|
25
25
|
|
26
|
-
puts str
|
26
|
+
fh.puts str
|
27
27
|
end
|
28
28
|
|
29
29
|
def learn!
|
@@ -161,7 +161,7 @@ module Reckon
|
|
161
161
|
rows.sort_by { |n| [n[:date], -n[:money], n[:description]] }.each { |row| yield row }
|
162
162
|
end
|
163
163
|
|
164
|
-
def print_transaction(rows)
|
164
|
+
def print_transaction(rows, fh = $stdout)
|
165
165
|
str = "\n"
|
166
166
|
header = %w[Date Amount Description Note]
|
167
167
|
maxes = header.map(&:length)
|
@@ -185,7 +185,7 @@ module Reckon
|
|
185
185
|
str += "\n"
|
186
186
|
end
|
187
187
|
|
188
|
-
interactive_output str
|
188
|
+
interactive_output str, fh
|
189
189
|
end
|
190
190
|
|
191
191
|
def ask_account_question(msg, row)
|
@@ -280,12 +280,12 @@ module Reckon
|
|
280
280
|
exit
|
281
281
|
end
|
282
282
|
|
283
|
-
def output_table
|
283
|
+
def output_table(fh = $stdout)
|
284
284
|
rows = []
|
285
285
|
each_row_backwards do |row|
|
286
286
|
rows << row
|
287
287
|
end
|
288
|
-
print_transaction(rows)
|
288
|
+
print_transaction(rows, fh)
|
289
289
|
end
|
290
290
|
end
|
291
291
|
end
|
@@ -1,47 +1,52 @@
|
|
1
1
|
require 'matrix'
|
2
2
|
require 'set'
|
3
3
|
|
4
|
-
# Implementation of
|
5
|
-
#
|
4
|
+
# Implementation of cosine similarity using TF-IDF for vectorization.
|
5
|
+
#
|
6
|
+
# In information retrieval, tf–idf, short for term frequency–inverse document frequency,
|
7
|
+
# is a numerical statistic that is intended to reflect how important a word is to a
|
8
|
+
# document in a collection or corpus
|
9
|
+
#
|
10
|
+
# Cosine Similarity a measurement to determine how similar 2 documents are to each other.
|
11
|
+
#
|
12
|
+
# These weights and measures are used to suggest which account a transaction should be
|
13
|
+
# assigned to.
|
6
14
|
module Reckon
|
7
15
|
class CosineSimilarity
|
16
|
+
DocumentInfo = Struct.new(:tokens, :accounts)
|
17
|
+
|
8
18
|
def initialize(options)
|
19
|
+
@docs = DocumentInfo.new({}, {})
|
9
20
|
@options = options
|
10
|
-
@tokens = {}
|
11
|
-
@accounts = Hash.new(0)
|
12
21
|
end
|
13
22
|
|
14
23
|
def add_document(account, doc)
|
15
|
-
tokenize(doc)
|
24
|
+
tokens = tokenize(doc)
|
25
|
+
LOGGER.info "doc tokens: #{tokens}"
|
26
|
+
tokens.each do |n|
|
16
27
|
(token, count) = n
|
17
28
|
|
18
|
-
@tokens[token] ||=
|
19
|
-
@tokens[token][account]
|
20
|
-
@
|
21
|
-
@accounts[account] += count
|
29
|
+
@docs.tokens[token] ||= Hash.new(0)
|
30
|
+
@docs.tokens[token][account] += count
|
31
|
+
@docs.accounts[account] ||= Hash.new(0)
|
32
|
+
@docs.accounts[account][token] += count
|
22
33
|
end
|
23
34
|
end
|
24
35
|
|
25
36
|
# find most similar documents to query
|
26
37
|
def find_similar(query)
|
27
|
-
|
38
|
+
LOGGER.info "find_similar #{query}"
|
28
39
|
|
29
|
-
|
40
|
+
accounts = docs_to_check(query).map do |a|
|
41
|
+
[a, tfidf(@docs.accounts[a])]
|
42
|
+
end
|
30
43
|
|
31
|
-
|
32
|
-
suggestions = corpus_scores.map do |account, scores|
|
33
|
-
acct_vector = Vector.elements(scores, false)
|
44
|
+
q = tfidf(tokenize(query))
|
34
45
|
|
35
|
-
|
36
|
-
# similarity is a float between 1 and -1, where 1 is exactly the same and -1 is
|
37
|
-
# exactly opposite
|
38
|
-
# see https://en.wikipedia.org/wiki/Cosine_similarity
|
39
|
-
# cos(theta) = (A . B) / (||A|| ||B||)
|
40
|
-
# where A . B is the "dot product" and ||A|| is the magnitude of A
|
41
|
-
# ruby has the 'matrix' library we can use to do these calculations.
|
46
|
+
suggestions = accounts.map do |a, d|
|
42
47
|
{
|
43
|
-
similarity:
|
44
|
-
account:
|
48
|
+
similarity: calc_similarity(q, d),
|
49
|
+
account: a
|
45
50
|
}
|
46
51
|
end.select { |n| n[:similarity] > 0 }.sort_by { |n| -n[:similarity] }
|
47
52
|
|
@@ -52,50 +57,51 @@ module Reckon
|
|
52
57
|
|
53
58
|
private
|
54
59
|
|
55
|
-
def
|
56
|
-
|
57
|
-
|
58
|
-
corpus_scores = {}
|
59
|
-
query_scores = []
|
60
|
-
num_docs = @accounts.length
|
61
|
-
|
62
|
-
query_tokens.each do |n|
|
63
|
-
(token, _count) = n
|
64
|
-
next unless @tokens[token]
|
65
|
-
corpus = corpus.union(Set.new(@tokens[token].keys))
|
60
|
+
def docs_to_check(query)
|
61
|
+
return tokenize(query).reduce(Set.new) do |corpus, t|
|
62
|
+
corpus.union(Set.new(@docs.tokens[t[0]]&.keys))
|
66
63
|
end
|
64
|
+
end
|
67
65
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
# if no other docs have token, ignore it
|
72
|
-
next unless @tokens[token]
|
66
|
+
def tfidf(tokens)
|
67
|
+
scores = {}
|
73
68
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
@tokens[
|
79
|
-
|
69
|
+
tokens.each do |t, n|
|
70
|
+
scores[t] = calc_tf_idf(
|
71
|
+
n,
|
72
|
+
tokens.length,
|
73
|
+
@docs.tokens[t]&.length&.to_f || 0,
|
74
|
+
@docs.accounts.length
|
80
75
|
)
|
81
|
-
|
82
|
-
## Next, calculate for the corpus, where our "account" is a document
|
83
|
-
corpus.each do |account|
|
84
|
-
corpus_scores[account] ||= []
|
85
|
-
|
86
|
-
corpus_scores[account] << calc_tf_idf(
|
87
|
-
(@tokens[token][account] || 0),
|
88
|
-
@accounts[account].to_f,
|
89
|
-
@tokens[token].length.to_f,
|
90
|
-
num_docs
|
91
|
-
)
|
92
|
-
end
|
93
76
|
end
|
94
|
-
|
77
|
+
|
78
|
+
return scores
|
95
79
|
end
|
96
80
|
|
97
|
-
|
81
|
+
# Cosine similarity is used to compare how similar 2 documents are. Returns a float
|
82
|
+
# between 1 and -1, where 1 is exactly the same and -1 is exactly opposite.
|
83
|
+
#
|
84
|
+
# see https://en.wikipedia.org/wiki/Cosine_similarity
|
85
|
+
# cos(theta) = (A . B) / (||A|| ||B||)
|
86
|
+
# where A . B is the "dot product" and ||A|| is the magnitude of A
|
87
|
+
#
|
88
|
+
# The variables A and B are the set of unique terms in q and d.
|
89
|
+
#
|
90
|
+
# For example, when q = "big red balloon" and d ="small green balloon" then the
|
91
|
+
# variables are (big,red,balloon,small,green) and a = (1,1,1,0,0) and b =
|
92
|
+
# (0,0,1,1,1).
|
93
|
+
#
|
94
|
+
# query and doc are hashes of token => tf/idf score
|
95
|
+
def calc_similarity(query, doc)
|
96
|
+
tokens = Set.new(query.keys + doc.keys)
|
97
|
+
|
98
|
+
a = Vector.elements(tokens.map { |n| query[n] || 0 }, false)
|
99
|
+
b = Vector.elements(tokens.map { |n| doc[n] || 0 }, false)
|
100
|
+
|
101
|
+
return a.inner_product(b) / (a.magnitude * b.magnitude)
|
102
|
+
end
|
98
103
|
|
104
|
+
def calc_tf_idf(token_count, num_words_in_doc, df, num_docs)
|
99
105
|
# tf(t,d) = count of t in d / number of words in d
|
100
106
|
tf = token_count / num_words_in_doc.to_f
|
101
107
|
|
@@ -109,14 +115,13 @@ module Reckon
|
|
109
115
|
end
|
110
116
|
|
111
117
|
def tokenize(str)
|
112
|
-
mk_tokens(str).
|
118
|
+
mk_tokens(str).each_with_object(Hash.new(0)) do |n, memo|
|
113
119
|
memo[n] += 1
|
114
|
-
memo
|
115
120
|
end.to_a
|
116
121
|
end
|
117
122
|
|
118
123
|
def mk_tokens(str)
|
119
|
-
str.downcase.tr(';', ' ').tr("'", '').split(/[^a-z0-9.]+/)
|
124
|
+
str.downcase.tr(';', ' ').tr("'", '').split(/[^a-z0-9.]+/).reject(&:empty?)
|
120
125
|
end
|
121
126
|
end
|
122
127
|
end
|
data/lib/reckon/date_column.rb
CHANGED
@@ -2,12 +2,13 @@ module Reckon
|
|
2
2
|
class DateColumn < Array
|
3
3
|
attr_accessor :endian_precedence
|
4
4
|
def initialize( arr = [], options = {} )
|
5
|
+
@options = options
|
5
6
|
arr.each do |value|
|
6
7
|
if options[:date_format]
|
7
8
|
begin
|
8
9
|
value = Date.strptime(value, options[:date_format])
|
9
10
|
rescue
|
10
|
-
puts "I'm having trouble parsing #{value} with the desired format: #{options[:date_format]}"
|
11
|
+
puts "I'm having trouble parsing '#{value}' with the desired format: #{options[:date_format]}"
|
11
12
|
exit 1
|
12
13
|
end
|
13
14
|
else
|
@@ -53,7 +54,7 @@ module Reckon
|
|
53
54
|
date = self.for(index)
|
54
55
|
return "" if date.nil?
|
55
56
|
|
56
|
-
date.
|
57
|
+
date.strftime(@options[:ledger_date_format] || '%Y-%m-%d')
|
57
58
|
end
|
58
59
|
|
59
60
|
def self.likelihood(entry)
|
data/lib/reckon/ledger_parser.rb
CHANGED
@@ -114,7 +114,7 @@ module Reckon
|
|
114
114
|
|
115
115
|
def initialize(ledger, options = {})
|
116
116
|
@options = options
|
117
|
-
@date_format = options[:date_format] || '%Y-%m-%d'
|
117
|
+
@date_format = options[:ledger_date_format] || options[:date_format] || '%Y-%m-%d'
|
118
118
|
parse(ledger)
|
119
119
|
end
|
120
120
|
|
data/lib/reckon/money.rb
CHANGED
@@ -50,11 +50,18 @@ module Reckon
|
|
50
50
|
return @amount_raw[0] == '-' ? @amount_raw[1..-1] : "-#{@amount_raw}"
|
51
51
|
end
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
53
|
+
amt = pretty_amount(@amount * (negate ? -1 : 1))
|
54
|
+
amt = if @suffixed
|
55
|
+
"#{amt} #{@currency}"
|
56
|
+
else
|
57
|
+
amt.gsub(/^((-)|)(?=\d)/, "\\1#{@currency}")
|
58
|
+
end
|
59
|
+
|
60
|
+
return (@amount >= 0 ? " " : "") + amt
|
61
|
+
end
|
62
|
+
|
63
|
+
def pretty_amount(amount)
|
64
|
+
sprintf("%0.2f", amount).reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
58
65
|
end
|
59
66
|
|
60
67
|
def parse(value, options = {})
|
data/lib/reckon/options.rb
CHANGED
@@ -73,10 +73,14 @@ module Reckon
|
|
73
73
|
options[:currency] = e
|
74
74
|
end
|
75
75
|
|
76
|
-
opts.on("", "--date-format
|
76
|
+
opts.on("", "--date-format FORMAT", "CSV file date format (see `date` for format)") do |d|
|
77
77
|
options[:date_format] = d
|
78
78
|
end
|
79
79
|
|
80
|
+
opts.on("", "--ledger-date-format FORMAT", "Ledger date format (see `date` for format)") do |d|
|
81
|
+
options[:ledger_date_format] = d
|
82
|
+
end
|
83
|
+
|
80
84
|
opts.on("-u", "--unattended", "Don't ask questions and guess all the accounts automatically. Use with --learn-from or --account-tokens options.") do |n|
|
81
85
|
options[:unattended] = n
|
82
86
|
end
|
@@ -85,6 +89,10 @@ module Reckon
|
|
85
89
|
options[:account_tokens_file] = a
|
86
90
|
end
|
87
91
|
|
92
|
+
opts.on("", "--table-output-file FILE") do |n|
|
93
|
+
options[:table_output_file] = n
|
94
|
+
end
|
95
|
+
|
88
96
|
options[:default_into_account] = 'Expenses:Unknown'
|
89
97
|
opts.on("", "--default-into-account NAME", "Default into account") do |a|
|
90
98
|
options[:default_into_account] = a
|
data/lib/reckon/version.rb
CHANGED
data/reckon.gemspec
CHANGED
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
require 'reckon'
|
6
|
+
|
7
|
+
ledger_file = ARGV[0]
|
8
|
+
account = ARGV[1]
|
9
|
+
seed = ARGV[2] ? ARGV[2].to_i : Random.new_seed
|
10
|
+
|
11
|
+
ledger = Reckon::LedgerParser.new(File.read(ledger_file))
|
12
|
+
matcher = Reckon::CosineSimilarity.new({})
|
13
|
+
|
14
|
+
train = []
|
15
|
+
test = []
|
16
|
+
|
17
|
+
def has_account(account, entry)
|
18
|
+
entry[:accounts].map { |a| a[:name] }.include?(account)
|
19
|
+
end
|
20
|
+
|
21
|
+
entries = ledger.entries.select { |e| has_account(account, e) }
|
22
|
+
|
23
|
+
r = Random.new(seed)
|
24
|
+
entries.length.times do |i|
|
25
|
+
r.rand < 0.9 ? train << i : test << i
|
26
|
+
end
|
27
|
+
|
28
|
+
train.each do |i|
|
29
|
+
entry = entries[i]
|
30
|
+
entry[:accounts].each do |a|
|
31
|
+
matcher.add_document(
|
32
|
+
a[:name],
|
33
|
+
[entry[:desc], a[:amount]].join(" ")
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
result = [nil] * test.length
|
39
|
+
test.each do |i|
|
40
|
+
entry = entries[i]
|
41
|
+
matches = matcher.find_similar(
|
42
|
+
entry[:desc] + " " + entry[:accounts][0][:amount].to_s
|
43
|
+
)
|
44
|
+
|
45
|
+
if !matches[0] || !has_account(matches[0][:account], entry)
|
46
|
+
result[i] = [entry, matches]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# pp result.compact
|
51
|
+
puts "using #{seed} as random seed"
|
52
|
+
puts "true: #{result.count(nil)} false: #{result.count { |v| !v.nil? }}"
|
@@ -1,5 +1,5 @@
|
|
1
1
|
2003-12-24 CREDIT; Some Company vendorpymt PPD ID: 5KL3832735
|
2
|
-
Assets:Bank:Checking $
|
2
|
+
Assets:Bank:Checking $2,105.00
|
3
3
|
Income:Unknown
|
4
4
|
|
5
5
|
2004-12-24 CREDIT; PAYPAL TRANSFER PPD ID: PAYPALSDSL
|
@@ -15,11 +15,11 @@
|
|
15
15
|
Expenses:Unknown
|
16
16
|
|
17
17
|
2007-12-24 CREDIT; Blarg BLARG REVENUE PPD ID: 00jah78563
|
18
|
-
Assets:Bank:Checking $
|
18
|
+
Assets:Bank:Checking $1,558.52
|
19
19
|
Income:Unknown
|
20
20
|
|
21
21
|
2008-12-24 CREDIT; Some Company vendorpymt PPD ID: 59728JSL20
|
22
|
-
Assets:Bank:Checking $
|
22
|
+
Assets:Bank:Checking $3,520.00
|
23
23
|
Income:Unknown
|
24
24
|
|
25
25
|
2009-12-24 DEBIT; GITHUB 041287430274 CA 12/22GITHUB 04
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/expect -f
|
2
|
+
|
3
|
+
|
4
|
+
set timeout 7
|
5
|
+
match_max 100000
|
6
|
+
expect "What is this account named in Ledger |Assets:Bank:Checking|?\r
|
7
|
+
\[1G▽\[6n"
|
8
|
+
send -- "\[45;2R"
|
9
|
+
expect -exact "\[1G\[K\[6n"
|
10
|
+
send -- "\[45;1R"
|
11
|
+
expect "\[1G\[K\[1G\[1G"
|
12
|
+
send -- "T"
|
13
|
+
expect "\[1GT\[K\[1G\[2G"
|
14
|
+
send -- "e"
|
15
|
+
expect "\[1GTe\[K\[1G\[3G"
|
16
|
+
send -- "s"
|
17
|
+
expect "\[1GTes\[K\[1G\[4G"
|
18
|
+
send -- "t"
|
19
|
+
expect "\[1GTest\[K\[1G\[5G"
|
20
|
+
send -- ":"
|
21
|
+
expect "\[1GTest:\[K\[1G\[6G"
|
22
|
+
send -- ":"
|
23
|
+
expect "\[1GTest::\[K\[1G\[7G"
|
24
|
+
send -- "B"
|
25
|
+
expect "\[1GTest::B\[K\[1G\[8G"
|
26
|
+
send -- "a"
|
27
|
+
expect "\[1GTest::Ba\[K\[1G\[9G"
|
28
|
+
send -- "n"
|
29
|
+
expect "\[1GTest::Ban\[K\[1G\[10G"
|
30
|
+
send -- "k"
|
31
|
+
expect "\[1GTest::Bank\[K\[1G\[11G"
|
32
|
+
send -- "\r"
|
33
|
+
expect eof
|
@@ -0,0 +1,11 @@
|
|
1
|
+
|
2
|
+
Date | Amount | Description | Note |
|
3
|
+
2003-12-24 | $2,105.00 | CREDIT; Some Company vendorpymt PPD ID: 5KL3832735 | |
|
4
|
+
2004-12-24 | -$116.22 | CREDIT; PAYPAL TRANSFER PPD ID: PAYPALSDSL | |
|
5
|
+
2005-12-24 | -$0.96 | DEBIT; WEBSITE-BALANCE-10DEC09 12 12/10WEBSITE-BAL | |
|
6
|
+
2006-12-24 | $0.23 | DEBIT; WEBSITE-BALANCE-17DEC09 12 12/17WEBSITE-BAL | |
|
7
|
+
2007-12-24 | $1,558.52 | CREDIT; Blarg BLARG REVENUE PPD ID: 00jah78563 | |
|
8
|
+
2008-12-24 | $3,520.00 | CREDIT; Some Company vendorpymt PPD ID: 59728JSL20 | |
|
9
|
+
2009-12-24 | -$7.00 | DEBIT; GITHUB 041287430274 CA 12/22GITHUB 04 | |
|
10
|
+
2010-12-24 | -$20.00 | CHECK; CHECK 2656 | |
|
11
|
+
2011-12-24 | -$85.00 | DEBIT; HOST 037196321563 MO 12/22SLICEHOST | |
|
@@ -0,0 +1,9 @@
|
|
1
|
+
DEBIT,2011/12/24,"HOST 037196321563 MO 12/22SLICEHOST",($85.00)
|
2
|
+
CHECK,2010/12/24,"CHECK 2656",($20.00)
|
3
|
+
DEBIT,2009/12/24,"GITHUB 041287430274 CA 12/22GITHUB 04",($7.00)
|
4
|
+
CREDIT,2008/12/24,"Some Company vendorpymt PPD ID: 59728JSL20",$3520.00
|
5
|
+
CREDIT,2007/12/24,"Blarg BLARG REVENUE PPD ID: 00jah78563",$1558.52
|
6
|
+
DEBIT,2006/12/24,"WEBSITE-BALANCE-17DEC09 12 12/17WEBSITE-BAL",$.23
|
7
|
+
DEBIT,2005/12/24,"WEBSITE-BALANCE-10DEC09 12 12/10WEBSITE-BAL",($0.96)
|
8
|
+
CREDIT,2004/12/24,"PAYPAL TRANSFER PPD ID: PAYPALSDSL",($116.22)
|
9
|
+
CREDIT,2003/12/24,"Some Company vendorpymt PPD ID: 5KL3832735",$2105.00
|
@@ -0,0 +1 @@
|
|
1
|
+
-f input.csv -p
|
@@ -8,7 +8,7 @@
|
|
8
8
|
|
9
9
|
2013-06-19 2013-06-24; Buy; ISHARES S&P/TSX CAPPED REIT IN; XRE; 300; 15.90; CDN; CAD
|
10
10
|
Income:Unknown
|
11
|
-
Assets:Bank:Checking -$
|
11
|
+
Assets:Bank:Checking -$4,779.95
|
12
12
|
|
13
13
|
2013-06-27 2013-06-27; Dividend; ICICI BK SPONSORED ADR; IBN; 100; USD
|
14
14
|
Assets:Bank:Checking $66.70
|
@@ -19,7 +19,7 @@
|
|
19
19
|
Income:Unknown
|
20
20
|
|
21
21
|
2014-01-07 2014-01-10; Sell; BMO NASDAQ 100 EQTY HEDGED TO; ZQQ; -300; 27.44; CDN; CAD
|
22
|
-
Assets:Bank:Checking $
|
22
|
+
Assets:Bank:Checking $8,222.05
|
23
23
|
Income:Unknown
|
24
24
|
|
25
25
|
2014-01-07 2014-01-07; Interest; BMO S&P/TSX EQUAL WEIGHT BKS I; ZEB; 250; CAD
|
@@ -1,5 +1,5 @@
|
|
1
1
|
2009-12-10 CREDIT; Some Company vendorpymt PPD ID: 5KL3832735
|
2
|
-
Assets:Bank:Checking $
|
2
|
+
Assets:Bank:Checking $2,105.00
|
3
3
|
Income:Unknown
|
4
4
|
|
5
5
|
2009-12-11 CREDIT; PAYPAL TRANSFER PPD ID: PAYPALSDSL
|
@@ -15,11 +15,11 @@
|
|
15
15
|
Assets:Bank:Checking -$12.23
|
16
16
|
|
17
17
|
2009-12-23 CREDIT; Some Company vendorpymt PPD ID: 59728JSL20
|
18
|
-
Assets:Bank:Checking $
|
18
|
+
Assets:Bank:Checking $3,520.00
|
19
19
|
Income:Unknown
|
20
20
|
|
21
21
|
2009-12-23 CREDIT; Blarg BLARG REVENUE PPD ID: 00jah78563
|
22
|
-
Assets:Bank:Checking $
|
22
|
+
Assets:Bank:Checking $1,558.52
|
23
23
|
Income:Unknown
|
24
24
|
|
25
25
|
2009-12-24 DEBIT; GITHUB 041287430274 CA 12/22GITHUB 04
|
@@ -1,5 +1,5 @@
|
|
1
1
|
2009-12-10 CREDIT; Some Company vendorpymt PPD ID: 5KL3832735
|
2
|
-
Assets:Bank:Checking $
|
2
|
+
Assets:Bank:Checking $2,105.00
|
3
3
|
Income:Default
|
4
4
|
|
5
5
|
2009-12-11 CREDIT; PAYPAL TRANSFER PPD ID: PAYPALSDSL
|
@@ -15,11 +15,11 @@
|
|
15
15
|
Assets:Bank:Checking -$12.23
|
16
16
|
|
17
17
|
2009-12-23 CREDIT; Some Company vendorpymt PPD ID: 59728JSL20
|
18
|
-
Assets:Bank:Checking $
|
18
|
+
Assets:Bank:Checking $3,520.00
|
19
19
|
Income:Default
|
20
20
|
|
21
21
|
2009-12-23 CREDIT; Blarg BLARG REVENUE PPD ID: 00jah78563
|
22
|
-
Assets:Bank:Checking $
|
22
|
+
Assets:Bank:Checking $1,558.52
|
23
23
|
Income:Default
|
24
24
|
|
25
25
|
2009-12-24 DEBIT; GITHUB 041287430274 CA 12/22GITHUB 04
|
@@ -1,5 +1,5 @@
|
|
1
1
|
2009-12-10 CREDIT; Some Company vendorpymt PPD ID: 5KL3832735
|
2
|
-
Assets:Bank:Checking $
|
2
|
+
Assets:Bank:Checking $2,105.00
|
3
3
|
Income:Unknown
|
4
4
|
|
5
5
|
2009-12-11 CREDIT; PAYPAL TRANSFER PPD ID: PAYPALSDSL
|
@@ -15,11 +15,11 @@
|
|
15
15
|
Assets:Bank:Checking -$12.23
|
16
16
|
|
17
17
|
2009-12-23 CREDIT; Some Company vendorpymt PPD ID: 59728JSL20
|
18
|
-
Assets:Bank:Checking $
|
18
|
+
Assets:Bank:Checking $3,520.00
|
19
19
|
Income:Unknown
|
20
20
|
|
21
21
|
2009-12-23 CREDIT; Blarg BLARG REVENUE PPD ID: 00jah78563
|
22
|
-
Assets:Bank:Checking $
|
22
|
+
Assets:Bank:Checking $1,558.52
|
23
23
|
Income:Unknown
|
24
24
|
|
25
25
|
2009-12-24 DEBIT; GITHUB 041287430274 CA 12/22GITHUB 04
|
@@ -1,5 +1,5 @@
|
|
1
1
|
2009-12-10 CREDIT; Some Company vendorpymt PPD ID: 5KL3832735
|
2
|
-
Assets:Bank:Checking $
|
2
|
+
Assets:Bank:Checking $2,105.00
|
3
3
|
Income:Unknown
|
4
4
|
|
5
5
|
2009-12-11 CREDIT; PAYPAL TRANSFER PPD ID: PAYPALSDSL
|
@@ -15,11 +15,11 @@
|
|
15
15
|
Assets:Bank:Checking -$12.23
|
16
16
|
|
17
17
|
2009-12-23 CREDIT; Some Company vendorpymt PPD ID: 59728JSL20
|
18
|
-
Assets:Bank:Checking $
|
18
|
+
Assets:Bank:Checking $3,520.00
|
19
19
|
Income:Unknown
|
20
20
|
|
21
21
|
2009-12-23 CREDIT; Blarg BLARG REVENUE PPD ID: 00jah78563
|
22
|
-
Assets:Bank:Checking $
|
22
|
+
Assets:Bank:Checking $1,558.52
|
23
23
|
Income:Unknown
|
24
24
|
|
25
25
|
2009-12-24 DEBIT; GITHUB 041287430274 CA 12/22GITHUB 04
|
@@ -1,5 +1,5 @@
|
|
1
1
|
2009-11-04 TRANSFER CREDIT INTERNET TRANSFER; INTERNET TRANSFER; 1234.00
|
2
|
-
Assets:Bank:Checking $
|
2
|
+
Assets:Bank:Checking $1,234.00
|
3
3
|
Income:Unknown
|
4
4
|
|
5
5
|
2009-11-10 TRANSFER DEBIT INTERNET TRANSFER; INTERNET TRANSFER MORTGAGE; 0.00
|
@@ -16,5 +16,5 @@
|
|
16
16
|
|
17
17
|
2011-11-04 TRANSFER DEBIT INTERNET TRANSFER; INTERNET TRANSFER SAV TO MECU; 0.00
|
18
18
|
Income:Unknown
|
19
|
-
Assets:Bank:Checking -$
|
19
|
+
Assets:Bank:Checking -$1,234.00
|
20
20
|
|
@@ -0,0 +1 @@
|
|
1
|
+
ledger --input-date-format '%d/%m/%Y'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
02/12/2009 BLARG R SH 456930; $1,826.06
|
2
|
+
Assets:Bank:Checking $327.49
|
3
|
+
Income:Unknown
|
4
|
+
|
5
|
+
02/12/2009 Check - 0000000122; 122; $1,750.06
|
6
|
+
Income:Unknown
|
7
|
+
Assets:Bank:Checking -$76.00
|
8
|
+
|
9
|
+
02/12/2009 Check - 0000000112; 112; $1,498.57
|
10
|
+
Income:Unknown
|
11
|
+
Assets:Bank:Checking -$800.00
|
12
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
-f input.csv --unattended --account Assets:Bank:Checking --date-format '%d/%m/%Y' --ledger-date-format '%d/%m/%Y'
|
data/spec/integration/test.sh
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
#!/bin/bash
|
2
2
|
|
3
|
-
# set -x
|
4
|
-
|
5
3
|
set -Euo pipefail
|
6
4
|
|
5
|
+
|
7
6
|
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
8
7
|
TEST_DIFF=""
|
9
8
|
OUTPUT=""
|
10
|
-
RECKON_CMD="
|
9
|
+
RECKON_CMD="reckon -v"
|
10
|
+
export RUBYLIB=$SCRIPT_DIR/../../lib:${RUBYLIB:-}
|
11
11
|
export PATH="$SCRIPT_DIR/../../bin:$PATH"
|
12
12
|
|
13
13
|
main () {
|
@@ -21,28 +21,64 @@ main () {
|
|
21
21
|
|
22
22
|
echo > test.log
|
23
23
|
|
24
|
+
NUM_TESTS=$(echo "$TESTS" |wc -l |awk '{print $1}')
|
25
|
+
|
26
|
+
echo "1..$NUM_TESTS"
|
27
|
+
|
28
|
+
I=1
|
29
|
+
|
24
30
|
for t in $TESTS; do
|
25
|
-
OUTPUT_FILE=$(mktemp)
|
26
31
|
TEST_DIR=$(dirname "$t")
|
32
|
+
TEST_LOG=$(mktemp)
|
27
33
|
pushd "$TEST_DIR" >/dev/null || exit 1
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
compare_output "$OUTPUT_FILE"
|
34
|
+
if [[ -e "cli_input.exp" ]]; then
|
35
|
+
cli_test >$TEST_LOG 2>&1
|
36
|
+
else
|
37
|
+
unattended_test >$TEST_LOG 2>&1
|
38
|
+
fi
|
34
39
|
|
35
40
|
popd >/dev/null || exit 1
|
36
41
|
# have to save output after popd
|
37
42
|
echo -e "\n\n======>$TEST_DIR" >> test.log
|
38
|
-
echo -e "TEST_CMD
|
43
|
+
echo -e "TEST_CMD: $TEST_CMD" >> test.log
|
44
|
+
cat $TEST_LOG >> test.log
|
39
45
|
|
40
46
|
if [[ $ERROR -ne 0 ]]; then
|
47
|
+
echo -e "not ok $I - $TEST_DIR"
|
48
|
+
tail -n25 test.log
|
41
49
|
exit 1
|
50
|
+
else
|
51
|
+
echo -e "ok $I - $TEST_DIR"
|
42
52
|
fi
|
53
|
+
I=$(($I + 1))
|
43
54
|
done
|
44
55
|
}
|
45
56
|
|
57
|
+
cli_test () {
|
58
|
+
OUTPUT_FILE=$(mktemp)
|
59
|
+
CLI_CMD="$RECKON_CMD --table-output-file $OUTPUT_FILE $(cat test_args)"
|
60
|
+
TEST_CMD="expect -d -c 'spawn $CLI_CMD' cli_input.exp"
|
61
|
+
eval "$TEST_CMD" 2>&1
|
62
|
+
ERROR=0
|
63
|
+
TEST_DIFF=$(diff -u "$OUTPUT_FILE" expected_output)
|
64
|
+
|
65
|
+
# ${#} is character length, test that there was no output from diff
|
66
|
+
if [ ${#TEST_DIFF} -eq 0 ]; then
|
67
|
+
ERROR=0
|
68
|
+
else
|
69
|
+
ERROR=1
|
70
|
+
fi
|
71
|
+
}
|
72
|
+
|
73
|
+
unattended_test() {
|
74
|
+
OUTPUT_FILE=$(mktemp)
|
75
|
+
TEST_CMD="$RECKON_CMD -o $OUTPUT_FILE $(cat test_args)"
|
76
|
+
eval "$TEST_CMD" 2>&1
|
77
|
+
ERROR=0
|
78
|
+
|
79
|
+
compare_output "$OUTPUT_FILE"
|
80
|
+
}
|
81
|
+
|
46
82
|
test_fail () {
|
47
83
|
STATUS=$?
|
48
84
|
if [[ $STATUS -ne 0 ]]; then
|
@@ -51,11 +87,42 @@ test_fail () {
|
|
51
87
|
fi
|
52
88
|
}
|
53
89
|
|
90
|
+
compare_output () {
|
91
|
+
OUTPUT_FILE=$1
|
92
|
+
pwd
|
93
|
+
if [[ -e compare_cmds ]]; then
|
94
|
+
COMPARE_CMDS=$(cat compare_cmds)
|
95
|
+
else
|
96
|
+
COMPARE_CMDS=$'ledger\nhledger'
|
97
|
+
fi
|
98
|
+
|
99
|
+
ERROR=1
|
100
|
+
while IFS= read -r n; do
|
101
|
+
if compare_output_for "$OUTPUT_FILE" "$n"; then
|
102
|
+
ERROR=0
|
103
|
+
else
|
104
|
+
ERROR=1
|
105
|
+
break
|
106
|
+
fi
|
107
|
+
done <<< "$COMPARE_CMDS"
|
108
|
+
}
|
109
|
+
|
54
110
|
compare_output_for () {
|
55
111
|
OUTPUT_FILE=$1
|
56
112
|
LEDGER=$2
|
57
113
|
|
58
|
-
|
114
|
+
EXPECTED_FILE=$(mktemp)
|
115
|
+
ACTUAL_FILE=$(mktemp)
|
116
|
+
|
117
|
+
EXPECTED_CMD="$LEDGER -f output.ledger r >$EXPECTED_FILE"
|
118
|
+
echo "$EXPECTED_CMD"
|
119
|
+
eval "$EXPECTED_CMD" || return 1
|
120
|
+
|
121
|
+
ACTUAL_CMD="$LEDGER -f \"$OUTPUT_FILE\" r"
|
122
|
+
echo "running $ACTUAL_CMD"
|
123
|
+
eval $ACTUAL_CMD >$ACTUAL_FILE || return 1
|
124
|
+
|
125
|
+
TEST_DIFF=$(diff -u "$EXPECTED_FILE" "$ACTUAL_FILE")
|
59
126
|
|
60
127
|
# ${#} is character length, test that there was no output from diff
|
61
128
|
if [ ${#TEST_DIFF} -eq 0 ]; then
|
@@ -65,19 +132,4 @@ compare_output_for () {
|
|
65
132
|
fi
|
66
133
|
}
|
67
134
|
|
68
|
-
compare_output () {
|
69
|
-
OUTPUT_FILE=$1
|
70
|
-
|
71
|
-
for n in {ledger,hledger}; do
|
72
|
-
echo -n " - $n..."
|
73
|
-
if compare_output_for "$OUTPUT_FILE" "$n"; then
|
74
|
-
echo "SUCCESS!"
|
75
|
-
else
|
76
|
-
echo "FAILED!"
|
77
|
-
ERROR=1
|
78
|
-
return 0
|
79
|
-
fi
|
80
|
-
done
|
81
|
-
}
|
82
|
-
|
83
135
|
main "$@"
|
data/spec/reckon/app_spec.rb
CHANGED
@@ -16,7 +16,7 @@ describe Reckon::App do
|
|
16
16
|
describe "each_row_backwards" do
|
17
17
|
it "should return rows with hashes" do
|
18
18
|
@rows[0][:pretty_date].should == "2009-12-10"
|
19
|
-
@rows[0][:pretty_money].should == " $
|
19
|
+
@rows[0][:pretty_money].should == " $2,105.00"
|
20
20
|
@rows[0][:description].should == "CREDIT; Some Company vendorpymt PPD ID: 5KL3832735"
|
21
21
|
@rows[1][:pretty_date].should == "2009-12-11"
|
22
22
|
@rows[1][:pretty_money].should == " $116.22"
|
@@ -242,7 +242,7 @@ describe Reckon::CSVParser do
|
|
242
242
|
describe "pretty_money_for" do
|
243
243
|
it "work with negative and positive numbers" do
|
244
244
|
some_other_bank.pretty_money_for(1).should == "-$20.00"
|
245
|
-
some_other_bank.pretty_money_for(4).should == " $
|
245
|
+
some_other_bank.pretty_money_for(4).should == " $1,558.52"
|
246
246
|
some_other_bank.pretty_money_for(7).should == "-$116.22"
|
247
247
|
some_other_bank.pretty_money_for(5).should == " $0.23"
|
248
248
|
some_other_bank.pretty_money_for(6).should == "-$0.96"
|
@@ -251,7 +251,7 @@ describe Reckon::CSVParser do
|
|
251
251
|
it "work with other currencies such as €" do
|
252
252
|
euro_bank = Reckon::CSVParser.new(file: fixture_path('some_other.csv'), currency: "€", suffixed: false )
|
253
253
|
euro_bank.pretty_money_for(1).should == "-€20.00"
|
254
|
-
euro_bank.pretty_money_for(4).should == " €
|
254
|
+
euro_bank.pretty_money_for(4).should == " €1,558.52"
|
255
255
|
euro_bank.pretty_money_for(7).should == "-€116.22"
|
256
256
|
euro_bank.pretty_money_for(5).should == " €0.23"
|
257
257
|
euro_bank.pretty_money_for(6).should == "-€0.96"
|
@@ -260,7 +260,7 @@ describe Reckon::CSVParser do
|
|
260
260
|
it "work with suffixed currencies such as SEK" do
|
261
261
|
swedish_bank = Reckon::CSVParser.new(file: fixture_path('some_other.csv'), currency: 'SEK', suffixed: true )
|
262
262
|
swedish_bank.pretty_money_for(1).should == "-20.00 SEK"
|
263
|
-
swedish_bank.pretty_money_for(4).should == "
|
263
|
+
swedish_bank.pretty_money_for(4).should == " 1,558.52 SEK"
|
264
264
|
swedish_bank.pretty_money_for(7).should == "-116.22 SEK"
|
265
265
|
swedish_bank.pretty_money_for(5).should == " 0.23 SEK"
|
266
266
|
swedish_bank.pretty_money_for(6).should == "-0.96 SEK"
|
@@ -38,4 +38,16 @@ describe Reckon::DateColumn do
|
|
38
38
|
.to eq(Date.new(2013, 2, 1))
|
39
39
|
end
|
40
40
|
end
|
41
|
+
|
42
|
+
describe "#pretty_for" do
|
43
|
+
it 'should use ledger_date_format' do
|
44
|
+
expect(Reckon::DateColumn.new(%w[13/02/2013], {ledger_date_format: '%d/%m/%Y'}).pretty_for(0))
|
45
|
+
.to eq('13/02/2013')
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should default to is' do
|
49
|
+
expect(Reckon::DateColumn.new(%w[13/12/2013]).pretty_for(0))
|
50
|
+
.to eq('2013-12-13')
|
51
|
+
end
|
52
|
+
end
|
41
53
|
end
|
data/spec/reckon/money_spec.rb
CHANGED
@@ -32,17 +32,17 @@ describe Reckon::Money do
|
|
32
32
|
describe "pretty" do
|
33
33
|
it "work with negative and positive numbers" do
|
34
34
|
expect(Reckon::Money.new(-20.00).pretty).to eq("-$20.00")
|
35
|
-
expect(Reckon::Money.new(1558.52).pretty).to eq(" $
|
35
|
+
expect(Reckon::Money.new(1558.52).pretty).to eq(" $1,558.52")
|
36
36
|
end
|
37
37
|
|
38
38
|
it "work with other currencies such as €" do
|
39
39
|
expect(Reckon::Money.new(-20.00, currency: "€", suffixed: false).pretty).to eq("-€20.00")
|
40
|
-
expect(Reckon::Money.new(1558.52, currency: "€", suffixed: false).pretty).to eq(" €
|
40
|
+
expect(Reckon::Money.new(1558.52, currency: "€", suffixed: false).pretty).to eq(" €1,558.52")
|
41
41
|
end
|
42
42
|
|
43
43
|
it "work with suffixed currencies such as SEK" do
|
44
44
|
expect(Reckon::Money.new(-20.00, currency: "SEK", suffixed: true).pretty).to eq("-20.00 SEK")
|
45
|
-
expect(Reckon::Money.new(1558.52, currency: "SEK", suffixed: true).pretty).to eq("
|
45
|
+
expect(Reckon::Money.new(1558.52, currency: "SEK", suffixed: true).pretty).to eq(" 1,558.52 SEK")
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
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.8.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:
|
13
|
+
date: 2022-07-02 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rspec
|
@@ -96,6 +96,20 @@ dependencies:
|
|
96
96
|
- - ">="
|
97
97
|
- !ruby/object:Gem::Version
|
98
98
|
version: 1.8.0
|
99
|
+
- !ruby/object:Gem::Dependency
|
100
|
+
name: matrix
|
101
|
+
requirement: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: 0.4.2
|
106
|
+
type: :runtime
|
107
|
+
prerelease: false
|
108
|
+
version_requirements: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 0.4.2
|
99
113
|
description: Reckon automagically converts CSV files for use with the command-line
|
100
114
|
accounting tool Ledger. It also helps you to select the correct accounts associated
|
101
115
|
with the CSV data using Bayesian machine learning.
|
@@ -130,6 +144,7 @@ files:
|
|
130
144
|
- lib/reckon/options.rb
|
131
145
|
- lib/reckon/version.rb
|
132
146
|
- reckon.gemspec
|
147
|
+
- spec/cosine_training_and_test.rb
|
133
148
|
- spec/data_fixtures/51-sample.csv
|
134
149
|
- spec/data_fixtures/51-tokens.yml
|
135
150
|
- spec/data_fixtures/73-sample.csv
|
@@ -162,6 +177,10 @@ files:
|
|
162
177
|
- spec/integration/another_bank_example/input.csv
|
163
178
|
- spec/integration/another_bank_example/output.ledger
|
164
179
|
- spec/integration/another_bank_example/test_args
|
180
|
+
- spec/integration/ask_for_account/cli_input.exp
|
181
|
+
- spec/integration/ask_for_account/expected_output
|
182
|
+
- spec/integration/ask_for_account/input.csv
|
183
|
+
- spec/integration/ask_for_account/test_args
|
165
184
|
- spec/integration/austrian_example/input.csv
|
166
185
|
- spec/integration/austrian_example/output.ledger
|
167
186
|
- spec/integration/austrian_example/test_args
|
@@ -212,6 +231,10 @@ files:
|
|
212
231
|
- spec/integration/inversed_credit_card/input.csv
|
213
232
|
- spec/integration/inversed_credit_card/output.ledger
|
214
233
|
- spec/integration/inversed_credit_card/test_args
|
234
|
+
- spec/integration/ledger_date_format/compare_cmds
|
235
|
+
- spec/integration/ledger_date_format/input.csv
|
236
|
+
- spec/integration/ledger_date_format/output.ledger
|
237
|
+
- spec/integration/ledger_date_format/test_args
|
215
238
|
- spec/integration/nationwide/input.csv
|
216
239
|
- spec/integration/nationwide/output.ledger
|
217
240
|
- spec/integration/nationwide/test_args
|
@@ -273,9 +296,142 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
273
296
|
- !ruby/object:Gem::Version
|
274
297
|
version: '0'
|
275
298
|
requirements: []
|
276
|
-
rubygems_version: 3.
|
299
|
+
rubygems_version: 3.3.7
|
277
300
|
signing_key:
|
278
301
|
specification_version: 4
|
279
302
|
summary: Utility for interactively converting and labeling CSV files for the Ledger
|
280
303
|
accounting tool.
|
281
|
-
test_files:
|
304
|
+
test_files:
|
305
|
+
- spec/cosine_training_and_test.rb
|
306
|
+
- spec/data_fixtures/51-sample.csv
|
307
|
+
- spec/data_fixtures/51-tokens.yml
|
308
|
+
- spec/data_fixtures/73-sample.csv
|
309
|
+
- spec/data_fixtures/73-tokens.yml
|
310
|
+
- spec/data_fixtures/73-transactions.ledger
|
311
|
+
- spec/data_fixtures/85-date-example.csv
|
312
|
+
- spec/data_fixtures/austrian_example.csv
|
313
|
+
- spec/data_fixtures/bom_utf8_file.csv
|
314
|
+
- spec/data_fixtures/broker_canada_example.csv
|
315
|
+
- spec/data_fixtures/chase.csv
|
316
|
+
- spec/data_fixtures/danish_kroner_nordea_example.csv
|
317
|
+
- spec/data_fixtures/english_date_example.csv
|
318
|
+
- spec/data_fixtures/extratofake.csv
|
319
|
+
- spec/data_fixtures/french_example.csv
|
320
|
+
- spec/data_fixtures/german_date_example.csv
|
321
|
+
- spec/data_fixtures/harder_date_example.csv
|
322
|
+
- spec/data_fixtures/ing.csv
|
323
|
+
- spec/data_fixtures/intuit_mint_example.csv
|
324
|
+
- spec/data_fixtures/invalid_header_example.csv
|
325
|
+
- spec/data_fixtures/inversed_credit_card.csv
|
326
|
+
- spec/data_fixtures/nationwide.csv
|
327
|
+
- spec/data_fixtures/simple.csv
|
328
|
+
- spec/data_fixtures/some_other.csv
|
329
|
+
- spec/data_fixtures/spanish_date_example.csv
|
330
|
+
- spec/data_fixtures/suntrust.csv
|
331
|
+
- spec/data_fixtures/test_money_column.csv
|
332
|
+
- spec/data_fixtures/tokens.yaml
|
333
|
+
- spec/data_fixtures/two_money_columns.csv
|
334
|
+
- spec/data_fixtures/yyyymmdd_date_example.csv
|
335
|
+
- spec/integration/another_bank_example/input.csv
|
336
|
+
- spec/integration/another_bank_example/output.ledger
|
337
|
+
- spec/integration/another_bank_example/test_args
|
338
|
+
- spec/integration/ask_for_account/cli_input.exp
|
339
|
+
- spec/integration/ask_for_account/expected_output
|
340
|
+
- spec/integration/ask_for_account/input.csv
|
341
|
+
- spec/integration/ask_for_account/test_args
|
342
|
+
- spec/integration/austrian_example/input.csv
|
343
|
+
- spec/integration/austrian_example/output.ledger
|
344
|
+
- spec/integration/austrian_example/test_args
|
345
|
+
- spec/integration/bom_utf8_file/input.csv
|
346
|
+
- spec/integration/bom_utf8_file/output.ledger
|
347
|
+
- spec/integration/bom_utf8_file/test_args
|
348
|
+
- spec/integration/broker_canada_example/input.csv
|
349
|
+
- spec/integration/broker_canada_example/output.ledger
|
350
|
+
- spec/integration/broker_canada_example/test_args
|
351
|
+
- spec/integration/chase/account_tokens_and_regex/output.ledger
|
352
|
+
- spec/integration/chase/account_tokens_and_regex/test_args
|
353
|
+
- spec/integration/chase/account_tokens_and_regex/tokens.yml
|
354
|
+
- spec/integration/chase/default_account_names/output.ledger
|
355
|
+
- spec/integration/chase/default_account_names/test_args
|
356
|
+
- spec/integration/chase/input.csv
|
357
|
+
- spec/integration/chase/learn_from_existing/learn.ledger
|
358
|
+
- spec/integration/chase/learn_from_existing/output.ledger
|
359
|
+
- spec/integration/chase/learn_from_existing/test_args
|
360
|
+
- spec/integration/chase/simple/output.ledger
|
361
|
+
- spec/integration/chase/simple/test_args
|
362
|
+
- spec/integration/danish_kroner_nordea_example/input.csv
|
363
|
+
- spec/integration/danish_kroner_nordea_example/output.ledger
|
364
|
+
- spec/integration/danish_kroner_nordea_example/test_args
|
365
|
+
- spec/integration/english_date_example/input.csv
|
366
|
+
- spec/integration/english_date_example/output.ledger
|
367
|
+
- spec/integration/english_date_example/test_args
|
368
|
+
- spec/integration/extratofake/input.csv
|
369
|
+
- spec/integration/extratofake/output.ledger
|
370
|
+
- spec/integration/extratofake/test_args
|
371
|
+
- spec/integration/french_example/input.csv
|
372
|
+
- spec/integration/french_example/output.ledger
|
373
|
+
- spec/integration/french_example/test_args
|
374
|
+
- spec/integration/german_date_example/input.csv
|
375
|
+
- spec/integration/german_date_example/output.ledger
|
376
|
+
- spec/integration/german_date_example/test_args
|
377
|
+
- spec/integration/harder_date_example/input.csv
|
378
|
+
- spec/integration/harder_date_example/output.ledger
|
379
|
+
- spec/integration/harder_date_example/test_args
|
380
|
+
- spec/integration/ing/input.csv
|
381
|
+
- spec/integration/ing/output.ledger
|
382
|
+
- spec/integration/ing/test_args
|
383
|
+
- spec/integration/intuit_mint_example/input.csv
|
384
|
+
- spec/integration/intuit_mint_example/output.ledger
|
385
|
+
- spec/integration/intuit_mint_example/test_args
|
386
|
+
- spec/integration/invalid_header_example/input.csv
|
387
|
+
- spec/integration/invalid_header_example/output.ledger
|
388
|
+
- spec/integration/invalid_header_example/test_args
|
389
|
+
- spec/integration/inversed_credit_card/input.csv
|
390
|
+
- spec/integration/inversed_credit_card/output.ledger
|
391
|
+
- spec/integration/inversed_credit_card/test_args
|
392
|
+
- spec/integration/ledger_date_format/compare_cmds
|
393
|
+
- spec/integration/ledger_date_format/input.csv
|
394
|
+
- spec/integration/ledger_date_format/output.ledger
|
395
|
+
- spec/integration/ledger_date_format/test_args
|
396
|
+
- spec/integration/nationwide/input.csv
|
397
|
+
- spec/integration/nationwide/output.ledger
|
398
|
+
- spec/integration/nationwide/test_args
|
399
|
+
- spec/integration/regression/issue_51_account_tokens/input.csv
|
400
|
+
- spec/integration/regression/issue_51_account_tokens/output.ledger
|
401
|
+
- spec/integration/regression/issue_51_account_tokens/test_args
|
402
|
+
- spec/integration/regression/issue_51_account_tokens/tokens.yml
|
403
|
+
- spec/integration/regression/issue_64_date_column/input.csv
|
404
|
+
- spec/integration/regression/issue_64_date_column/output.ledger
|
405
|
+
- spec/integration/regression/issue_64_date_column/test_args
|
406
|
+
- spec/integration/regression/issue_73_account_token_matching/input.csv
|
407
|
+
- spec/integration/regression/issue_73_account_token_matching/output.ledger
|
408
|
+
- spec/integration/regression/issue_73_account_token_matching/test_args
|
409
|
+
- spec/integration/regression/issue_73_account_token_matching/tokens.yml
|
410
|
+
- spec/integration/regression/issue_85_date_example/input.csv
|
411
|
+
- spec/integration/regression/issue_85_date_example/output.ledger
|
412
|
+
- spec/integration/regression/issue_85_date_example/test_args
|
413
|
+
- spec/integration/spanish_date_example/input.csv
|
414
|
+
- spec/integration/spanish_date_example/output.ledger
|
415
|
+
- spec/integration/spanish_date_example/test_args
|
416
|
+
- spec/integration/suntrust/input.csv
|
417
|
+
- spec/integration/suntrust/output.ledger
|
418
|
+
- spec/integration/suntrust/test_args
|
419
|
+
- spec/integration/test.sh
|
420
|
+
- spec/integration/test_money_column/input.csv
|
421
|
+
- spec/integration/test_money_column/output.ledger
|
422
|
+
- spec/integration/test_money_column/test_args
|
423
|
+
- spec/integration/two_money_columns/input.csv
|
424
|
+
- spec/integration/two_money_columns/output.ledger
|
425
|
+
- spec/integration/two_money_columns/test_args
|
426
|
+
- spec/integration/yyyymmdd_date_example/input.csv
|
427
|
+
- spec/integration/yyyymmdd_date_example/output.ledger
|
428
|
+
- spec/integration/yyyymmdd_date_example/test_args
|
429
|
+
- spec/reckon/app_spec.rb
|
430
|
+
- spec/reckon/csv_parser_spec.rb
|
431
|
+
- spec/reckon/date_column_spec.rb
|
432
|
+
- spec/reckon/ledger_parser_spec.rb
|
433
|
+
- spec/reckon/money_column_spec.rb
|
434
|
+
- spec/reckon/money_spec.rb
|
435
|
+
- spec/reckon/options_spec.rb
|
436
|
+
- spec/spec.opts
|
437
|
+
- spec/spec_helper.rb
|