thefox-wallet 0.8.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c7c1ba3617a57f9c4291350ffb2b035cd41c5904
4
+ data.tar.gz: 2cb59e3a489b3ff322bb70a00986fac90f56027e
5
+ SHA512:
6
+ metadata.gz: 0d14831c7cc6199f946266b3b1908f715072c0f6694f430811f8fc12e50039925a6508d1e7cec26e5408b9551ebebb5986988e4823589e68f55d9ac0900170e9
7
+ data.tar.gz: 736ba6899807150cc510348e6a113a0f7ce22ba034a9c614a8fc4ebb03ed5f2960b1aa6c3713e999633ea77babc0422f9b6741a6632e2c3151c09d9fcf0e569d
@@ -0,0 +1,9 @@
1
+ .setup
2
+ CHANGELOG-*.txt
3
+ /bin/test
4
+ /releases/
5
+ /wallet/
6
+ /wallet_test/
7
+ *.csv
8
+ *.txt
9
+ *.yml
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+
2
+ source 'https://rubygems.org'
3
+ gemspec
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ thefox-wallet (0.8.0)
5
+ ArgsParser (~> 1.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ArgsParser (1.0.0)
11
+ coderay (1.1.0)
12
+ method_source (0.8.2)
13
+ minitest (5.8.3)
14
+ pry (0.10.3)
15
+ coderay (~> 1.1.0)
16
+ method_source (~> 0.8.1)
17
+ slop (~> 3.4)
18
+ rake (10.4.2)
19
+ slop (3.6.0)
20
+
21
+ PLATFORMS
22
+ ruby
23
+
24
+ DEPENDENCIES
25
+ bundler (~> 1.10)
26
+ minitest (~> 5.7)
27
+ pry (~> 0.10)
28
+ rake (~> 10.0)
29
+ thefox-wallet!
30
+
31
+ BUNDLED WITH
32
+ 1.10.2
@@ -0,0 +1,4 @@
1
+
2
+ GEM_NAME = thefox-wallet
3
+
4
+ include Makefile.common
@@ -0,0 +1,50 @@
1
+
2
+ # Ruby Common Big
3
+ # 2015-12-07
4
+
5
+ MV = mv -nv
6
+ RM = rm -rf
7
+ MKDIR = mkdir -p
8
+ BUNDLER = bundle
9
+ GEMSPEC_FILE = $(GEM_NAME).gemspec
10
+
11
+ .PHONY: all
12
+ all: setup
13
+
14
+ .PHONY: setup
15
+ setup: .setup
16
+
17
+ .setup:
18
+ $(BUNDLER) install
19
+ touch $@
20
+
21
+ .PHONY: install
22
+ install:
23
+ gem_file=$$(gem build $(GEMSPEC_FILE) | grep 'File:' | tail -1 | awk '{ print $$2 }'); \
24
+ sudo gem install $$gem_file; \
25
+ $(RM) $$gem_file
26
+
27
+ .PHONY: uninstall
28
+ uninstall:
29
+ sudo gem uninstall $(GEM_NAME)
30
+
31
+ .PHONY: update
32
+ update:
33
+ $(BUNDLER) update
34
+
35
+ .PHONY: clean
36
+ clean:
37
+ $(RM) .setup
38
+
39
+ .PHONY: release
40
+ release: | releases
41
+ set -e; \
42
+ gem_file=$$(gem build $(GEMSPEC_FILE) | grep 'File:' | tail -1 | awk '{ print $$2 }'); \
43
+ dst="releases/$$gem_file"; \
44
+ [ ! -f $$dst ]; \
45
+ $(MV) $$gem_file releases; \
46
+ gem push $$dst; \
47
+ echo 'done'
48
+
49
+ releases:
50
+ $(MKDIR) $@
@@ -0,0 +1,38 @@
1
+ # Wallet
2
+
3
+ A spreadsheet likewise Ruby library to track your finances. Using the best data format ever, YAML. It's designed to host the data offline, e.g. in a Git repository.
4
+
5
+ ## Why this project?
6
+
7
+ While conventionally programs like Microsoft Excel or [LibreOffice](https://www.libreoffice.org/) uses [proprietary file formats](https://en.wikipedia.org/wiki/Proprietary_format) and [binary files](https://en.wikipedia.org/wiki/Binary_file) to save your data this script uses [YAML](https://en.wikipedia.org/wiki/YAML). YAML is a human-readable data serialization format. This means you can edit the YAML files on any [operating system](https://en.wikipedia.org/wiki/Operating_system) with any text editor.
8
+
9
+ ## Features
10
+
11
+ - Save revenues/expenses entries monthly.
12
+ - List saved entries.
13
+ - Import CSV files.
14
+ - Export data to CSV.
15
+ - Generate HTML summary.
16
+ - VI/editor import mode. (CSV)
17
+
18
+ ## Install
19
+
20
+ The preferred method of installation is via RubyGems.org:
21
+ <https://rubygems.org/gems/thefox-wallet>
22
+
23
+ gem install thefox-wallet
24
+
25
+ or via `Gemfile`:
26
+
27
+ gem 'thefox-wallet', '~>0.8'
28
+
29
+ Use it in your sources:
30
+
31
+ require 'thefox-wallet'
32
+
33
+ ## License
34
+ Copyright (C) 2015 Christian Mayer <http://fox21.at>
35
+
36
+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
37
+
38
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
@@ -0,0 +1,2 @@
1
+
2
+ require 'bundler/gem_tasks'
@@ -0,0 +1,328 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'tempfile'
4
+ require 'bundler/setup'
5
+ require 'wallet'
6
+ require 'ArgsParser'
7
+
8
+
9
+ parser = ArgsParser.parser
10
+ parser.bind(:wallet, :w, 'Wallet', 'wallet')
11
+ parser.bind(:title, :t, 'Title')
12
+ parser.bind(:date, :d, 'Date', Date.today.to_s)
13
+ parser.bind(:revenue, :r, 'Revenue', 0)
14
+ parser.bind(:expense, :e, 'Expense', 0)
15
+ parser.bind(:category, :c, 'Category')
16
+ parser.bind(:comment, :o, 'Comment')
17
+ parser.bind(:path, :p, 'Path')
18
+
19
+ command, params = parser.parse(ARGV)
20
+ command = command.to_s
21
+
22
+ # puts 'command: ' + command.to_s
23
+ # puts 'params: ' + params.to_s
24
+ # puts
25
+
26
+ # Command Aliases
27
+ if command == 'i'
28
+ command = 'int'
29
+ elsif command == 'l'
30
+ command = 'list'
31
+ elsif command == 'lc'
32
+ command = 'cat'
33
+ end
34
+
35
+ wallet = params[:wallet].to_s
36
+ title = params[:title].to_s
37
+ date = params[:date].to_s
38
+ revenue = params[:revenue].to_s.gsub('.', '').sub(/,/, '.').to_f.round(3).abs
39
+ category = params[:category].to_s
40
+ comment = params[:comment].to_s
41
+ path = params[:path].to_s
42
+ import = params[:i]
43
+
44
+ path_is_temp = false
45
+
46
+ expense = 0.0
47
+ export = false
48
+ if params[:expense].is_a? TrueClass
49
+ export = true
50
+ else
51
+ expense = -params[:expense].to_s.gsub('.', '').sub(/,/, '.').to_f.round(3).abs
52
+ end
53
+
54
+ # puts 'argv len: ' + ARGV.length.to_s
55
+ # puts 'command: ' + '%-10s' % command.class.to_s + ' "' + command.to_s + '"'
56
+ # puts 'wallet: ' + '%-10s' % wallet.class.to_s + ' "' + wallet.to_s + '"'
57
+ # puts 'title: ' + '%-10s' % title.class.to_s + ' "' + title + '"'
58
+ # puts 'date: ' + '%-10s' % date.class.to_s + ' "' + date + '"'
59
+ # puts 'revenue: ' + '%-10s' % revenue.class.to_s + ' "' + revenue.to_s + '"'
60
+ # puts 'expense: ' + '%-10s' % expense.class.to_s + ' "' + expense.to_s + '"'
61
+ # puts 'category: ' + '%-10s' % category.class.to_s + ' "' + category + '"'
62
+ # puts 'comment: ' + '%-10s' % comment.class.to_s + ' "' + comment.to_s + '"'
63
+ # puts 'path: ' + '%-10s' % path.class.to_s + ' "' + path.to_s + '"'
64
+ # puts
65
+
66
+ if command == 'help' || command.length == 0
67
+ script_name = File.basename(__FILE__)
68
+ padding = ' ' * (script_name.length + 8)
69
+
70
+ puts "#{::TheFox::Wallet::NAME} #{::TheFox::Wallet::VERSION} (#{::TheFox::Wallet::DATE})"
71
+ puts 'Usage: ' + script_name + ' COMMAND OPTIONS'
72
+ puts
73
+ puts 'Commands:'
74
+ puts padding + 'help Show this help'
75
+ puts padding + 'add Add a new entry'
76
+ puts padding + 'i|int Add a new entry interactively'
77
+ puts padding + 'l|list List entries'
78
+ puts padding + 'lc|cat List categories'
79
+ puts padding + 'html Generate HTML files'
80
+ puts padding + 'csv Import/Export CSV file'
81
+ puts padding + 'vi Import per VIM editor'
82
+ puts
83
+ puts 'Options:'
84
+ puts padding + '-w|--wallet PATH Optional'
85
+ puts padding + '-t|--title TITLE Required for command "add"'
86
+ puts padding + '-d|--date Optional'
87
+ puts padding + '-r|--revenue Optional. Default: 0'
88
+ puts padding + '-e|--expense Optional. Default: 0'
89
+ puts padding + '-c|--category Optional. Default: default'
90
+ puts padding + '-o|--comment Optional'
91
+ puts padding + '-i|-e Import or Export. Default: -i'
92
+ puts padding + '-p|--path PATH Required for command "csv"'
93
+
94
+ exit 3
95
+ end
96
+
97
+ if command == 'int'
98
+ command = 'add'
99
+
100
+ print 'title: [' + title + '] '
101
+ title_t = STDIN.gets.strip
102
+ if title_t.length > 0
103
+ title = title_t
104
+ end
105
+
106
+ print 'date: [' + date + '] '
107
+ date_t = STDIN.gets.strip
108
+ if date_t.length > 0
109
+ date = date_t
110
+ end
111
+
112
+ print 'revenue: [' + revenue.to_s + '] '
113
+ revenue_t = STDIN.gets.strip
114
+ if revenue_t.length > 0
115
+ revenue = revenue_t.to_s.gsub('.', '').sub(/,/, '.').to_f.round(3).abs
116
+ end
117
+
118
+ print 'expense: [' + expense.to_s + '] '
119
+ expense_t = STDIN.gets.strip
120
+ if expense_t.length > 0
121
+ expense = -expense_t.to_s.gsub('.', '').sub(/,/, '.').to_f.round(3).abs
122
+ end
123
+
124
+ if category.to_s.length == 0
125
+ category = 'default'
126
+ end
127
+ print 'category: [' + category + '] '
128
+ category_t = STDIN.gets.strip
129
+ if category_t.length > 0
130
+ category = category_t
131
+ end
132
+
133
+ print 'comment: [' + comment + '] '
134
+ comment_t = STDIN.gets.strip
135
+ if comment_t.length > 0
136
+ comment = comment_t
137
+ end
138
+
139
+ puts '-' * 20
140
+ elsif command == 'vi'
141
+ vi_file = Tempfile.create('wallet-vi-import', '/tmp')
142
+ vi_file.puts('# This is a comment line.')
143
+ vi_file.puts('# Date,Title,Revenue,Expense,Category,Comment')
144
+ vi_file.puts('# Date,Title,Expense')
145
+ vi_file.close
146
+
147
+ puts 'cwd: ' + Dir.pwd
148
+ puts 'editor: ' + ENV['EDITOR']
149
+ puts 'file: ' + vi_file.path
150
+ sleep 1
151
+
152
+ system(ENV['EDITOR'] + ' ' + vi_file.path)
153
+ system('grep -v "#" ' + vi_file.path + ' | grep -v ^$ > ' + vi_file.path + '.ok')
154
+
155
+ command = 'csv'
156
+ path = vi_file.path + '.ok'
157
+ path_is_temp = true
158
+ import = true
159
+
160
+ File.unlink vi_file.path
161
+ end
162
+
163
+ wallet = TheFox::Wallet::Wallet.new(wallet)
164
+
165
+ if command == 'add'
166
+ if category.to_s.length == 0
167
+ category = 'default'
168
+ end
169
+
170
+ if title == ''
171
+ puts 'ERROR: Option --title is required for command "' + command + '"'
172
+ exit 1
173
+ end
174
+
175
+ puts 'title: ' + title
176
+ puts 'date: ' + Date.parse(date).to_s
177
+ puts 'revenue: ' + revenue.to_s
178
+ puts 'expense: ' + expense.to_s
179
+ puts 'balance: ' + (revenue + expense).to_f.round(3).to_s
180
+ puts 'category: ' + category.to_s
181
+ puts 'comment: ' + comment.to_s
182
+
183
+ wallet.add TheFox::Wallet::Entry.new(title, date, revenue, expense, category, comment)
184
+ elsif command == 'list'
185
+ puts
186
+
187
+ year, month, day = date.scan(/^(\d{2,4}).?(\d{0,2}).?(\d{0,2})/).shift.map{ |x|
188
+ y = x.to_i
189
+ y == 0 ? nil : y
190
+ }
191
+
192
+ entries = wallet.entries(year, month, day, category)
193
+
194
+ entries_l = entries.map{ |day_name, day_items| day_items.count }.inject{ |sum, n| sum + n }.to_s.length
195
+ title_l = entries.map{ |month_item| month_item[1].map{ |day_item| day_item['title'].length } }.flatten.max.to_i
196
+ revenue_l = entries.map{ |month_item| month_item[1].map{ |day_item| (::TheFox::Wallet::NUMBER_FORMAT % day_item['revenue']).length } }.flatten.max.to_i
197
+ expense_l = entries.map{ |month_item| month_item[1].map{ |day_item| (::TheFox::Wallet::NUMBER_FORMAT % day_item['expense']).length } }.flatten.max.to_i
198
+ balance_l = entries.map{ |month_item| month_item[1].map{ |day_item| (::TheFox::Wallet::NUMBER_FORMAT % day_item['balance']).length } }.flatten.max.to_i
199
+ category_l = entries.map{ |month_item| month_item[1].map{ |day_item| day_item['category'].length } }.flatten.max.to_i
200
+ comment_l = entries.map{ |month_item| month_item[1].map{ |day_item| day_item['comment'].length } }.flatten.max.to_i
201
+
202
+ has_category_col = entries.map{ |month_item| month_item[1].map{ |day_item| day_item['category'] } }.flatten.select{ |i| i != 'default' }.count > 0
203
+ has_comment_col = entries.map{ |month_item| month_item[1].map{ |day_item| day_item['comment'] } }.flatten.select{ |i| i != '' }.count > 0
204
+
205
+ if title_l < 6
206
+ title_l = 6
207
+ end
208
+ if title_l > 25
209
+ title_l = 25
210
+ end
211
+
212
+ if revenue_l < 7
213
+ revenue_l = 7
214
+ end
215
+ if expense_l < 7
216
+ expense_l = 7
217
+ end
218
+ if balance_l < 7
219
+ balance_l = 7
220
+ end
221
+ if category_l < 8
222
+ category_l = 8
223
+ end
224
+ if comment_l > 25
225
+ comment_l = 25
226
+ end
227
+
228
+ entries_f = '%' + entries_l.to_s + 's'
229
+ title_f = '%-' + title_l.to_s + 's'
230
+ revenue_f = '%' + revenue_l.to_s + 's'
231
+ expense_f = '%' + expense_l.to_s + 's'
232
+ balance_f = '%' + balance_l.to_s + 's'
233
+ category_f = '%-' + category_l.to_s + 's'
234
+ comment_f = '%-' + comment_l.to_s + 's'
235
+
236
+ header = ''
237
+ header += '#' * entries_l + ' '
238
+ header += 'Date ' + ' ' * 7
239
+ header += title_f % 'Title' + ' '
240
+ header += revenue_f % 'Revenue' + ' '
241
+ header += expense_f % 'Expense' + ' '
242
+ header += balance_f % 'Balance'
243
+ header += ' ' + category_f % 'Category' if has_category_col
244
+ header += ' ' + comment_f % 'Comment' if has_comment_col
245
+
246
+ header_l = header.length
247
+ header.sub!(/ +$/, '')
248
+ puts header
249
+ puts '-' * header_l
250
+
251
+ revenue_total = 0.0
252
+ expense_total = 0.0
253
+ balance_total = 0.0
254
+ previous_date = ''
255
+ entry_no = 0
256
+ entries.sort.each do |day_name, day_items|
257
+ day_items.each do |entry|
258
+ entry_no += 1
259
+
260
+ title = entry['title']
261
+ title = title[0, 22] + '...' if title.length >= 25
262
+
263
+ revenue_total += entry['revenue']
264
+ expense_total += entry['expense']
265
+ balance_total += entry['balance']
266
+
267
+ category = entry['category'] == 'default' ? '' : entry['category']
268
+ has_category = category != ''
269
+
270
+ comment = entry['comment']
271
+ comment = comment[0, 22] + '...' if comment.length >= 25
272
+
273
+ out = ''
274
+ out += entries_f % entry_no
275
+ out += ' ' + '%10s' % (entry['date'] == previous_date ? '' : entry['date'])
276
+ out += ' ' + title_f % title
277
+ out += ' ' + revenue_f % (::TheFox::Wallet::NUMBER_FORMAT % entry['revenue'])
278
+ out += ' ' + expense_f % (::TheFox::Wallet::NUMBER_FORMAT % entry['expense'])
279
+ out += ' ' + balance_f % (::TheFox::Wallet::NUMBER_FORMAT % entry['balance'])
280
+ out += ' ' + category_f % category if has_category_col
281
+ out += ' ' + comment_f % comment if has_comment_col
282
+
283
+ out.sub!(/ +$/, '')
284
+ puts out
285
+
286
+ previous_date = entry['date']
287
+ end
288
+ end
289
+ puts
290
+
291
+ out = ''
292
+ out += ' ' * (12 + entries_l)
293
+ out += ' ' + title_f % 'TOTAL'
294
+ out += ' ' + revenue_f % (::TheFox::Wallet::NUMBER_FORMAT % revenue_total)
295
+ out += ' ' + expense_f % (::TheFox::Wallet::NUMBER_FORMAT % expense_total)
296
+ out += ' ' + balance_f % (::TheFox::Wallet::NUMBER_FORMAT % balance_total)
297
+ puts out
298
+ elsif command == 'cat'
299
+ categories = wallet.categories
300
+ puts 'categories(' + categories.count.to_s + '):'
301
+ categories.each do |category_item|
302
+ puts "\t" + category_item
303
+ end
304
+ elsif command == 'html'
305
+ puts 'generate html to ' + wallet.html_path + ' ...'
306
+ wallet.gen_html
307
+ puts 'generate html done'
308
+ elsif command == 'csv'
309
+ if path == ''
310
+ puts 'ERROR: Option --path is required for command "' + command + '"'
311
+ exit 1
312
+ end
313
+
314
+ if import || !export
315
+ puts 'import csv ' + path + ' ...'
316
+ wallet.import_csv_file path
317
+ puts 'import csv ' + path + ' done'
318
+
319
+ if path_is_temp
320
+ puts 'delete ' + path
321
+ File.unlink(path)
322
+ end
323
+ elsif export
324
+ puts 'export csv ' + path + ' ...'
325
+ wallet.export_csv_file path
326
+ puts 'export csv ' + path + ' done'
327
+ end
328
+ end