mirror of
https://github.com/gwenhael-le-moine/ledgerrb.git
synced 2024-12-26 09:59:18 +01:00
implement memoization
Signed-off-by: Gwenhael Le Moine <gwenhael.le.moine@gmail.com>
This commit is contained in:
parent
657d4dd05e
commit
1f3c0d9e14
1 changed files with 146 additions and 135 deletions
281
lib/ledger.rb
281
lib/ledger.rb
|
@ -4,152 +4,163 @@ require 'csv'
|
||||||
|
|
||||||
# Ruby wrapper module for calling ledger
|
# Ruby wrapper module for calling ledger
|
||||||
module Ledger
|
module Ledger
|
||||||
module_function
|
module_function
|
||||||
|
|
||||||
@binary = 'ledger'
|
@binary = 'ledger'
|
||||||
@file = ENV[ 'LEDGER_FILE' ]
|
@file = ENV[ 'LEDGER_FILE' ]
|
||||||
|
@last_mtime = Pathname.new(@file).mtime
|
||||||
|
|
||||||
def run( options, command = '', command_parameters = '' )
|
@cache = Hash.new
|
||||||
STDERR.puts "#{@binary} -f #{@file} #{options} #{command} #{command_parameters}"
|
|
||||||
`#{@binary} -f #{@file} #{options} #{command} #{command_parameters}`
|
|
||||||
end
|
|
||||||
|
|
||||||
def version
|
def run( options, command = '', command_parameters = '' )
|
||||||
run '--version'
|
command = "#{@binary} -f #{@file} #{options} #{command} #{command_parameters}"
|
||||||
end
|
|
||||||
|
|
||||||
def accounts( depth = 9999 )
|
mtime = Pathname.new(@file).mtime
|
||||||
accounts = run( '', 'accounts' )
|
if @last_mtime < mtime || !@cache.has_key?( command )
|
||||||
.split( "\n" )
|
@last_mtime = mtime
|
||||||
.map do |a|
|
|
||||||
a.split( ':' )
|
|
||||||
.each_slice( depth )
|
|
||||||
.to_a.first
|
|
||||||
end.uniq
|
|
||||||
|
|
||||||
accounts.map(&:length).max.times do |i|
|
@cache[ command ] = `#{command}`
|
||||||
accounts += accounts.map { |acc| acc.first( i ) }
|
end
|
||||||
|
|
||||||
|
@cache[ command ]
|
||||||
end
|
end
|
||||||
|
|
||||||
accounts
|
def version
|
||||||
.uniq
|
run '--version'
|
||||||
.sort
|
|
||||||
.reject { |a| a.empty? }
|
|
||||||
.sort_by { |a| a.length }
|
|
||||||
end
|
|
||||||
|
|
||||||
def dates_salaries( category = 'salaire' )
|
|
||||||
CSV.parse( run( '', 'csv', category ) )
|
|
||||||
.map do |row|
|
|
||||||
Date.parse row[ 0 ]
|
|
||||||
end
|
|
||||||
.uniq
|
|
||||||
end
|
|
||||||
|
|
||||||
def register( period = nil, categories = '' )
|
|
||||||
period = period.nil? ? '' : "-p '#{period}'"
|
|
||||||
|
|
||||||
CSV.parse( run( "--no-revalued --exchange '#{CURRENCY}' #{period}", 'csv', categories ) )
|
|
||||||
.map do |row|
|
|
||||||
{ date: row[ 0 ],
|
|
||||||
payee: row[ 2 ],
|
|
||||||
account: row[ 3 ],
|
|
||||||
amount: row[ 5 ],
|
|
||||||
currency: row[ 4 ] }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def balance( cleared = false, depth = nil, period = nil, categories = '' )
|
|
||||||
period = period.nil? ? '' : "-p '#{period}'"
|
|
||||||
depth = depth.nil? ? '' : "--depth #{depth}"
|
|
||||||
operation = cleared ? 'cleared' : 'balance'
|
|
||||||
|
|
||||||
run( "--flat --no-total --exchange '#{CURRENCY}' #{period} #{depth}", operation, categories )
|
|
||||||
.split( "\n" )
|
|
||||||
.map do |line|
|
|
||||||
line_array = line.split( "#{CURRENCY}" )
|
|
||||||
|
|
||||||
{ account: line_array[ 1 ].strip,
|
|
||||||
amount: line_array[ 0 ].tr( SEPARATOR, '.' ).to_f }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# def int_treefied_balance( node )
|
|
||||||
# return { name: node[:account], size: node[:amount] } unless node[:account].include( ':' )
|
|
||||||
|
|
||||||
# { name: node[:account].split(':').first,
|
|
||||||
# children: int_treefied_balance( ... ) }
|
|
||||||
# end
|
|
||||||
|
|
||||||
# def treefeid_balance( cleared = false, depth = nil, period = nil, categories = '' )
|
|
||||||
# bal = balance( cleared, depth, period, categories )
|
|
||||||
|
|
||||||
|
|
||||||
# end
|
|
||||||
|
|
||||||
def cleared
|
|
||||||
run( "--flat --no-total --exchange '#{CURRENCY}'", 'cleared', 'Assets Equity' )
|
|
||||||
.split( "\n" )
|
|
||||||
.map do |row|
|
|
||||||
fields = row.match( /\s*(\S+ €)\s*(\S+ €)\s*(\S+)\s*(\S+)/ )
|
|
||||||
{ account: fields[ 4 ],
|
|
||||||
amount: { cleared: fields[ 2 ],
|
|
||||||
all: fields[ 1 ] } } unless fields.nil?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def budget( period = nil, categories = '' )
|
|
||||||
period = period.nil? ? '' : "-p '#{period}'"
|
|
||||||
|
|
||||||
budgeted = run( "--flat --no-total --budget --exchange '#{CURRENCY}' #{period}", 'budget', categories )
|
|
||||||
.lines
|
|
||||||
.map do |line|
|
|
||||||
ary = line.split
|
|
||||||
|
|
||||||
{ currency: ary[1],
|
|
||||||
amount: ary[0].tr( SEPARATOR, '.' ).to_f,
|
|
||||||
budget: ary[2].tr( SEPARATOR, '.' ).to_f,
|
|
||||||
percentage: ary.last( 2 ).first.gsub( /%/, '' ).tr( SEPARATOR, '.' ).to_f,
|
|
||||||
account: ary.last }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
unbudgeted_amount = run( "--flat --no-total --unbudgeted -Mn --exchange '#{CURRENCY}' #{period}", 'register', categories )
|
def accounts( depth = 9999 )
|
||||||
.lines
|
accounts = run( '', 'accounts' )
|
||||||
.map do |line|
|
.split( "\n" )
|
||||||
line.split[4].tr( SEPARATOR, '.' ).to_f
|
.map do |a|
|
||||||
end
|
a.split( ':' )
|
||||||
.reduce( :+ )
|
.each_slice( depth )
|
||||||
|
.to_a.first
|
||||||
|
end.uniq
|
||||||
|
|
||||||
budget = budgeted.map { |account| account[:budget] }.reduce( :+ )
|
accounts.map(&:length).max.times do |i|
|
||||||
income = run( "--flat --no-total --unbudgeted -Mn --exchange '#{CURRENCY}' #{period}", 'register', 'Income' )
|
accounts += accounts.map { |acc| acc.first( i ) }
|
||||||
.lines
|
end
|
||||||
.last
|
|
||||||
.split[4]
|
|
||||||
.tr( SEPARATOR, '.' )
|
|
||||||
.to_f * -1
|
|
||||||
disposable_income = income - budget
|
|
||||||
|
|
||||||
budgeted << { currency: CURRENCY,
|
accounts
|
||||||
amount: unbudgeted_amount,
|
.uniq
|
||||||
budget: disposable_income,
|
.sort
|
||||||
percentage: (unbudgeted_amount / disposable_income) * 100,
|
.reject { |a| a.empty? }
|
||||||
account: '(unbudgeted)' }
|
.sort_by { |a| a.length }
|
||||||
end
|
|
||||||
|
|
||||||
def graph_values( period = nil, categories = [ 'Expenses' ] )
|
|
||||||
period = period.nil? ? '' : "-p '#{period}'"
|
|
||||||
|
|
||||||
result = {}
|
|
||||||
categories.each do |category|
|
|
||||||
result[ category ] = CSV
|
|
||||||
.parse( run( "-MAn --exchange '#{CURRENCY}' #{period}", 'csv --no-revalued', category ) )
|
|
||||||
.map do |row|
|
|
||||||
{ date: row[ 0 ],
|
|
||||||
amount: row[ 5 ],
|
|
||||||
currency: row[ 4 ] }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
result
|
def dates_salaries( category = 'salaire' )
|
||||||
end
|
CSV.parse( run( '', 'csv', category ) )
|
||||||
|
.map do |row|
|
||||||
|
Date.parse row[ 0 ]
|
||||||
|
end
|
||||||
|
.uniq
|
||||||
|
end
|
||||||
|
|
||||||
|
def register( period = nil, categories = '' )
|
||||||
|
period = period.nil? ? '' : "-p '#{period}'"
|
||||||
|
|
||||||
|
CSV.parse( run( "--no-revalued --exchange '#{CURRENCY}' #{period}", 'csv', categories ) )
|
||||||
|
.map do |row|
|
||||||
|
{ date: row[ 0 ],
|
||||||
|
payee: row[ 2 ],
|
||||||
|
account: row[ 3 ],
|
||||||
|
amount: row[ 5 ],
|
||||||
|
currency: row[ 4 ] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def balance( cleared = false, depth = nil, period = nil, categories = '' )
|
||||||
|
period = period.nil? ? '' : "-p '#{period}'"
|
||||||
|
depth = depth.nil? ? '' : "--depth #{depth}"
|
||||||
|
operation = cleared ? 'cleared' : 'balance'
|
||||||
|
|
||||||
|
run( "--flat --no-total --exchange '#{CURRENCY}' #{period} #{depth}", operation, categories )
|
||||||
|
.split( "\n" )
|
||||||
|
.map do |line|
|
||||||
|
line_array = line.split( "#{CURRENCY}" )
|
||||||
|
|
||||||
|
{ account: line_array[ 1 ].strip,
|
||||||
|
amount: line_array[ 0 ].tr( SEPARATOR, '.' ).to_f }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# def int_treefied_balance( node )
|
||||||
|
# return { name: node[:account], size: node[:amount] } unless node[:account].include( ':' )
|
||||||
|
|
||||||
|
# { name: node[:account].split(':').first,
|
||||||
|
# children: int_treefied_balance( ... ) }
|
||||||
|
# end
|
||||||
|
|
||||||
|
# def treefeid_balance( cleared = false, depth = nil, period = nil, categories = '' )
|
||||||
|
# bal = balance( cleared, depth, period, categories )
|
||||||
|
|
||||||
|
|
||||||
|
# end
|
||||||
|
|
||||||
|
def cleared
|
||||||
|
run( "--flat --no-total --exchange '#{CURRENCY}'", 'cleared', 'Assets Equity' )
|
||||||
|
.split( "\n" )
|
||||||
|
.map do |row|
|
||||||
|
fields = row.match( /\s*(\S+ €)\s*(\S+ €)\s*(\S+)\s*(\S+)/ )
|
||||||
|
{ account: fields[ 4 ],
|
||||||
|
amount: { cleared: fields[ 2 ],
|
||||||
|
all: fields[ 1 ] } } unless fields.nil?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def budget( period = nil, categories = '' )
|
||||||
|
period = period.nil? ? '' : "-p '#{period}'"
|
||||||
|
|
||||||
|
budgeted = run( "--flat --no-total --budget --exchange '#{CURRENCY}' #{period}", 'budget', categories )
|
||||||
|
.lines
|
||||||
|
.map do |line|
|
||||||
|
ary = line.split
|
||||||
|
|
||||||
|
{ currency: ary[1],
|
||||||
|
amount: ary[0].tr( SEPARATOR, '.' ).to_f,
|
||||||
|
budget: ary[2].tr( SEPARATOR, '.' ).to_f,
|
||||||
|
percentage: ary.last( 2 ).first.gsub( /%/, '' ).tr( SEPARATOR, '.' ).to_f,
|
||||||
|
account: ary.last }
|
||||||
|
end
|
||||||
|
|
||||||
|
unbudgeted_amount = run( "--flat --no-total --unbudgeted -Mn --exchange '#{CURRENCY}' #{period}", 'register', categories )
|
||||||
|
.lines
|
||||||
|
.map do |line|
|
||||||
|
line.split[4].tr( SEPARATOR, '.' ).to_f
|
||||||
|
end
|
||||||
|
.reduce( :+ )
|
||||||
|
|
||||||
|
budget = budgeted.map { |account| account[:budget] }.reduce( :+ )
|
||||||
|
income = run( "--flat --no-total --unbudgeted -Mn --exchange '#{CURRENCY}' #{period}", 'register', 'Income' )
|
||||||
|
.lines
|
||||||
|
.last
|
||||||
|
.split[4]
|
||||||
|
.tr( SEPARATOR, '.' )
|
||||||
|
.to_f * -1
|
||||||
|
disposable_income = income - budget
|
||||||
|
|
||||||
|
budgeted << { currency: CURRENCY,
|
||||||
|
amount: unbudgeted_amount,
|
||||||
|
budget: disposable_income,
|
||||||
|
percentage: (unbudgeted_amount / disposable_income) * 100,
|
||||||
|
account: '(unbudgeted)' }
|
||||||
|
end
|
||||||
|
|
||||||
|
def graph_values( period = nil, categories = [ 'Expenses' ] )
|
||||||
|
period = period.nil? ? '' : "-p '#{period}'"
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
categories.each do |category|
|
||||||
|
result[ category ] = CSV
|
||||||
|
.parse( run( "-MAn --exchange '#{CURRENCY}' #{period}", 'csv --no-revalued', category ) )
|
||||||
|
.map do |row|
|
||||||
|
{ date: row[ 0 ],
|
||||||
|
amount: row[ 5 ],
|
||||||
|
currency: row[ 4 ] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue