ledger_web 1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. data/.gitignore +10 -0
  2. data/LICENSE +7 -0
  3. data/README.md +140 -0
  4. data/Rakefile +11 -0
  5. data/bin/ledger_web +14 -0
  6. data/ledger_web.gemspec +26 -0
  7. data/lib/ledger_web/app.rb +69 -0
  8. data/lib/ledger_web/config.rb +87 -0
  9. data/lib/ledger_web/db/migrate/20111226180900_initial_schema.rb +38 -0
  10. data/lib/ledger_web/db/migrate/20111231132900_add_views.rb +37 -0
  11. data/lib/ledger_web/db.rb +54 -0
  12. data/lib/ledger_web/helpers.rb +62 -0
  13. data/lib/ledger_web/public/bootstrap-dropdown.js +55 -0
  14. data/lib/ledger_web/public/bootstrap.min.css +356 -0
  15. data/lib/ledger_web/public/codemirror/keymap/emacs.js +29 -0
  16. data/lib/ledger_web/public/codemirror/keymap/vim.js +76 -0
  17. data/lib/ledger_web/public/codemirror/lib/codemirror.css +104 -0
  18. data/lib/ledger_web/public/codemirror/lib/codemirror.js +2761 -0
  19. data/lib/ledger_web/public/codemirror/lib/util/dialog.css +23 -0
  20. data/lib/ledger_web/public/codemirror/lib/util/dialog.js +63 -0
  21. data/lib/ledger_web/public/codemirror/lib/util/foldcode.js +66 -0
  22. data/lib/ledger_web/public/codemirror/lib/util/formatting.js +291 -0
  23. data/lib/ledger_web/public/codemirror/lib/util/javascript-hint.js +83 -0
  24. data/lib/ledger_web/public/codemirror/lib/util/overlay.js +51 -0
  25. data/lib/ledger_web/public/codemirror/lib/util/runmode.js +27 -0
  26. data/lib/ledger_web/public/codemirror/lib/util/search.js +114 -0
  27. data/lib/ledger_web/public/codemirror/lib/util/searchcursor.js +117 -0
  28. data/lib/ledger_web/public/codemirror/lib/util/simple-hint.css +16 -0
  29. data/lib/ledger_web/public/codemirror/lib/util/simple-hint.js +66 -0
  30. data/lib/ledger_web/public/codemirror/mode/clike/clike.js +249 -0
  31. data/lib/ledger_web/public/codemirror/mode/clike/index.html +101 -0
  32. data/lib/ledger_web/public/codemirror/mode/clojure/clojure.js +207 -0
  33. data/lib/ledger_web/public/codemirror/mode/clojure/index.html +66 -0
  34. data/lib/ledger_web/public/codemirror/mode/coffeescript/LICENSE +22 -0
  35. data/lib/ledger_web/public/codemirror/mode/coffeescript/coffeescript.js +325 -0
  36. data/lib/ledger_web/public/codemirror/mode/coffeescript/index.html +721 -0
  37. data/lib/ledger_web/public/codemirror/mode/css/css.js +124 -0
  38. data/lib/ledger_web/public/codemirror/mode/css/index.html +55 -0
  39. data/lib/ledger_web/public/codemirror/mode/diff/diff.css +3 -0
  40. data/lib/ledger_web/public/codemirror/mode/diff/diff.js +13 -0
  41. data/lib/ledger_web/public/codemirror/mode/diff/index.html +99 -0
  42. data/lib/ledger_web/public/codemirror/mode/gfm/gfm.js +108 -0
  43. data/lib/ledger_web/public/codemirror/mode/gfm/index.html +47 -0
  44. data/lib/ledger_web/public/codemirror/mode/groovy/groovy.js +210 -0
  45. data/lib/ledger_web/public/codemirror/mode/groovy/index.html +71 -0
  46. data/lib/ledger_web/public/codemirror/mode/haskell/haskell.js +242 -0
  47. data/lib/ledger_web/public/codemirror/mode/haskell/index.html +60 -0
  48. data/lib/ledger_web/public/codemirror/mode/htmlembedded/htmlembedded.js +68 -0
  49. data/lib/ledger_web/public/codemirror/mode/htmlembedded/index.html +49 -0
  50. data/lib/ledger_web/public/codemirror/mode/htmlmixed/htmlmixed.js +83 -0
  51. data/lib/ledger_web/public/codemirror/mode/htmlmixed/index.html +51 -0
  52. data/lib/ledger_web/public/codemirror/mode/javascript/index.html +77 -0
  53. data/lib/ledger_web/public/codemirror/mode/javascript/javascript.js +360 -0
  54. data/lib/ledger_web/public/codemirror/mode/jinja2/index.html +37 -0
  55. data/lib/ledger_web/public/codemirror/mode/jinja2/jinja2.js +42 -0
  56. data/lib/ledger_web/public/codemirror/mode/lua/index.html +72 -0
  57. data/lib/ledger_web/public/codemirror/mode/lua/lua.js +140 -0
  58. data/lib/ledger_web/public/codemirror/mode/markdown/index.html +339 -0
  59. data/lib/ledger_web/public/codemirror/mode/markdown/markdown.js +242 -0
  60. data/lib/ledger_web/public/codemirror/mode/ntriples/index.html +32 -0
  61. data/lib/ledger_web/public/codemirror/mode/ntriples/ntriples.js +172 -0
  62. data/lib/ledger_web/public/codemirror/mode/pascal/LICENSE +7 -0
  63. data/lib/ledger_web/public/codemirror/mode/pascal/index.html +48 -0
  64. data/lib/ledger_web/public/codemirror/mode/pascal/pascal.js +138 -0
  65. data/lib/ledger_web/public/codemirror/mode/perl/LICENSE +19 -0
  66. data/lib/ledger_web/public/codemirror/mode/perl/index.html +62 -0
  67. data/lib/ledger_web/public/codemirror/mode/perl/perl.js +816 -0
  68. data/lib/ledger_web/public/codemirror/mode/php/index.html +48 -0
  69. data/lib/ledger_web/public/codemirror/mode/php/php.js +120 -0
  70. data/lib/ledger_web/public/codemirror/mode/plsql/index.html +62 -0
  71. data/lib/ledger_web/public/codemirror/mode/plsql/plsql.js +217 -0
  72. data/lib/ledger_web/public/codemirror/mode/python/LICENSE.txt +21 -0
  73. data/lib/ledger_web/public/codemirror/mode/python/index.html +122 -0
  74. data/lib/ledger_web/public/codemirror/mode/python/python.js +333 -0
  75. data/lib/ledger_web/public/codemirror/mode/r/LICENSE +24 -0
  76. data/lib/ledger_web/public/codemirror/mode/r/index.html +73 -0
  77. data/lib/ledger_web/public/codemirror/mode/r/r.js +141 -0
  78. data/lib/ledger_web/public/codemirror/mode/rpm/changes/changes.js +19 -0
  79. data/lib/ledger_web/public/codemirror/mode/rpm/changes/index.html +53 -0
  80. data/lib/ledger_web/public/codemirror/mode/rpm/spec/index.html +99 -0
  81. data/lib/ledger_web/public/codemirror/mode/rpm/spec/spec.css +5 -0
  82. data/lib/ledger_web/public/codemirror/mode/rpm/spec/spec.js +66 -0
  83. data/lib/ledger_web/public/codemirror/mode/rst/index.html +525 -0
  84. data/lib/ledger_web/public/codemirror/mode/rst/rst.js +326 -0
  85. data/lib/ledger_web/public/codemirror/mode/ruby/LICENSE +24 -0
  86. data/lib/ledger_web/public/codemirror/mode/ruby/index.html +171 -0
  87. data/lib/ledger_web/public/codemirror/mode/ruby/ruby.js +195 -0
  88. data/lib/ledger_web/public/codemirror/mode/rust/index.html +48 -0
  89. data/lib/ledger_web/public/codemirror/mode/rust/rust.js +411 -0
  90. data/lib/ledger_web/public/codemirror/mode/scheme/index.html +64 -0
  91. data/lib/ledger_web/public/codemirror/mode/scheme/scheme.js +202 -0
  92. data/lib/ledger_web/public/codemirror/mode/smalltalk/index.html +55 -0
  93. data/lib/ledger_web/public/codemirror/mode/smalltalk/smalltalk.js +139 -0
  94. data/lib/ledger_web/public/codemirror/mode/sparql/index.html +40 -0
  95. data/lib/ledger_web/public/codemirror/mode/sparql/sparql.js +143 -0
  96. data/lib/ledger_web/public/codemirror/mode/stex/index.html +95 -0
  97. data/lib/ledger_web/public/codemirror/mode/stex/stex.js +167 -0
  98. data/lib/ledger_web/public/codemirror/mode/tiddlywiki/index.html +183 -0
  99. data/lib/ledger_web/public/codemirror/mode/tiddlywiki/tiddlywiki.css +21 -0
  100. data/lib/ledger_web/public/codemirror/mode/tiddlywiki/tiddlywiki.js +374 -0
  101. data/lib/ledger_web/public/codemirror/mode/velocity/index.html +103 -0
  102. data/lib/ledger_web/public/codemirror/mode/velocity/velocity.js +146 -0
  103. data/lib/ledger_web/public/codemirror/mode/xml/index.html +44 -0
  104. data/lib/ledger_web/public/codemirror/mode/xml/xml.js +252 -0
  105. data/lib/ledger_web/public/codemirror/mode/xmlpure/index.html +59 -0
  106. data/lib/ledger_web/public/codemirror/mode/xmlpure/xmlpure.js +485 -0
  107. data/lib/ledger_web/public/codemirror/mode/yaml/index.html +67 -0
  108. data/lib/ledger_web/public/codemirror/mode/yaml/yaml.js +95 -0
  109. data/lib/ledger_web/public/codemirror/theme/cobalt.css +18 -0
  110. data/lib/ledger_web/public/codemirror/theme/eclipse.css +25 -0
  111. data/lib/ledger_web/public/codemirror/theme/elegant.css +10 -0
  112. data/lib/ledger_web/public/codemirror/theme/monokai.css +28 -0
  113. data/lib/ledger_web/public/codemirror/theme/neat.css +9 -0
  114. data/lib/ledger_web/public/codemirror/theme/night.css +21 -0
  115. data/lib/ledger_web/public/codemirror/theme/rubyblue.css +21 -0
  116. data/lib/ledger_web/public/jquery-1.7.1.min.js +4 -0
  117. data/lib/ledger_web/public/jquery.tablesorter.min.js +4 -0
  118. data/lib/ledger_web/public/ledger.css +14 -0
  119. data/lib/ledger_web/report.rb +187 -0
  120. data/lib/ledger_web/reports/savings_rate.erb +49 -0
  121. data/lib/ledger_web/version.rb +3 -0
  122. data/lib/ledger_web/views/error.erb +5 -0
  123. data/lib/ledger_web/views/help.erb +6 -0
  124. data/lib/ledger_web/views/layout.erb +44 -0
  125. data/lib/ledger_web/views/table.erb +31 -0
  126. data/lib/ledger_web/watcher.rb +37 -0
  127. data/lib/ledger_web.rb +20 -0
  128. metadata +229 -0
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ .bundle
2
+ db/*.sqlite3
3
+ log/*.log
4
+ tmp/
5
+ .sass-cache/
6
+ env.sh
7
+ .DS_Store
8
+ \#*
9
+ .\#*
10
+ *.gem
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2011 Peter Keen <pete@bugsplat.info>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,140 @@
1
+ Ledger Web
2
+ ----------
3
+
4
+ Ledger Web is a web-based, postgresql-backed reporting system for the [Ledger](http://www.ledger-cli.org) command-line accounting system.
5
+ It is intended to be completely flexible, allowing you to write whatever reports you want. Note that Ledger Web requires **PostgreSQL version 9.0 or greater**.
6
+
7
+ To install:
8
+
9
+ $ gem install ledger_web
10
+ $ createdb ledger
11
+
12
+ To run:
13
+
14
+ $ ledger_web
15
+
16
+ From there, open up http://localhost:9090 in your browser and poke around. You'll see a few example reports.
17
+
18
+ #### Configuration
19
+
20
+ Configuring Ledger Web is pretty simple. Create a file at `~/.ledger_web/config.rb` that looks something like this:
21
+
22
+
23
+ LedgerWeb::Config.new do |config|
24
+ config.set :database_url, "postgres://localhost/ledger"
25
+ end
26
+
27
+ `:database_url` should point at your database instance. It doesn't have to be local, but the configured user needs to be able to alter the schema. There are a bunch more settings that you can set:
28
+
29
+ * `:index_report` is the report that Ledger Web will redirect your browser to when you open it up the first time. Defaults to `:help`
30
+ * `:port` is the port that Ledger Web will run on. Defaults to `9090`
31
+ * `:ledger_file` is the file that Ledger Web will read. Defaults to the `LEDGER_FILE` environment variable
32
+ * `:ledger_bin_path` is the path to the ledger binary. Defaults to finding it in the `PATH`
33
+
34
+ #### Writing Reports
35
+
36
+ Reports are just HTML ERB files that live in `~/.ledger_web/reports`. Ledger Web provides a few useful helpers that let you easily define SQL queries. Here's an example report:
37
+
38
+ <% @query = query do %>
39
+ select
40
+ xtn_month,
41
+ account,
42
+ sum(amount)
43
+ from
44
+ ledger
45
+ where
46
+ (account ~ 'Income'
47
+ or account ~ 'Expenses')
48
+ and xtn_date between :from and :to
49
+ group by
50
+ xtn_month,
51
+ account
52
+ <% end %>
53
+ <%= table @query %>
54
+
55
+ The `query` helper takes a block of SQL and returns a `LedgerWeb::Report` instance. It can take a few options:
56
+
57
+ * `:pivot` is the name of a column to pivot the report on.
58
+ * `:pivot_sort_order` says how to order the resulting pivoted columns. Can be `asc` or `desc`. Defaults to `asc`.
59
+
60
+ Ledger Web uses [Twitter Bootstrap](http://twitter.github.com/bootstrap) for formatting, so you can use whatever you want to format your reports from there.
61
+
62
+ The `table` helper takes a query produced by the `query` helper and some options and builds an HTML table. Also, it can take a `:links` option which will linkify values in the table. Here's an example:
63
+
64
+ :links => {"Account" => "/reports/register?account=:1"}
65
+
66
+ This says that every value in the `Account` column will be surrounded with an `<a>` tag pointing at `/reports/register?account=:1`, where `:1` will be replaced by the value in column 1 of that particular row. You can also use `:title` in a link template. It will get replaced with the title of the column that is currently getting linked. In this case, `:title` would get replaced with `Account`.
67
+
68
+ #### Customizing
69
+
70
+ Any ruby files in `~/.ledger_web/lib` will be loaded at startup so you can define your own classes, modules, whatever you want. In addition, you can put [Sequel migrations](http://sequel.rubyforge.org/rdoc/files/doc/migration_rdoc.html) in `~/.ledger_web/migrate` and they'll get applied as necessary at startup.
71
+
72
+ #### Hooks
73
+
74
+ Ledger Web provides several different hooks that get run during the data load process.
75
+
76
+ * `:before_insert_row` gets the Sequel database and the current row immediatley before insertion. Row is to be modified in place.
77
+ * `:after_insert_row` gets the Sequel database and the current row. Row modifications don't matter.
78
+ * `:before_load` gets the Sequel database
79
+ * `:after_load` gets the Sequel database
80
+
81
+ To define a hook, put something like this in your config file:
82
+
83
+ config.add_hook :before_insert_row do |db, row|
84
+ # modify the row in place
85
+ end
86
+
87
+ #### Schema
88
+
89
+ The base table is named `ledger`. Here's the DDL:
90
+
91
+ create table ledger (
92
+ xtn_id integer, -- line number of the first line of the transaction
93
+ xtn_date date, -- date of the transaction
94
+ xtn_month date, -- month pre-extracted from the date
95
+ xtn_year date, -- year pre-extracted from the date
96
+ checknum text, -- check number (code)
97
+ note text, -- payee
98
+ account text, -- account name
99
+ commodity text, -- commodity
100
+ amount number, -- amount
101
+ tags text, -- any tags attached to the transaction
102
+ virtual boolean, -- if the transaction is virutal or not
103
+ cleared boolean -- if the transaction is cleared or not
104
+ )
105
+
106
+ In addition, there's a few predefined views:
107
+
108
+ create view accounts_months as
109
+ with
110
+ _a as (select account from ledger group by account),
111
+ _m as (select xtn_month from ledger group by xtn_month)
112
+ select
113
+ account,
114
+ xtn_month
115
+ from
116
+ _a cross join _m
117
+ ;
118
+
119
+ create view accounts_days as
120
+ with
121
+ _a as (select account from ledger group by account),
122
+ _d as (select xtn_date from ledger group by xtn_date)
123
+ select
124
+ account,
125
+ xtn_date
126
+ from
127
+ _a cross join _d
128
+ ;
129
+
130
+ create view accounts_years as
131
+ with
132
+ _a as (select account from ledger group by account),
133
+ _y as (select xtn_year from ledger group by xtn_year)
134
+ select
135
+ account,
136
+ xtn_year
137
+ from
138
+ _a cross join _y
139
+ ;
140
+
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
2
+ require "ledger_web/version"
3
+ require 'rake'
4
+
5
+ task :build do
6
+ system "gem build ledger_web.gemspec"
7
+ end
8
+
9
+ task :release => :build do
10
+ system "gem push ledger_web-#{LedgerWeb::VERSION}.gem"
11
+ end
data/bin/ledger_web ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+
5
+ begin
6
+ require 'ledger_web'
7
+ rescue LoadError => e
8
+ path = File.expand_path '../../lib', __FILE__
9
+ $:.unshift(path) if File.directory?(path) && !$:.include?(path)
10
+ require 'ledger_web'
11
+ end
12
+
13
+ LedgerWeb::Watcher.run!
14
+ LedgerWeb::Application.run!(:port => CONFIG.get(:port))
@@ -0,0 +1,26 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+ require 'ledger_web/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ledger_web"
7
+ s.version = LedgerWeb::VERSION
8
+ s.date = "2011-12-31"
9
+ s.platform = Gem::Platform::RUBY
10
+ s.authors = ["Pete Keen"]
11
+ s.email = ["pete@bugsplat.info"]
12
+ s.homepage = "https://github.com/peterkeen/ledger-web"
13
+ s.summary = %q{A web-based, sql-backed front-end for the Ledger command-line accounting system}
14
+ s.description = %q{Allows arbitrary reporting on your ledger using easy-to-write SQL queries}
15
+
16
+ s.add_dependency("pg")
17
+ s.add_dependency("sequel")
18
+ s.add_dependency("directory_watcher")
19
+ s.add_dependency("sinatra")
20
+ s.add_dependency("sinatra-session")
21
+
22
+ s.files = `git ls-files`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = ["lib"]
25
+ end
26
+
@@ -0,0 +1,69 @@
1
+ require 'rubygems'
2
+ require 'sinatra/base'
3
+ require 'sinatra/session'
4
+
5
+ module LedgerWeb
6
+ class Application < Sinatra::Base
7
+ register Sinatra::Session
8
+ set :session_secret, CONFIG.get(:session_secret)
9
+ set :session_expire, CONFIG.get(:session_expire)
10
+ set :views, CONFIG.get(:report_directories) + [File.join(File.dirname(__FILE__), 'views')]
11
+ set :reload_templates, true
12
+
13
+ helpers LedgerWeb::Helpers
14
+
15
+ def find_template(views, name, engine, &block)
16
+ Array(views).each { |v| super(v, name, engine, &block) }
17
+ end
18
+
19
+ before do
20
+ if not session?
21
+ session_start!
22
+ today = Date.today
23
+ session[:from] = Date.new(today.year - 1, today.month, today.day)
24
+ session[:to] = today
25
+ end
26
+ Report.session = session
27
+ Report.params = params
28
+
29
+ @reports = find_all_reports
30
+ end
31
+
32
+ post '/update-date-range' do
33
+
34
+ if params[:reset]
35
+ today = Date.today
36
+ session[:from] = Date.new(today.year - 1, today.month, today.day).strftime('%Y/%m/%d')
37
+ session[:to] = today.strftime('%Y/%m/%d')
38
+ else
39
+ session[:from] = Date.strptime(params[:from], '%Y/%m/%d').strftime('%Y/%m/%d')
40
+ session[:to] = Date.strptime(params[:to], '%Y/%m/%d').strftime('%Y/%m/%d')
41
+ end
42
+
43
+ redirect back
44
+ end
45
+
46
+ get '/reports/:name' do
47
+ begin
48
+ erb params[:name].to_sym
49
+ rescue Exception => e
50
+ @error = e
51
+ erb :error
52
+ end
53
+ end
54
+
55
+ get '/' do
56
+ index_report = CONFIG.get :index_report
57
+ if index_report
58
+ redirect "/reports/#{index_report.to_s}"
59
+ else
60
+ redirect '/help'
61
+ end
62
+ end
63
+
64
+ get '/help' do
65
+ erb :help
66
+ end
67
+ end
68
+
69
+ end
@@ -0,0 +1,87 @@
1
+ module LedgerWeb
2
+
3
+ class Config
4
+ attr_reader :vars, :hooks
5
+
6
+ def initialize
7
+ @vars = {}
8
+ @hooks = {}
9
+
10
+ if block_given?
11
+ yield self
12
+ end
13
+ end
14
+
15
+ def set(key, value)
16
+ @vars[key] = value
17
+ end
18
+
19
+ def get(key)
20
+ @vars[key]
21
+ end
22
+
23
+ def add_hook(phase, &block)
24
+ _add_hook(phase, block)
25
+ end
26
+
27
+ def _add_hook(phase, hook)
28
+ @hooks[phase] ||= []
29
+ @hooks[phase] << hook
30
+ end
31
+
32
+ def run_hooks(phase, data)
33
+ if @hooks.has_key? phase
34
+ @hooks[phase].each do |hook|
35
+ hook.call(data)
36
+ end
37
+ return data
38
+ end
39
+ end
40
+
41
+ def override_with(config)
42
+ config.vars.each do |key, value|
43
+ set key, value
44
+ end
45
+
46
+ config.hooks.each do |phase, hooks|
47
+ hooks.each do |hook|
48
+ _add_hook phase, hook
49
+ end
50
+ end
51
+ end
52
+
53
+ def self.from_file(filename)
54
+ File.open(filename) do |file|
55
+ return eval(file.read, nil, filename)
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ CONFIG = LedgerWeb::Config.new do |config|
62
+ config.set :database_url, "postgres://localhost/ledger"
63
+ config.set :port, "9090"
64
+ config.set :ledger_file, ENV['LEDGER_FILE']
65
+ config.set :report_directories, ["#{File.dirname(__FILE__)}/reports"]
66
+ config.set :session_secret, 'SomethingSecretThisWayPassed'
67
+ config.set :session_expire, 60*60
68
+ config.set :watch_interval, 5
69
+ config.set :watch_stable_count, 3
70
+ config.set :ledger_bin_path, "ledger"
71
+
72
+ config.set :ledger_format, "%(quoted(xact.beg_line)),%(quoted(date)),%(quoted(payee)),%(quoted(account)),%(quoted(commodity)),%(quoted(quantity(scrub(display_amount)))),%(quoted(cleared)),%(quoted(virtual)),%(quoted(join(note | xact.note)))\n"
73
+
74
+ ledger_web_dir = "#{ENV['HOME']}/.ledger_web"
75
+
76
+ if File.directory? ledger_web_dir
77
+ if File.directory? "#{ledger_web_dir}/reports"
78
+ dirs = config.get(:report_directories)
79
+ dirs.unshift "#{ledger_web_dir}/reports"
80
+ config.set :report_directories, dirs
81
+ end
82
+
83
+ if File.exists? "#{ledger_web_dir}/config.rb"
84
+ config.override_with(LedgerWeb::Config.from_file("#{ledger_web_dir}/config.rb"))
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,38 @@
1
+ Sequel.migration do
2
+ up do
3
+ create_table(:ledger, :ignore_index_errors=>true) do
4
+ Date :xtn_date
5
+ String :checknum, :text=>true
6
+ String :note, :text=>true
7
+ String :account, :text=>true
8
+ String :commodity, :text=>true
9
+ BigDecimal :amount
10
+ String :tags, :text=>true
11
+ Date :xtn_month
12
+ Date :xtn_year
13
+ TrueClass :virtual
14
+ Integer :xtn_id
15
+ TrueClass :cleared
16
+
17
+ index [:account]
18
+ index [:commodity]
19
+ index [:note]
20
+ index [:tags]
21
+ index [:virtual]
22
+ index [:xtn_date]
23
+ index [:xtn_month]
24
+ index [:xtn_year]
25
+ end
26
+
27
+ create_table(:schema_info) do
28
+ String :filename, :text=>true, :null=>false
29
+
30
+ primary_key [:filename]
31
+ end
32
+ end
33
+
34
+ down do
35
+ drop_table(:ledger, :schema_info)
36
+ end
37
+ end
38
+
@@ -0,0 +1,37 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_or_replace_view(:accounts_days, <<HERE)
4
+ with
5
+ _a as (select account from ledger group by account),
6
+ _d as (select xtn_date from ledger group by xtn_date)
7
+ select
8
+ account,
9
+ xtn_date
10
+ from
11
+ _a cross join _d
12
+ HERE
13
+
14
+ create_or_replace_view(:accounts_months, <<HERE)
15
+ with
16
+ _a as (select account from ledger group by account),
17
+ _m as (select xtn_month from ledger group by xtn_month)
18
+ select
19
+ account,
20
+ xtn_month
21
+ from
22
+ _a cross join _m
23
+ HERE
24
+
25
+ create_or_replace_view(:accounts_years, <<HERE)
26
+ with
27
+ _a as (select account from ledger group by account),
28
+ _y as (select xtn_year from ledger group by xtn_year)
29
+ select
30
+ account,
31
+ xtn_year
32
+ from
33
+ _a cross join _y
34
+ HERE
35
+
36
+ end
37
+ end
@@ -0,0 +1,54 @@
1
+ require 'sequel'
2
+ require 'sequel/extensions/migration'
3
+ require 'csv'
4
+
5
+ DB = Sequel.connect(CONFIG.get(:database_url))
6
+
7
+ Sequel::Migrator.apply(DB, File.join(File.dirname(__FILE__), "db/migrate"))
8
+
9
+ home_migrations = File.join(ENV['HOME'], '.ledger_web', 'migrate')
10
+ if File.directory?(home_migrations)
11
+ Sequel::Migrator.run(DB, home_migrations, :table => "user_schema_changes")
12
+ end
13
+
14
+ def load_database
15
+ ledger_format = CONFIG.get :ledger_format
16
+ ledger_bin_path = CONFIG.get :ledger_bin_path
17
+ ledger_file = CONFIG.get :ledger_file
18
+
19
+ # dump ledger to tempfile
20
+ print " dumping ledger to file...."
21
+ file = Tempfile.new('ledger')
22
+ system "#{ledger_bin_path} -f #{ledger_file} --format='#{ledger_format}' reg > #{file.path}"
23
+ puts "done"
24
+ counter = 0
25
+ DB.transaction do
26
+
27
+ CONFIG.run_hooks(:before_load, DB)
28
+
29
+ print " clearing ledger table...."
30
+ DB["DELETE FROM ledger"].delete
31
+ puts "done"
32
+
33
+ print " loading into database...."
34
+ CSV.foreach(file.path) do |row|
35
+ counter += 1
36
+ row = Hash[*[:xtn_id, :xtn_date, :note, :account, :commodity, :amount, :cleared, :virtual, :tags].zip(row).flatten]
37
+
38
+ xtn_date = Date.strptime(row[:xtn_date], '%Y/%m/%d')
39
+
40
+ row[:xtn_month] = xtn_date.strftime('%Y/%m/01')
41
+ row[:xtn_year] = xtn_date.strftime('%Y/01/01')
42
+
43
+ row = CONFIG.run_hooks(:before_insert_row, row)
44
+ DB[:ledger].insert(row)
45
+ CONFIG.run_hooks(:after_insert_row, row)
46
+ end
47
+
48
+ CONFIG.run_hooks(:after_load, DB)
49
+ end
50
+ print " analyzing ledger table"
51
+ DB.fetch('VACUUM ANALYZE ledger')
52
+ puts "done"
53
+ counter
54
+ end
@@ -0,0 +1,62 @@
1
+ module LedgerWeb
2
+ module Helpers
3
+ def partial (template, locals = {})
4
+ erb(template, :layout => false, :locals => locals)
5
+ end
6
+
7
+ def table(report, options = {})
8
+ links = options[:links] || {}
9
+ partial(:table, :report => report, :links => links)
10
+ end
11
+
12
+ def query(options={}, &block)
13
+ q = erb_with_output_buffer block
14
+ report = LedgerWeb::Report.from_query(q)
15
+ if options[:pivot]
16
+ report = report.pivot(options[:pivot], options[:pivot_sort_order])
17
+ end
18
+ return report
19
+ end
20
+
21
+ def erb_with_output_buffer(buf = '', block)
22
+ @_out_buf, old_buffer = buf, @_out_buf
23
+ block.call
24
+ @_out_buf
25
+ ensure
26
+ @_out_buf = old_buffer
27
+ end
28
+
29
+ def expect(expected)
30
+ not_present = []
31
+ expected.each do |key|
32
+ if not params.has_key? key
33
+ not_present << key
34
+ end
35
+ end
36
+
37
+ if not_present.length > 0
38
+ raise "Missing params: #{not_present.join(', ')}"
39
+ end
40
+ end
41
+
42
+ def linkify(links, row, value, display_value)
43
+ links.each do |key, val|
44
+ if key.is_a? String
45
+ key = /^#{key}$/
46
+ end
47
+
48
+ if key.match(value[1].title.to_s)
49
+ url = String.new(links[key])
50
+ row.each_with_index do |v,i|
51
+ url.gsub!(":#{i}", v[0].to_s)
52
+ end
53
+
54
+ url.gsub!(':title', value[1].title.to_s)
55
+ display_value = "<a href='#{url}'>#{display_value}</a>"
56
+ end
57
+ end
58
+ display_value
59
+ end
60
+ end
61
+ end
62
+
@@ -0,0 +1,55 @@
1
+ /* ============================================================
2
+ * bootstrap-dropdown.js v1.4.0
3
+ * http://twitter.github.com/bootstrap/javascript.html#dropdown
4
+ * ============================================================
5
+ * Copyright 2011 Twitter, Inc.
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ * ============================================================ */
19
+
20
+
21
+ !function( $ ){
22
+
23
+ "use strict"
24
+
25
+ /* DROPDOWN PLUGIN DEFINITION
26
+ * ========================== */
27
+
28
+ $.fn.dropdown = function ( selector ) {
29
+ return this.each(function () {
30
+ $(this).delegate(selector || d, 'click', function (e) {
31
+ var li = $(this).parent('li')
32
+ , isActive = li.hasClass('open')
33
+
34
+ clearMenus()
35
+ !isActive && li.toggleClass('open')
36
+ return false
37
+ })
38
+ })
39
+ }
40
+
41
+ /* APPLY TO STANDARD DROPDOWN ELEMENTS
42
+ * =================================== */
43
+
44
+ var d = 'a.menu, .dropdown-toggle'
45
+
46
+ function clearMenus() {
47
+ $(d).parent('li').removeClass('open')
48
+ }
49
+
50
+ $(function () {
51
+ $('html').bind("click", clearMenus)
52
+ $('body').dropdown( '[data-dropdown] a.menu, [data-dropdown] .dropdown-toggle' )
53
+ })
54
+
55
+ }( window.jQuery || window.ender );