reckon 0.5.4 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/CHANGELOG.md +21 -5
- data/Gemfile.lock +1 -45
- data/README.md +0 -10
- data/lib/reckon.rb +1 -2
- data/lib/reckon/app.rb +137 -68
- data/lib/reckon/version.rb +1 -1
- data/reckon.gemspec +1 -3
- data/spec/reckon/app_spec.rb +2 -2
- data/spec/reckon/ledger_parser_spec.rb +2 -2
- metadata +2 -30
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f25c7df49774d944ebc40a73374ac593ebeac2e0b0a201e0d29f9c5ace2642e9
|
|
4
|
+
data.tar.gz: 27acd960b5210e7e0fb88c50acf35def784411d0b8112f0f0b8e6c4e2f5ab621
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ddd5fe0ef08b2718e8083cdbd9695e936737b9ff6b258808787192496675f9a371c302e9936069019821f1c865a3280488aa4396185f1e0bf6e7d84b78dc5c0a
|
|
7
|
+
data.tar.gz: f611a48dad98a6a4764518dcb2e72969898d6dfbdaf5e06a77f36ab071429ac6f3bd0b0f51200dc8fbaaca913a95519a6bb38e6ec203dc0af61d861802d101f6
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.0.0-p648
|
data/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [0.
|
|
3
|
+
## [0.6.0](https://github.com/cantino/reckon/tree/0.6.0) (2020-09-03)
|
|
4
4
|
|
|
5
|
-
[Full Changelog](https://github.com/cantino/reckon/compare/v0.5.
|
|
5
|
+
[Full Changelog](https://github.com/cantino/reckon/compare/v0.5.4...0.6.0)
|
|
6
|
+
|
|
7
|
+
**Fixed bugs:**
|
|
8
|
+
|
|
9
|
+
- \[BUG\] Reckon appears not to be parsing ISO standard date yyyy-mm-dd? [\#85](https://github.com/cantino/reckon/issues/85)
|
|
10
|
+
|
|
11
|
+
**Closed issues:**
|
|
12
|
+
|
|
13
|
+
- duplicate detection [\#16](https://github.com/cantino/reckon/issues/16)
|
|
14
|
+
|
|
15
|
+
**Merged pull requests:**
|
|
16
|
+
|
|
17
|
+
- Add ability to add note to transaction when entering it [\#89](https://github.com/cantino/reckon/pull/89) ([benprew](https://github.com/benprew))
|
|
18
|
+
|
|
19
|
+
## [v0.5.4](https://github.com/cantino/reckon/tree/v0.5.4) (2020-06-05)
|
|
20
|
+
|
|
21
|
+
[Full Changelog](https://github.com/cantino/reckon/compare/v0.5.3...v0.5.4)
|
|
6
22
|
|
|
7
23
|
**Fixed bugs:**
|
|
8
24
|
|
|
@@ -36,7 +52,7 @@
|
|
|
36
52
|
|
|
37
53
|
**Merged pull requests:**
|
|
38
54
|
|
|
39
|
-
-
|
|
55
|
+
- guard against rows that don't parse dates [\#82](https://github.com/cantino/reckon/pull/82) ([benprew](https://github.com/benprew))
|
|
40
56
|
|
|
41
57
|
## [v0.5.0](https://github.com/cantino/reckon/tree/v0.5.0) (2020-02-19)
|
|
42
58
|
|
|
@@ -63,7 +79,7 @@
|
|
|
63
79
|
|
|
64
80
|
**Merged pull requests:**
|
|
65
81
|
|
|
66
|
-
-
|
|
82
|
+
- Fix bugs in ledger file parsing. Fixes \#56. [\#81](https://github.com/cantino/reckon/pull/81) ([benprew](https://github.com/benprew))
|
|
67
83
|
- Better file encoding suggestions [\#80](https://github.com/cantino/reckon/pull/80) ([benprew](https://github.com/benprew))
|
|
68
84
|
- :bug: fix matching algorithm, add logging and a spec helper. Fixes \#73 [\#79](https://github.com/cantino/reckon/pull/79) ([benprew](https://github.com/benprew))
|
|
69
85
|
- bug: invalid header lines should be ignored, not parsed. [\#78](https://github.com/cantino/reckon/pull/78) ([benprew](https://github.com/benprew))
|
|
@@ -182,7 +198,6 @@
|
|
|
182
198
|
|
|
183
199
|
**Merged pull requests:**
|
|
184
200
|
|
|
185
|
-
- Updated the sources to allow for custom curreny [\#11](https://github.com/cantino/reckon/pull/11) ([ghost](https://github.com/ghost))
|
|
186
201
|
- Add --account option on the commandline [\#10](https://github.com/cantino/reckon/pull/10) ([copiousfreetime](https://github.com/copiousfreetime))
|
|
187
202
|
|
|
188
203
|
## [v0.3.6](https://github.com/cantino/reckon/tree/v0.3.6) (2013-04-30)
|
|
@@ -216,6 +231,7 @@
|
|
|
216
231
|
|
|
217
232
|
**Merged pull requests:**
|
|
218
233
|
|
|
234
|
+
- Updated the sources to allow for custom curreny [\#11](https://github.com/cantino/reckon/pull/11) ([ghost](https://github.com/ghost))
|
|
219
235
|
- adds support for Nordea csv files [\#1](https://github.com/cantino/reckon/pull/1) ([x2q](https://github.com/x2q))
|
|
220
236
|
|
|
221
237
|
## [v0.3.3](https://github.com/cantino/reckon/tree/v0.3.3) (2013-01-13)
|
data/Gemfile.lock
CHANGED
|
@@ -1,58 +1,25 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
reckon (0.
|
|
4
|
+
reckon (0.6.0)
|
|
5
5
|
chronic (>= 0.3.0)
|
|
6
6
|
highline (>= 1.5.2)
|
|
7
7
|
rchardet (>= 1.8.0)
|
|
8
|
-
terminal-table (>= 1.4.2)
|
|
9
8
|
|
|
10
9
|
GEM
|
|
11
10
|
remote: http://rubygems.org/
|
|
12
11
|
specs:
|
|
13
|
-
activesupport (6.0.3.1)
|
|
14
|
-
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
15
|
-
i18n (>= 0.7, < 2)
|
|
16
|
-
minitest (~> 5.1)
|
|
17
|
-
tzinfo (~> 1.1)
|
|
18
|
-
zeitwerk (~> 2.2, >= 2.2.2)
|
|
19
|
-
addressable (2.7.0)
|
|
20
|
-
public_suffix (>= 2.0.2, < 5.0)
|
|
21
12
|
chronic (0.10.2)
|
|
22
13
|
coderay (1.1.2)
|
|
23
|
-
concurrent-ruby (1.1.6)
|
|
24
14
|
diff-lcs (1.3)
|
|
25
|
-
faraday (1.0.1)
|
|
26
|
-
multipart-post (>= 1.2, < 3)
|
|
27
|
-
faraday-http-cache (2.2.0)
|
|
28
|
-
faraday (>= 0.8)
|
|
29
|
-
github_changelog_generator (1.15.2)
|
|
30
|
-
activesupport
|
|
31
|
-
faraday-http-cache
|
|
32
|
-
multi_json
|
|
33
|
-
octokit (~> 4.6)
|
|
34
|
-
rainbow (>= 2.2.1)
|
|
35
|
-
rake (>= 10.0)
|
|
36
|
-
retriable (~> 3.0)
|
|
37
15
|
highline (2.0.3)
|
|
38
|
-
i18n (1.8.2)
|
|
39
|
-
concurrent-ruby (~> 1.0)
|
|
40
16
|
method_source (0.9.2)
|
|
41
|
-
minitest (5.14.1)
|
|
42
|
-
multi_json (1.14.1)
|
|
43
|
-
multipart-post (2.1.1)
|
|
44
|
-
octokit (4.18.0)
|
|
45
|
-
faraday (>= 0.9)
|
|
46
|
-
sawyer (~> 0.8.0, >= 0.5.3)
|
|
47
17
|
pry (0.12.2)
|
|
48
18
|
coderay (~> 1.1.0)
|
|
49
19
|
method_source (~> 0.9.0)
|
|
50
|
-
public_suffix (4.0.5)
|
|
51
|
-
rainbow (3.0.0)
|
|
52
20
|
rake (12.3.3)
|
|
53
21
|
rantly (1.2.0)
|
|
54
22
|
rchardet (1.8.0)
|
|
55
|
-
retriable (3.1.2)
|
|
56
23
|
rspec (3.9.0)
|
|
57
24
|
rspec-core (~> 3.9.0)
|
|
58
25
|
rspec-expectations (~> 3.9.0)
|
|
@@ -66,22 +33,11 @@ GEM
|
|
|
66
33
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
67
34
|
rspec-support (~> 3.9.0)
|
|
68
35
|
rspec-support (3.9.2)
|
|
69
|
-
sawyer (0.8.2)
|
|
70
|
-
addressable (>= 2.3.5)
|
|
71
|
-
faraday (> 0.8, < 2.0)
|
|
72
|
-
terminal-table (1.8.0)
|
|
73
|
-
unicode-display_width (~> 1.1, >= 1.1.1)
|
|
74
|
-
thread_safe (0.3.6)
|
|
75
|
-
tzinfo (1.2.7)
|
|
76
|
-
thread_safe (~> 0.1)
|
|
77
|
-
unicode-display_width (1.7.0)
|
|
78
|
-
zeitwerk (2.3.0)
|
|
79
36
|
|
|
80
37
|
PLATFORMS
|
|
81
38
|
ruby
|
|
82
39
|
|
|
83
40
|
DEPENDENCIES
|
|
84
|
-
github_changelog_generator
|
|
85
41
|
pry (>= 0.12.2)
|
|
86
42
|
rake
|
|
87
43
|
rantly (= 1.2.0)
|
data/README.md
CHANGED
|
@@ -114,13 +114,3 @@ You can override them with `--default_outof_account` and `--default_into_account
|
|
|
114
114
|
Copyright (c) 2013 Andrew Cantino. See LICENSE for details.
|
|
115
115
|
|
|
116
116
|
Thanks to @BlackEdder for many contributions!
|
|
117
|
-
|
|
118
|
-
## Building a new version of reckon
|
|
119
|
-
1. Update reckon/version.rb
|
|
120
|
-
2. Run `bundle install` to build updated Gemfile.lock
|
|
121
|
-
3. Run `bundle exec github_changelog_generator -u cantino -p reckon -t $TOKEN --future-release $VERSION`
|
|
122
|
-
3. Commit changes with: 'Release $VERSION'
|
|
123
|
-
4. Tag release `git tag v$VERSION`
|
|
124
|
-
5. `git push && git push --tags`
|
|
125
|
-
6. Build new gem `gem build reckon.gemspec`
|
|
126
|
-
7. Push new gem `gem push <gemname>`
|
data/lib/reckon.rb
CHANGED
data/lib/reckon/app.rb
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
|
+
|
|
2
3
|
require 'pp'
|
|
3
4
|
require 'yaml'
|
|
4
5
|
|
|
5
6
|
module Reckon
|
|
6
7
|
class App
|
|
7
8
|
attr_accessor :options, :seen, :csv_parser, :regexps, :matcher
|
|
9
|
+
@@cli = HighLine.new
|
|
8
10
|
|
|
9
11
|
def initialize(options = {})
|
|
10
12
|
LOGGER.level = Logger::INFO if options[:verbose]
|
|
11
13
|
self.options = options
|
|
12
14
|
self.regexps = {}
|
|
13
|
-
self.seen =
|
|
15
|
+
self.seen = Set.new
|
|
14
16
|
self.options[:currency] ||= '$'
|
|
15
|
-
options[:string] = File.read(options[:file]) unless options[:string]
|
|
16
17
|
@csv_parser = CSVParser.new( options )
|
|
17
18
|
@matcher = CosineSimilarity.new(options)
|
|
18
19
|
learn!
|
|
@@ -20,22 +21,19 @@ module Reckon
|
|
|
20
21
|
|
|
21
22
|
def interactive_output(str)
|
|
22
23
|
return if options[:unattended]
|
|
24
|
+
|
|
23
25
|
puts str
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
def learn!
|
|
27
29
|
learn_from_account_tokens(options[:account_tokens_file])
|
|
28
|
-
|
|
29
|
-
ledger_file = options[:existing_ledger_file]
|
|
30
|
-
return unless ledger_file
|
|
31
|
-
fail "#{ledger_file} doesn't exist!" unless File.exists?(ledger_file)
|
|
32
|
-
learn_from(File.read(ledger_file))
|
|
30
|
+
learn_from_ledger_file(options[:existing_ledger_file])
|
|
33
31
|
end
|
|
34
32
|
|
|
35
33
|
def learn_from_account_tokens(filename)
|
|
36
34
|
return unless filename
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
raise "#{filename} doesn't exist!" unless File.exist?(filename)
|
|
39
37
|
|
|
40
38
|
extract_account_tokens(YAML.load_file(filename)).each do |account, tokens|
|
|
41
39
|
tokens.each do |t|
|
|
@@ -48,14 +46,27 @@ module Reckon
|
|
|
48
46
|
end
|
|
49
47
|
end
|
|
50
48
|
|
|
51
|
-
def
|
|
49
|
+
def learn_from_ledger_file(ledger_file)
|
|
50
|
+
return unless ledger_file
|
|
51
|
+
|
|
52
|
+
raise "#{ledger_file} doesn't exist!" unless File.exist?(ledger_file)
|
|
53
|
+
|
|
54
|
+
learn_from_ledger(File.read(ledger_file))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def learn_from_ledger(ledger)
|
|
58
|
+
LOGGER.info "learning from #{ledger}"
|
|
52
59
|
LedgerParser.new(ledger).entries.each do |entry|
|
|
53
60
|
entry[:accounts].each do |account|
|
|
54
61
|
str = [entry[:desc], account[:amount]].join(" ")
|
|
55
|
-
|
|
62
|
+
if account[:name] != options[:bank_account]
|
|
63
|
+
LOGGER.info "adding document #{account[:name]} #{str}"
|
|
64
|
+
@matcher.add_document(account[:name], str)
|
|
65
|
+
end
|
|
56
66
|
pretty_date = entry[:date].iso8601
|
|
57
|
-
|
|
58
|
-
|
|
67
|
+
if account[:name] == options[:bank_account]
|
|
68
|
+
seen << seen_key(pretty_date, @csv_parser.pretty_money(account[:amount]))
|
|
69
|
+
end
|
|
59
70
|
end
|
|
60
71
|
end
|
|
61
72
|
end
|
|
@@ -91,9 +102,10 @@ module Reckon
|
|
|
91
102
|
end
|
|
92
103
|
|
|
93
104
|
def walk_backwards
|
|
105
|
+
cmd_options = "[account]/[q]uit/[s]kip/[n]ote/[d]escription"
|
|
94
106
|
seen_anything_new = false
|
|
95
107
|
each_row_backwards do |row|
|
|
96
|
-
|
|
108
|
+
print_transaction([row])
|
|
97
109
|
|
|
98
110
|
if already_seen?(row)
|
|
99
111
|
interactive_output "NOTE: This row is very similar to a previous one!"
|
|
@@ -105,50 +117,28 @@ module Reckon
|
|
|
105
117
|
seen_anything_new = true
|
|
106
118
|
end
|
|
107
119
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
else
|
|
114
|
-
out_of_account = ask("Which account provided this income? ([account]/[q]uit/[s]kip) ") { |q|
|
|
115
|
-
q.completion = possible_answers
|
|
116
|
-
q.readline = true
|
|
117
|
-
q.default = possible_answers.first
|
|
118
|
-
}
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
finish if out_of_account == "quit" || out_of_account == "q"
|
|
122
|
-
if out_of_account == "skip" || out_of_account == "s"
|
|
123
|
-
interactive_output "Skipping"
|
|
124
|
-
next
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
ledger_format( row,
|
|
128
|
-
[options[:bank_account], row[:pretty_money]],
|
|
129
|
-
[out_of_account, row[:pretty_money_negated]] )
|
|
120
|
+
if row[:money] > 0
|
|
121
|
+
# out_of_account
|
|
122
|
+
answer = ask_account_question("Which account provided this income? (#{cmd_options})", row)
|
|
123
|
+
line1 = [options[:bank_account], row[:pretty_money]]
|
|
124
|
+
line2 = [answer, ""]
|
|
130
125
|
else
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
q.default = possible_answers.first
|
|
138
|
-
}
|
|
139
|
-
end
|
|
140
|
-
finish if into_account == "quit" || into_account == 'q'
|
|
141
|
-
if into_account == "skip" || into_account == 's'
|
|
142
|
-
interactive_output "Skipping"
|
|
143
|
-
next
|
|
144
|
-
end
|
|
126
|
+
# into_account
|
|
127
|
+
answer = ask_account_question("To which account did this money go? (#{cmd_options})", row)
|
|
128
|
+
# line1 = [answer, row[:pretty_money_negated]]
|
|
129
|
+
line1 = [answer, ""]
|
|
130
|
+
line2 = [options[:bank_account], row[:pretty_money]]
|
|
131
|
+
end
|
|
145
132
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
133
|
+
finish if %w[quit q].include?(answer)
|
|
134
|
+
if %w[skip s].include?(answer)
|
|
135
|
+
interactive_output "Skipping"
|
|
136
|
+
next
|
|
149
137
|
end
|
|
150
138
|
|
|
151
|
-
|
|
139
|
+
ledger = ledger_format(row, line1, line2)
|
|
140
|
+
LOGGER.info "ledger line: #{ledger}"
|
|
141
|
+
learn_from_ledger(ledger) unless options[:account_tokens_file]
|
|
152
142
|
output(ledger)
|
|
153
143
|
end
|
|
154
144
|
end
|
|
@@ -167,16 +157,93 @@ module Reckon
|
|
|
167
157
|
:money => @csv_parser.money_for(index),
|
|
168
158
|
:description => @csv_parser.description_for(index) }
|
|
169
159
|
end
|
|
170
|
-
rows.sort_by { |n| n[:date] }.each {|row| yield row }
|
|
160
|
+
rows.sort_by { |n| n[:date] }.each { |row| yield row }
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def print_transaction(rows)
|
|
164
|
+
str = "\n"
|
|
165
|
+
header = %w[Date Amount Description Note]
|
|
166
|
+
maxes = header.map(&:length)
|
|
167
|
+
|
|
168
|
+
rows = rows.map { |r| [r[:pretty_date], r[:pretty_money], r[:description], r[:note]] }
|
|
169
|
+
|
|
170
|
+
rows.each do |r|
|
|
171
|
+
r.length.times { |i| l = r[i] ? r[i].length : 0; maxes[i] = l if maxes[i] < l }
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
header.each_with_index do |n, i|
|
|
175
|
+
str += " #{n.center(maxes[i])} |"
|
|
176
|
+
end
|
|
177
|
+
str += "\n"
|
|
178
|
+
|
|
179
|
+
rows.each do |row|
|
|
180
|
+
row.each_with_index do |_, i|
|
|
181
|
+
just = maxes[i]
|
|
182
|
+
str += sprintf(" %#{just}s |", row[i])
|
|
183
|
+
end
|
|
184
|
+
str += "\n"
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
interactive_output str
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def ask_account_question(msg, row)
|
|
191
|
+
possible_answers = suggest(row)
|
|
192
|
+
LOGGER.info "possible_answers===> #{possible_answers.inspect}"
|
|
193
|
+
|
|
194
|
+
if options[:unattended]
|
|
195
|
+
default = if row[:pretty_money][0] == '-'
|
|
196
|
+
options[:default_into_account] || 'Expenses:Unknown'
|
|
197
|
+
else
|
|
198
|
+
options[:default_outof_account] || 'Income:Unknown'
|
|
199
|
+
end
|
|
200
|
+
return possible_answers[0] || default
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
answer = @@cli.ask(msg) do |q|
|
|
204
|
+
q.completion = possible_answers
|
|
205
|
+
q.readline = true
|
|
206
|
+
q.default = possible_answers.first
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# if answer isn't n/note/d/description, must be an account name, or skip, or quit
|
|
210
|
+
return answer unless %w[n note d description].include?(answer)
|
|
211
|
+
|
|
212
|
+
add_description(row) if %w[d description].include?(answer)
|
|
213
|
+
add_note(row) if %w[n note].include?(answer)
|
|
214
|
+
|
|
215
|
+
print_transaction([row])
|
|
216
|
+
# give user a chance to set account name or retry description
|
|
217
|
+
return ask_account_question(msg, row)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def add_description(row)
|
|
221
|
+
desc_answer = @@cli.ask("Enter a new description for this transaction (empty line aborts)\n") do |q|
|
|
222
|
+
q.overwrite = true
|
|
223
|
+
q.readline = true
|
|
224
|
+
q.default = row[:description]
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
row[:description] = desc_answer unless desc_answer.empty?
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def add_note(row)
|
|
231
|
+
desc_answer = @@cli.ask("Enter a new note for this transaction (empty line aborts)\n") do |q|
|
|
232
|
+
q.overwrite = true
|
|
233
|
+
q.readline = true
|
|
234
|
+
q.default = row[:note]
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
row[:note] = desc_answer unless desc_answer.empty?
|
|
171
238
|
end
|
|
172
239
|
|
|
173
|
-
def most_specific_regexp_match(
|
|
240
|
+
def most_specific_regexp_match(row)
|
|
174
241
|
matches = regexps.map { |regexp, account|
|
|
175
242
|
if match = regexp.match(row[:description])
|
|
176
243
|
[account, match[0]]
|
|
177
244
|
end
|
|
178
245
|
}.compact
|
|
179
|
-
matches.sort_by! { |
|
|
246
|
+
matches.sort_by! { |_account, matched_text| matched_text.length }.map(&:first)
|
|
180
247
|
end
|
|
181
248
|
|
|
182
249
|
def suggest(row)
|
|
@@ -185,9 +252,9 @@ module Reckon
|
|
|
185
252
|
end
|
|
186
253
|
|
|
187
254
|
def ledger_format(row, line1, line2)
|
|
188
|
-
out = "#{row[:pretty_date]}\t#{row[:description]}\n"
|
|
189
|
-
out += "\t#{line1.first}\t\t\t
|
|
190
|
-
out += "\t#{line2.first}\t\t\t
|
|
255
|
+
out = "#{row[:pretty_date]}\t#{row[:description]}\t; #{row[:note]}\n"
|
|
256
|
+
out += "\t#{line1.first}\t\t\t#{line1.last}\n"
|
|
257
|
+
out += "\t#{line2.first}\t\t\t#{line2.last}\n\n"
|
|
191
258
|
out
|
|
192
259
|
end
|
|
193
260
|
|
|
@@ -196,8 +263,12 @@ module Reckon
|
|
|
196
263
|
options[:output_file].flush
|
|
197
264
|
end
|
|
198
265
|
|
|
266
|
+
def seen_key(date, amount)
|
|
267
|
+
return [date, amount].join("|")
|
|
268
|
+
end
|
|
269
|
+
|
|
199
270
|
def already_seen?(row)
|
|
200
|
-
seen
|
|
271
|
+
seen.include?(seen_key(row[:pretty_date], row[:pretty_money]))
|
|
201
272
|
end
|
|
202
273
|
|
|
203
274
|
def finish
|
|
@@ -207,13 +278,11 @@ module Reckon
|
|
|
207
278
|
end
|
|
208
279
|
|
|
209
280
|
def output_table
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
t << [ row[:pretty_date], row[:pretty_money], row[:description] ]
|
|
214
|
-
end
|
|
281
|
+
rows = []
|
|
282
|
+
each_row_backwards do |row|
|
|
283
|
+
rows << row
|
|
215
284
|
end
|
|
216
|
-
|
|
285
|
+
print_transaction(rows)
|
|
217
286
|
end
|
|
218
287
|
|
|
219
288
|
def self.parse_opts(args = ARGV)
|
|
@@ -321,7 +390,7 @@ module Reckon
|
|
|
321
390
|
end
|
|
322
391
|
|
|
323
392
|
unless options[:file]
|
|
324
|
-
options[:file] = ask("What CSV file should I parse? ")
|
|
393
|
+
options[:file] = @@cli.ask("What CSV file should I parse? ")
|
|
325
394
|
unless options[:file].length > 0
|
|
326
395
|
puts "\nYou must provide a CSV file to parse.\n"
|
|
327
396
|
puts parser
|
|
@@ -332,7 +401,7 @@ module Reckon
|
|
|
332
401
|
unless options[:bank_account]
|
|
333
402
|
fail "Please specify --account for the unattended mode" if options[:unattended]
|
|
334
403
|
|
|
335
|
-
options[:bank_account] = ask("What is the account name of this bank account in Ledger? ") do |q|
|
|
404
|
+
options[:bank_account] = @@cli.ask("What is the account name of this bank account in Ledger? ") do |q|
|
|
336
405
|
q.readline = true
|
|
337
406
|
q.validate = /^.{2,}$/
|
|
338
407
|
q.default = "Assets:Bank:Checking"
|
data/lib/reckon/version.rb
CHANGED
data/reckon.gemspec
CHANGED
|
@@ -13,15 +13,13 @@ Gem::Specification.new do |s|
|
|
|
13
13
|
|
|
14
14
|
s.files = `git ls-files`.split("\n")
|
|
15
15
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
16
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
|
17
17
|
s.require_paths = ["lib"]
|
|
18
18
|
|
|
19
19
|
s.add_development_dependency "rspec", ">= 1.2.9"
|
|
20
20
|
s.add_development_dependency "pry", ">= 0.12.2"
|
|
21
21
|
s.add_development_dependency "rantly", "= 1.2.0"
|
|
22
|
-
s.add_development_dependency "github_changelog_generator"
|
|
23
22
|
s.add_runtime_dependency "chronic", ">= 0.3.0"
|
|
24
23
|
s.add_runtime_dependency "highline", ">= 1.5.2"
|
|
25
|
-
s.add_runtime_dependency "terminal-table", ">= 1.4.2"
|
|
26
24
|
s.add_runtime_dependency "rchardet", ">= 1.8.0"
|
|
27
25
|
end
|
data/spec/reckon/app_spec.rb
CHANGED
|
@@ -8,7 +8,7 @@ describe Reckon::App do
|
|
|
8
8
|
context 'with chase csv input' do
|
|
9
9
|
before do
|
|
10
10
|
@chase = Reckon::App.new(string: BANK_CSV)
|
|
11
|
-
@chase.
|
|
11
|
+
@chase.learn_from_ledger(BANK_LEDGER)
|
|
12
12
|
@rows = []
|
|
13
13
|
@chase.each_row_backwards { |row| @rows.push(row) }
|
|
14
14
|
end
|
|
@@ -68,7 +68,7 @@ describe Reckon::App do
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
it 'should learn from a ledger file' do
|
|
71
|
-
chase.
|
|
71
|
+
chase.learn_from_ledger(BANK_LEDGER)
|
|
72
72
|
chase.walk_backwards
|
|
73
73
|
output_file.string.scan('Expenses:Books').count.should == 1
|
|
74
74
|
end
|
|
@@ -58,10 +58,10 @@ describe Reckon::LedgerParser do
|
|
|
58
58
|
headers = %w[date code desc name currency amount type commend]
|
|
59
59
|
safe_s = Shellwords.escape(s)
|
|
60
60
|
|
|
61
|
-
lp_csv = Reckon::LedgerParser.new(s, date_format: '%Y
|
|
61
|
+
lp_csv = Reckon::LedgerParser.new(s, date_format: '%Y/%m/%d').to_csv.join("\n")
|
|
62
62
|
actual = CSV.parse(lp_csv, headers: headers).map(&filter_format)
|
|
63
63
|
|
|
64
|
-
ledger_csv = `echo #{safe_s} | ledger csv --date-format '%Y
|
|
64
|
+
ledger_csv = `echo #{safe_s} | ledger csv --date-format '%Y/%m/%d' -f - `
|
|
65
65
|
expected = CSV.parse(ledger_csv.gsub('\"', '""'), headers: headers).map(&filter_format)
|
|
66
66
|
expected.length.times do |i|
|
|
67
67
|
expect(actual[i]).to eq(expected[i])
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: reckon
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Cantino
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2020-
|
|
13
|
+
date: 2020-09-04 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: rspec
|
|
@@ -54,20 +54,6 @@ dependencies:
|
|
|
54
54
|
- - '='
|
|
55
55
|
- !ruby/object:Gem::Version
|
|
56
56
|
version: 1.2.0
|
|
57
|
-
- !ruby/object:Gem::Dependency
|
|
58
|
-
name: github_changelog_generator
|
|
59
|
-
requirement: !ruby/object:Gem::Requirement
|
|
60
|
-
requirements:
|
|
61
|
-
- - ">="
|
|
62
|
-
- !ruby/object:Gem::Version
|
|
63
|
-
version: '0'
|
|
64
|
-
type: :development
|
|
65
|
-
prerelease: false
|
|
66
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
67
|
-
requirements:
|
|
68
|
-
- - ">="
|
|
69
|
-
- !ruby/object:Gem::Version
|
|
70
|
-
version: '0'
|
|
71
57
|
- !ruby/object:Gem::Dependency
|
|
72
58
|
name: chronic
|
|
73
59
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -96,20 +82,6 @@ dependencies:
|
|
|
96
82
|
- - ">="
|
|
97
83
|
- !ruby/object:Gem::Version
|
|
98
84
|
version: 1.5.2
|
|
99
|
-
- !ruby/object:Gem::Dependency
|
|
100
|
-
name: terminal-table
|
|
101
|
-
requirement: !ruby/object:Gem::Requirement
|
|
102
|
-
requirements:
|
|
103
|
-
- - ">="
|
|
104
|
-
- !ruby/object:Gem::Version
|
|
105
|
-
version: 1.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: 1.4.2
|
|
113
85
|
- !ruby/object:Gem::Dependency
|
|
114
86
|
name: rchardet
|
|
115
87
|
requirement: !ruby/object:Gem::Requirement
|