budgetcrmod 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2f6c65051be0ec3e7d8da43ff822486d8af50573
4
+ data.tar.gz: 75f5c0816e48e18fe9b13076a1af87e16826f959
5
+ SHA512:
6
+ metadata.gz: 003fe0aab6ef4f244dc6eb8e21346ddec920274077b6cec71d80f40288cdee4d93e7d7104662a75cbc19d6beeba150dcdb37ab7eb62f3ca579662dd7c8764c7c
7
+ data.tar.gz: 62b8ccf7c84d2aba282fa22d4711efd7192f5fda2912afdb5172009adef1e186d0ab78f88256cc33294ae62ba834745f0e9b132abb4878acf78c6256125e3047
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ gem "coderunner", ">= 0.11.0"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "shoulda", ">= 0"
10
+ gem "rdoc", "~> 3.12"
11
+ gem "bundler", "> 1.0.0"
12
+ gem "jeweler", ">= 1.8.4"
13
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Edmund Highcock
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,19 @@
1
+ = budgetcrmod
2
+
3
+ Description goes here.
4
+
5
+ == Contributing to budgetcrmod
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
9
+ * Fork the project.
10
+ * Start a feature/bugfix branch.
11
+ * Commit and push until you are happy with your contribution.
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2013 Edmund Highcock. See LICENSE.txt for
18
+ further details.
19
+
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "budgetcrmod"
18
+ gem.homepage = "http://github.com/edmundhighcock/budgetcrmod"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{A CodeRunner module to help you do your budget}
21
+ gem.description = %Q{A CodeRunner module to help you do your budget}
22
+ gem.email = "edmundhighcock@users.sourceforge.net"
23
+ gem.authors = ["Edmund Highcock"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ #require 'rcov/rcovtask'
36
+ #Rcov::RcovTask.new do |test|
37
+ ##test.libs << 'test'
38
+ ##test.pattern = 'test/**/test_*.rb'
39
+ ##test.verbose = true
40
+ ##test.rcov_opts << '--exclude "gems/*"'
41
+ ##end
42
+
43
+ task :default => :test
44
+
45
+ require 'rdoc/task'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "budgetcrmod #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1 @@
1
+ require 'budgetcrmod/budget.rb'
@@ -0,0 +1,568 @@
1
+ class String
2
+ def latex_escape
3
+ self.gsub(/(?<!\\)([%_&^])/, '\\\\\1')
4
+ end
5
+ end
6
+
7
+ class Date
8
+ attr_accessor :sortsign
9
+ def -@
10
+ #@sortsign ? @sortsign *= -1 : (@sortsign=-1)
11
+ newdate = dup
12
+ newdate.sortsign = -1
13
+ newdate
14
+ end
15
+ alias :oldcompare :<=>
16
+ def <=>(other)
17
+ oldres = oldcompare(other)
18
+ oldres *= -1 if @sortsign==-1 and other.sortsign == -1
19
+ oldres
20
+ end
21
+ end
22
+
23
+ require 'date'
24
+ class CodeRunner
25
+ class Budget < Run
26
+ @code_long = "Budget Calculator"
27
+ @uses_mpi = false
28
+ @naming_pars = []
29
+ @run_info = []
30
+ @variables = [:data_file, :account]
31
+ @excluded_sub_folders = []
32
+ @modlet_required = false
33
+ @defaults_file_name = "budget_defaults.rb"
34
+
35
+ @code_module_folder = folder = File.dirname(File.expand_path(__FILE__)) # i.e. the directory this file is in
36
+
37
+ # ["05/07/2010",
38
+ # "DEB",
39
+ # "308760",
40
+ # "25387560",
41
+ # "ANGELSCD 0655",
42
+ # "",
43
+ # "11.50",
44
+ # "4506.80"]
45
+ #
46
+ @component_results = [:date, :type, :sc, :ac, :description, :debit, :credit, :balance]
47
+ @results = [:data] + @component_results
48
+ def generate_input_file
49
+ FileUtils.cp @data_file.sub(/~/, ENV['HOME']), @directory + '/data.cvs'
50
+ end
51
+ DOUBLE_STRING=/"(?:\\\\|\\"|[^"\\]|\\[^"\\])*"/
52
+ def process_directory_code_specific
53
+ @status=:Complete
54
+ data = File.read('data.cvs')
55
+ data = data.split("\n").map do |line|
56
+ p matches = line.scan(Regexp.new("((?:#{DOUBLE_STRING}|[^,])*)(?:,|$)"))
57
+ matches.flatten
58
+ end
59
+ pp data
60
+ @data = data
61
+ @data.shift
62
+ end
63
+ def print_out_line
64
+ if @is_component
65
+ sprintf("%4d. %10s %3s %6s %8s %-60s %8s %8s %8s", id, *rcp.component_results.map{|res| send(res)})
66
+ else
67
+ #pr = component_runs.sort_by{|r| r.id}
68
+ "#{sprintf("%3d", @id)}. #{sprintf("%-20s", @account)} Start: #{start_date} End: #{end_date} Final Balance: #{final_balance} "
69
+ end
70
+ end
71
+ def date_sorted_component
72
+ @date_sorted_component ||= component_runs.sort_by{|r| r.id}
73
+ end
74
+ def end_date
75
+ date_sorted_component[-1].date
76
+ end
77
+ def start_date
78
+ date_sorted_component[0].date
79
+ end
80
+ def final_balance
81
+ date_sorted_component[-1].balance
82
+ end
83
+ def parameter_transition(run)
84
+ end
85
+ def parameter_string
86
+ ""
87
+ end
88
+
89
+ def generate_component_runs
90
+ @runner.cache[:data] ||= []
91
+ @data.each do |dataset|
92
+ next if @runner.cache[:data].include? dataset and Date.parse(dataset[0]) > Date.parse("1/1/2013")
93
+ component = create_component
94
+ rcp.component_results.each_with_index do |res,index|
95
+ value = dataset[index]
96
+ # ep value
97
+ value = Date.parse value if res == :date
98
+ value = value.to_f if [:credit, :debit, :balance].include? res
99
+ component.set(res, value)
100
+ end
101
+ @runner.cache[:data].push dataset
102
+ #component.account = @account
103
+ end
104
+ end
105
+ def days_ago
106
+ # sprintf("%04d%02d%02d", date.year, date.month, date.day).to_i
107
+ - ((date.to_datetime.to_time.to_i - Time.now.to_i) / 24 / 3600).to_i
108
+ end
109
+ def idate
110
+ date.to_datetime.to_time.to_i
111
+ end
112
+ def ds
113
+ description
114
+ end
115
+
116
+
117
+ def self.sum_future(future_items, end_date)
118
+ sum = future_items.inject(0) do |sum, (name, item)|
119
+ item = [item] unless item.kind_of? Array
120
+ value = item.inject(0) do |value,info|
121
+ value += info[:size] unless (Date.today > info[:date]) or (info[:date] > end_date) # add unless we have already passed that date
122
+ value
123
+ end
124
+ rcp.excluding.include?(name) ? sum : sum + value
125
+ end
126
+ sum
127
+ end
128
+
129
+ def self.budget_expenditure(runner, budget, budget_info, start_date)
130
+ dates = []
131
+ expenditures = []
132
+ budget_items = []
133
+ date = budget_info[:end]||Date.today
134
+ start_date = [(budget_info[:start]||start_date), start_date].max
135
+ expenditure = 0
136
+ items_temp = []
137
+ items = runner.component_run_list.values.find_all{|r| r.budget == budget and r.in_date(budget_info)}
138
+ #ep ['items', items]
139
+ #ep ['budget', budget]
140
+ counter = 0
141
+ if not budget_info[:period]
142
+ dates.push date
143
+ budget_items.push items
144
+ expenditures.push (items.map{|r| r.debit - r.credit}+[0]).sum
145
+ else
146
+
147
+ case budget_info[:period][1]
148
+ when :month
149
+ while date > start_date
150
+ items_temp += items.find_all{|r| r.date == date}
151
+ if date.mday == (budget_info[:monthday] or 1)
152
+ counter +=1
153
+ if counter % budget_info[:period][0] == 0
154
+ expenditure = (items_temp.map{|r| r.debit - r.credit}+[0]).sum
155
+ dates.push date
156
+ expenditures.push expenditure
157
+ budget_items.push items_temp
158
+ items_temp = []
159
+ expenditure = 0
160
+ end
161
+ end
162
+ date-=1
163
+ end
164
+ when :day
165
+ while date > start_date
166
+ items_temp += items.find_all{|r| r.date == date}
167
+ #expenditure += (budget_items[-1].map{|r| r.debit}+[0]).sum
168
+ counter +=1
169
+ if counter % budget_info[:period][0] == 0
170
+ expenditure = (items_temp.map{|r| r.debit - r.credit}+[0]).sum
171
+ dates.push date
172
+ expenditures.push expenditure
173
+ budget_items.push items_temp
174
+ items_temp = []
175
+ expenditure = 0
176
+ end
177
+ date-=1
178
+ end
179
+ end
180
+ end
181
+
182
+ [dates, expenditures, budget_items]
183
+
184
+ end
185
+
186
+ def self.sum_regular(regular_items, end_date)
187
+ today = Date.today
188
+ sum = regular_items.inject(0) do |sum, (name, item)|
189
+ item = [item] unless item.kind_of? Array
190
+ # ep item
191
+ value = item.inject(0) do |value,info|
192
+ finish = (info[:end] and info[:end] < end_date) ? info[:end] : end_date
193
+ #today = (Time.now.to_i / (24.0*3600.0)).round
194
+
195
+ nunits = 0
196
+ counter = info[:period][0] == 1 ? 0 : nil
197
+ unless counter
198
+ date = today
199
+ counter = 0
200
+ case info[:period][1]
201
+ when :month
202
+ while date >= (info[:start] or Date.today)
203
+ counter +=1 if date.mday == (info[:monthday] or 1)
204
+ date -= 1
205
+ end
206
+ when :year
207
+ while date >= (info[:start] or Date.today)
208
+ counter +=1 if date.yday == (info[:yearday] or 1)
209
+ date -= 1
210
+ end
211
+ when :day
212
+ while date > (info[:start] or Date.today)
213
+ counter +=1
214
+ date -= 1
215
+ end
216
+ end
217
+ end
218
+ date = today
219
+ case info[:period][1]
220
+ when :month
221
+ #p date, info
222
+ while date <= finish
223
+ if date.mday == (info[:monthday] or 1)
224
+ nunits += 1 if counter % info[:period][0] == 0
225
+ counter +=1
226
+ end
227
+ date += 1
228
+ end
229
+ when :year
230
+ while date <= finish
231
+ if date.yday == (info[:yearday] or 1)
232
+ nunits += 1 if counter % info[:period][0] == 0
233
+ counter +=1
234
+ end
235
+ date += 1
236
+ end
237
+ when :day
238
+ while date <= finish
239
+ nunits += 1 if counter % info[:period][0] == 0
240
+ counter +=1
241
+ date += 1
242
+ end
243
+ end
244
+
245
+
246
+
247
+
248
+
249
+ #nyears = (finish.year - today.year)
250
+ #nmonths = nyears * 12 + (finish.month - today.month)
251
+ #(puts "Number of Months: #{nmonths}"; @pnmonths=true) unless @pnmonths
252
+ #ndays = nyears * 12 + (finish.yday - today.yday)
253
+ #number = case info[:period][1]
254
+ #when :month
255
+ #(nmonths / info[:period][0]).ceil
256
+ #when :day
257
+ #(ndays / info[:period][0]).ceil
258
+ #end
259
+ #value + number * info[:size]
260
+ #eputs "Regular Expenditure Item: #{sprintf("%10s", name)} -- #{nunits} payments, total #{nunits * info[:size]}"
261
+ value + nunits * info[:size]
262
+
263
+ end
264
+ (rcp.excluding? and rcp.excluding.include?(name)) ? sum : sum + value
265
+ end
266
+ sum
267
+ end
268
+ def self.predictable_component_ids(runner)
269
+ #runner.instance_variable_set(:@component_run_list, {})
270
+ #runner.instance_variable_set(:@component_ids, [])
271
+ #runner.instance_variable_set(:@component_id, -1)
272
+ #runner.run_list.each{|r| r.instance_variable_set(:@component_runs, [])}
273
+ #runner.run_list.values.sort_by{|r| r.id}.each{|r| r.generate_component_runs}
274
+ #runner.save_large_cache
275
+ end
276
+ def self.kit_time_format_x(kit)
277
+ kit.gp.timefmt = %["%s"]
278
+ kit.data.each{|dk| dk.gp.using = "1:2"}
279
+ kit.gp.xdata = "time"
280
+ kit.gp.format = %[x "%d %B %Y"]
281
+ kit.gp.xtics = "rotate by 340 offset 0,-0.5 #{24*3600*14}"
282
+ kit.gp.xtics = "rotate by 340 offset 0,-0.5 "
283
+ end
284
+
285
+ def self.print_budget(options={})
286
+ @excluding = (options[:excluding] or [])
287
+ runner = CodeRunner.fetch_runner(Y: Dir.pwd)
288
+ puts "------------------------------"
289
+ puts " Budget Report"
290
+ puts "------------------------------"
291
+ balance = runner.component_run_list.values.sort_by{|r| [r.date, r.id]}.map{|r| r.balance}[-1]
292
+ puts "Balance: #{balance}"
293
+ end_date = Date.parse("01/10/2011")
294
+ puts "Regular Expenditure: #{total_regular = sum_regular(REGULAR_EXPENDITURE, end_date)}"
295
+ puts "Future Expenditure: #{total_future = sum_future(FUTURE_EXPENDITURE, end_date)}"
296
+ puts "Total Expenditure: #{total_expenditure = total_future + total_regular}"
297
+ puts "Total Income: #{total_incoming = sum_future(FUTURE_INCOME, end_date)}"
298
+ total = balance + total_incoming - total_expenditure
299
+ puts "Weekly Budget: #{total / ((end_date.to_datetime.to_time.to_i - Time.now.to_i) / 3600 / 24 /7)}"
300
+ end
301
+
302
+ def self.budgets_with_averages(runner, budgets, start_date)
303
+ projected_budgets = budgets.dup
304
+ projected_budgets.each{|key,v| projected_budgets[key]=projected_budgets[key].dup}
305
+ projected_budgets.each do |budget, budget_info|
306
+ #budget_info = budgets[budget]
307
+ dates, expenditures, items = budget_expenditure(runner, budget, budget_info, start_date)
308
+ budget_info[:size] = expenditures.mean rescue 0.0
309
+ end
310
+ projected_budgets
311
+ end
312
+
313
+ def self.latex_budget_transfers(runner, budgets, options)
314
+ numdays = options[:days]
315
+ "#{budgets.map{|budget, budget_info|
316
+ dates, expenditures, items = budget_expenditure(runner, budget, budget_info, Date.today - numdays)
317
+ #ep ['budget', budget, dates, expenditures]
318
+ kit = GraphKit.quick_create([dates.map{|d| d.to_time.to_i}, expenditures])
319
+ kit.data.each{|dk| dk.gp.with="boxes"}
320
+ kit.gp.style = "fill solid"
321
+ kit.xlabel = nil
322
+ kit.ylabel = "Expenditure"
323
+ unless options[:transfers]
324
+ kits = budgets_with_averages(runner, {budget => budget_info}, Date.today - numdays).map{|budget, budget_info|
325
+ #ep 'Budget is ', budget
326
+ kit2 = GraphKit.quick_create([
327
+ [dates[0], dates[-1]].map{|d| d.to_time.to_i},
328
+ [budget_info[:size], budget_info[:size]]
329
+ ])
330
+ kit2.data[0].gp.with = 'lp lw 4'
331
+ kit2
332
+ }
333
+ #$debug_gnuplot = true
334
+ #kits.sum.gnuplot
335
+ kit += kits.sum
336
+
337
+ else
338
+ kit.data[0].y.data.map!{|expen| expen*-1.0}
339
+ end
340
+ kit.title = "#{budget} Expenditure with average (Total = #{kit.data[0].y.data.sum})"
341
+ kit_time_format_x(kit)
342
+ #kit.gnuplot
343
+ kit.gnuplot_write("#{budget}.eps")
344
+ "\\begin{center}\\includegraphics[width=4.0in]{#{budget}.eps}\\vspace{1em}\\end{center}"
345
+ }.join("\n\n")
346
+ }"
347
+ end
348
+
349
+ def self.latex_report(options={})
350
+ runner = CodeRunner.fetch_runner(Y: Dir.pwd, h: :component)
351
+ numdays = options[:days]
352
+ # Delete budgets that contain no items
353
+ actual_budgets = BUDGETS.dup
354
+ BUDGETS.keys.each do |budget|
355
+ actual_budgets.delete(budget) if budget_expenditure(runner, budget, BUDGETS[budget], Date.today - numdays)[0].size == 0
356
+ end
357
+ #predictable_component_ids(runner)
358
+ days_ahead = options[:days_ahead]
359
+ runs = runner.component_run_list.values
360
+ indateruns = runs.find_all{|r| r.days_ago < numdays}
361
+ accounts = runs.map{|r| r.account}.uniq
362
+ ep 'Accounts', accounts
363
+ projected_budgets = Hash[actual_budgets.dup.find_all{|k,v| v[:discretionary]}]
364
+ projected_budgets = budgets_with_averages(runner,projected_budgets, Date.today - numdays)
365
+ #projected_budgets.each{|key,v| projected_budgets[key]=projected_budgets[key].dup}
366
+ #projected_budgets.each do |budget, budget_info|
367
+ ##budget_info = budgets[budget]
368
+ #dates, expenditures, items = budget_expenditure(runner, budget, budget_info, Date.today - numdays)
369
+ #budget_info[:size] = expenditures.mean
370
+ #end
371
+ ep 'projected_budgets', projected_budgets
372
+ File.open('report.tex', 'w') do |file|
373
+ file.puts <<EOF
374
+ \\documentclass{article}
375
+ \\usepackage[cm]{fullpage}
376
+ \\usepackage{tabulary}
377
+ \\usepackage{graphicx}
378
+ %\\usepackage{hyperlink}
379
+ \\newcommand\\Tstrut{\\rule{0pt}{2.8ex}}
380
+ \\begin{document}
381
+ \\title{#{numdays}-day Budget Report}
382
+ \\maketitle
383
+ \\tableofcontents
384
+
385
+ \\section{Summary of Accounts}
386
+ #{accounts.map{|acc|
387
+ "\\subsection{#{acc}}
388
+ \\begin{tabulary}{0.8\\textwidth}{ r l}
389
+ Balance & #{runs.find_all{|r| r.account==acc}.sort_by{|r|
390
+ r.date
391
+ }[-1].balance} \\\\
392
+ Expenditure & #{runs.find_all{|r| r.account==acc &&
393
+ r.days_ago < numdays
394
+ }.map{|r|
395
+ r.debit
396
+ }.sum} \\\\
397
+ Income & #{runs.find_all{|r| r.account==acc &&
398
+ r.days_ago < numdays
399
+ }.map{|r| r.credit}.sum} \\\\
400
+ \\end{tabulary}"}.join("\n\n")
401
+ }
402
+
403
+ \\section{Graphs of Recent Balances}
404
+ #{accounts.map{|acc|
405
+ balance = runs.find_all{|r| r.account==acc}.sort_by{|r| r.date }[-1].balance
406
+ accshort = acc.gsub(/\s/, '')
407
+ kit = runner.graphkit(['date.to_time.to_i', 'balance'], {conditions: "account == #{acc.inspect} and days_ago < #{numdays}", sort: 'date'})
408
+ futuredates = (Date.today..Date.today+days_ahead).to_a
409
+ #p ["Regtrans", REGULAR_TRANSFERS.values_at(*REGULAR_TRANSFERS.keys.find_all{|from,to| to == acc})]
410
+
411
+ budgets = Hash[projected_budgets.find_all{|k,v| v[:account] == acc}]
412
+ ep ['budgets', budgets]
413
+ projection = futuredates.map{|date| balance -
414
+ sum_regular(REGULAR_EXPENDITURE[acc], date) +
415
+ sum_regular(REGULAR_INCOME[acc], date) -
416
+ sum_regular(budgets, date) -
417
+ sum_future(FUTURE_EXPENDITURE[acc], date) +
418
+ sum_future(FUTURE_INCOME[acc], date) +
419
+ (REGULAR_TRANSFERS.keys.find_all{|from,to| to == acc}.map{|key|
420
+ #p [acc, 'to', "key", key]
421
+ sum_regular( REGULAR_TRANSFERS[key], date)
422
+ }.sum||0) -
423
+ (REGULAR_TRANSFERS.keys.find_all{|from,to| from == acc}.map{|key|
424
+ #p [acc, 'from',"key", key]
425
+ sum_regular( REGULAR_TRANSFERS[key], date)
426
+ }.sum||0)
427
+ }
428
+ kit2 = GraphKit.quick_create([futuredates.map{|d| d.to_time.to_i}, projection])
429
+ kit += kit2
430
+ kit.title = "Balance for #{acc}"
431
+ kit.xlabel = %['Date' offset 0,-2]
432
+ kit.xlabel = nil
433
+ kit.ylabel = "Balance"
434
+
435
+ kit.data[0].gp.title = 'Previous'
436
+ kit.data[1].gp.title = '0 GBP Discretionary'
437
+ kit.data[1].gp.title = 'Projection'
438
+ kit.data.each{|dk| dk.gp.with = "lp"}
439
+
440
+ kit_time_format_x(kit)
441
+
442
+ (kit).gnuplot_write("#{accshort}_balance.eps", size: "4.0in,3.0in")
443
+ "\\begin{center}\\includegraphics[width=4.0in]{#{accshort}_balance.eps}\\end{center}"
444
+ }.join("\n\n")
445
+ }
446
+
447
+
448
+ \\section{Budget Expenditure}
449
+ #{latex_budget_transfers(runner,actual_budgets, options)}
450
+
451
+ \\section{Transfers}
452
+ #{latex_budget_transfers(runner, TRANSFERS, options.dup.absorb({transfers: true}))}
453
+
454
+
455
+
456
+ \\section{Budget Resolutions}
457
+
458
+ This section sums items from budgets drawn from an alternate account, i.e. it determines how much should be transferred from one account to another as a result of expenditure from a given budget.
459
+
460
+ #{actual_budgets.map{|budget, budget_info|
461
+
462
+ "\\subsection{#{budget} }
463
+ \\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{r l}
464
+ %\\hline
465
+ Account Owed & Amount \\\\
466
+ \\hline
467
+ \\Tstrut
468
+ #{budget_items = indateruns.find_all{|r| r.budget == budget}
469
+ alternate_accounts = budget_items.map{|r| r.account}.uniq - [budget_info[:account]]
470
+ alternate_accounts.map{|acc|
471
+ alternate_items = budget_items.find_all{|r| r.account == acc}
472
+ total = alternate_items.map{|r| r.debit - r.credit}.sum
473
+ "#{acc} & #{total} \\\\"
474
+ }.join("\n\n")
475
+ }
476
+
477
+ \\\\
478
+ \\hline
479
+ \\end{tabulary}
480
+ \\normalsize
481
+ \\vspace{1em}\n\n
482
+
483
+ #{ alternate_accounts.map{|acc|
484
+ alternate_items = budget_items.find_all{|r| r.account == acc}
485
+ alternate_items.pieces((alternate_items.size.to_f/50.to_f).ceil).map{|piece|
486
+ "\\footnotesize\\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{ #{"c " * 4 + " L " + " r " * 3 }}
487
+ #{budget}: & #{budget_info[:account]} & owes & #{acc} &&&&\\\\
488
+ \\hline
489
+
490
+ \\Tstrut
491
+
492
+ #{piece.map{|r|
493
+ ([:id] + rcp.component_results - [:sc]).map{|res|
494
+ r.send(res).to_s.latex_escape
495
+ #rcp.component_results.map{|res| r.send(res).to_s.gsub(/(.{20})/, '\1\\\\\\\\').latex_escape
496
+ }.join(" & ")
497
+ }.join("\\\\\n")
498
+ }
499
+ \\end{tabulary}\\normalsize"}.join("\n\n")
500
+ }.join("\n\n")}
501
+ "
502
+ }.join("\n\n")
503
+ }
504
+
505
+
506
+
507
+ \\section{Budget and Transfer Breakdown}
508
+ #{(TRANSFERS + actual_budgets).map{|budget, budget_info|
509
+ dates, expenditures, budget_items = budget_expenditure(runner, budget, budget_info, Date.today - numdays)
510
+ pp budget, budget_items.map{|items| items.map{|i| i.date.to_s}}
511
+ "\\subsection{#{budget}}" +
512
+ budget_items.zip(dates, expenditures).map{|items, date, expenditure|
513
+ if items.size > 0
514
+ "
515
+ \\footnotesize
516
+ \\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{ #{"c " * 4 + " L " + " r " * 2 }}
517
+ %\\hline
518
+ & #{date.to_s.latex_escape} & & & Total & #{expenditure} & \\\\
519
+ \\hline
520
+ \\Tstrut
521
+ #{items.map{|r|
522
+ ([:id] + rcp.component_results - [:sc, :balance]).map{|res|
523
+ r.send(res).to_s.latex_escape
524
+ }.join(" & ")
525
+ }.join("\\\\\n")
526
+ }
527
+ \\\\
528
+ \\hline
529
+ \\end{tabulary}
530
+ \\normalsize
531
+ \\vspace{1em}\n\n"
532
+ else
533
+ ""
534
+ end
535
+ }.join("\n\n")
536
+ }.join("\n\n")
537
+ }
538
+
539
+
540
+
541
+ \\section{Recent Transactions}
542
+ #{accounts.map{|acc|
543
+ "\\subsection{#{acc}}
544
+ \\footnotesize
545
+ #{all = runs.find_all{|r| r.account == acc and r.days_ago < numdays}.sort_by{|r| [r.date, r.id]}.reverse
546
+ ep ['acc', acc, 'ids', all.map{|r| r.id}, 'size', all.size]
547
+ all.pieces((all.size.to_f/50.to_f).ceil).map{|piece|
548
+ "\\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{ #{"c " * 4 + " L " + " r " * 3 + "l"}}
549
+ #{piece.map{|r|
550
+ ([:id] + rcp.component_results - [:sc] + [:budget]).map{|res| r.send(res).to_s.latex_escape
551
+ #rcp.component_results.map{|res| r.send(res).to_s.gsub(/(.{20})/, '\1\\\\\\\\').latex_escape
552
+ }.join(" & ")
553
+ }.join("\\\\\n")}
554
+ \\end{tabulary}"}.join("\n\n")}"
555
+ }.join("\n\n")}
556
+ \\end{document}
557
+ EOF
558
+
559
+
560
+ end
561
+ system "latex report.tex && latex report.tex"
562
+ end
563
+
564
+ end # class Budget
565
+ end #class CodeRunner
566
+
567
+ p Dir.pwd
568
+ require Dir.pwd + '/local_customisations.rb'
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'budgetcrmod'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestBudgetcrmod < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: budgetcrmod
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Edmund Highcock
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: coderunner
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.11.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.11.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: shoulda
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rdoc
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.0.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: jeweler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 1.8.4
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.8.4
83
+ description: A CodeRunner module to help you do your budget
84
+ email: edmundhighcock@users.sourceforge.net
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files:
88
+ - LICENSE.txt
89
+ - README.rdoc
90
+ files:
91
+ - ".document"
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.rdoc
95
+ - Rakefile
96
+ - VERSION
97
+ - lib/budgetcrmod.rb
98
+ - lib/budgetcrmod/budget.rb
99
+ - lib/budgetcrmod/defaults_files/budget_defaults.rb
100
+ - test/helper.rb
101
+ - test/test_budgetcrmod.rb
102
+ homepage: http://github.com/edmundhighcock/budgetcrmod
103
+ licenses:
104
+ - MIT
105
+ metadata: {}
106
+ post_install_message:
107
+ rdoc_options: []
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ requirements: []
121
+ rubyforge_project:
122
+ rubygems_version: 2.2.2
123
+ signing_key:
124
+ specification_version: 4
125
+ summary: A CodeRunner module to help you do your budget
126
+ test_files: []