activewarehouse 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/README +41 -0
  2. data/Rakefile +121 -0
  3. data/TODO +4 -0
  4. data/db/migrations/001_create_table_reports.rb +28 -0
  5. data/doc/agg_queries.txt +26 -0
  6. data/doc/agg_queries_results.txt +150 -0
  7. data/doc/queries.txt +35 -0
  8. data/generators/cube/USAGE +1 -0
  9. data/generators/cube/cube_generator.rb +28 -0
  10. data/generators/cube/templates/model.rb +3 -0
  11. data/generators/cube/templates/unit_test.rb +8 -0
  12. data/generators/dimension/USAGE +1 -0
  13. data/generators/dimension/dimension_generator.rb +46 -0
  14. data/generators/dimension/templates/fixture.yml +5 -0
  15. data/generators/dimension/templates/migration.rb +20 -0
  16. data/generators/dimension/templates/model.rb +3 -0
  17. data/generators/dimension/templates/unit_test.rb +10 -0
  18. data/generators/fact/USAGE +1 -0
  19. data/generators/fact/fact_generator.rb +46 -0
  20. data/generators/fact/templates/fixture.yml +5 -0
  21. data/generators/fact/templates/migration.rb +11 -0
  22. data/generators/fact/templates/model.rb +3 -0
  23. data/generators/fact/templates/unit_test.rb +10 -0
  24. data/install.rb +5 -0
  25. data/lib/active_warehouse.rb +65 -0
  26. data/lib/active_warehouse/builder.rb +2 -0
  27. data/lib/active_warehouse/builder/date_dimension_builder.rb +65 -0
  28. data/lib/active_warehouse/builder/random_data_builder.rb +13 -0
  29. data/lib/active_warehouse/core_ext.rb +1 -0
  30. data/lib/active_warehouse/core_ext/time.rb +5 -0
  31. data/lib/active_warehouse/core_ext/time/calculations.rb +40 -0
  32. data/lib/active_warehouse/migrations.rb +65 -0
  33. data/lib/active_warehouse/model.rb +5 -0
  34. data/lib/active_warehouse/model/aggregate.rb +244 -0
  35. data/lib/active_warehouse/model/cube.rb +273 -0
  36. data/lib/active_warehouse/model/dimension.rb +3 -0
  37. data/lib/active_warehouse/model/dimension/bridge.rb +32 -0
  38. data/lib/active_warehouse/model/dimension/dimension.rb +152 -0
  39. data/lib/active_warehouse/model/dimension/hierarchical_dimension.rb +35 -0
  40. data/lib/active_warehouse/model/fact.rb +96 -0
  41. data/lib/active_warehouse/model/report.rb +3 -0
  42. data/lib/active_warehouse/model/report/abstract_report.rb +121 -0
  43. data/lib/active_warehouse/model/report/chart_report.rb +9 -0
  44. data/lib/active_warehouse/model/report/table_report.rb +23 -0
  45. data/lib/active_warehouse/version.rb +9 -0
  46. data/lib/active_warehouse/view.rb +2 -0
  47. data/lib/active_warehouse/view/report_helper.rb +213 -0
  48. data/tasks/active_warehouse_tasks.rake +50 -0
  49. metadata +144 -0
@@ -0,0 +1,9 @@
1
+ module ActiveWarehouse
2
+ module Report
3
+ class ChartReport < ActiveRecord::Base
4
+ include AbstractReport
5
+ before_save :to_storage
6
+ after_save :from_storage
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ module ActiveWarehouse
2
+ module Report
3
+ # A report which is used to represent a tabular report.
4
+ class TableReport < ActiveRecord::Base
5
+ include AbstractReport
6
+ before_save :to_storage
7
+ after_save :from_storage
8
+ attr_accessor :format, :link_cell, :html_params
9
+
10
+ def format
11
+ @format ||= {}
12
+ end
13
+
14
+ def link_cell
15
+ @link_cell ||= false
16
+ end
17
+
18
+ def html_params
19
+ @html_params ||= {}
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ module ActiveWarehouse
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,2 @@
1
+ require 'active_warehouse/view/report_helper'
2
+ ActionView::Base.send :include, ReportHelper
@@ -0,0 +1,213 @@
1
+ module ReportHelper
2
+
3
+ # Render the report.
4
+ # * <tt>report</tt>: The report instance
5
+ # * <tt>html_options</tt>: HTML options
6
+ def render_report(report, html_options={})
7
+ case report
8
+ when ActiveWarehouse::Report::TableReport
9
+ render_table_report(report, html_options)
10
+ when ActiveWarehouse::Report::ChartReport
11
+ render_chart_report(report, html_options)
12
+ else
13
+ raise "Unsupported report type: #{report.class}"
14
+ end
15
+ end
16
+
17
+ private
18
+ def render_table_report(report, html_options)
19
+ cube = report.cube
20
+ fact_class = report.fact_class
21
+ column_dimension_class = report.column_dimension_class
22
+ row_dimension_class = report.row_dimension_class
23
+
24
+ column_dimension = report.column_dimension_name
25
+ row_dimension = report.row_dimension_name
26
+
27
+ column_hierarchy = report.column_hierarchy
28
+ column_hierarchy_constraints = report.column_constraints
29
+
30
+ row_hierarchy = report.row_hierarchy
31
+ row_hierarchy_constraints = report.row_constraints
32
+
33
+ fact_attributes = report.fact_attributes
34
+
35
+ html_params = report.html_params
36
+
37
+ cstage = (params[:cstage] || report.column_stage).to_i
38
+ rstage = (params[:rstage] || report.row_stage).to_i
39
+
40
+ col_param_prefix = report.column_param_prefix
41
+ row_param_prefix = report.row_param_prefix
42
+
43
+ link_cell = report.link_cell
44
+ format = report.format
45
+
46
+ # Get the dimension instances
47
+ col_dims = find_column_dimensions(column_dimension_class, column_hierarchy, cstage, col_param_prefix)
48
+ row_dims = find_row_dimensions(row_dimension_class, row_hierarchy, rstage, row_param_prefix)
49
+
50
+ # Get the aggregate map
51
+ agg_map = cube.aggregate_map(column_dimension, column_hierarchy, row_dimension, row_hierarchy, cstage, rstage)
52
+
53
+ table_attributes = {}
54
+ table_attributes[:class] = html_options[:report_class] ||= 'report'
55
+
56
+ col_hierarchy_length = column_dimension_class.hierarchy(column_hierarchy).length
57
+ row_hierarchy_length = row_dimension_class.hierarchy(row_hierarchy).length
58
+ col_group = column_dimension_class.hierarchy(column_hierarchy)[cstage]
59
+ row_group = row_dimension_class.hierarchy(row_hierarchy)[rstage]
60
+
61
+ # build the paths back to the top of the two dimension hierarchies
62
+ col_path = []
63
+ row_path = []
64
+ 0.upto(cstage) do |s|
65
+ p = params["#{col_param_prefix}_#{column_dimension_class.hierarchy(column_hierarchy)[s]}"]
66
+ col_path << p unless p.nil?
67
+ end
68
+ 0.upto(rstage) do |s|
69
+ p = params["#{row_param_prefix}_#{row_dimension_class.hierarchy(row_hierarchy)[s]}"]
70
+ row_path << p unless p.nil?
71
+ end
72
+ #col_path << col_group.to_s
73
+ #row_path << row_group.to_s
74
+
75
+ # build the XHTML
76
+ x = Builder::XmlMarkup.new(:indent => 2)
77
+ x.table(table_attributes) do |x|
78
+ x.tr do |x| # column dimension
79
+ x.th
80
+ col_dims.each do |col_dim|
81
+ col_dim_value = col_dim.send(col_group)
82
+ x.th({:colspan => fact_attributes.length}) do |x|
83
+ x << link_to_if((cstage < col_hierarchy_length - 1), col_dim_value, {
84
+ :cstage => cstage + 1,
85
+ :rstage => rstage,
86
+ "#{col_param_prefix}_#{col_group}" => col_dim_value}.merge(cube_params).merge(html_params))
87
+ end
88
+ end
89
+ end
90
+
91
+ x.tr do |x| # aggregated fact headers
92
+ x.th
93
+ col_dims.each do |col_dim|
94
+ #fact_class.aggregate_fields.each do |field_name, options|
95
+ # x.th(options[:label].to_s || field_name.to_s.humanize)
96
+ fact_attributes.each do |fact_attribute|
97
+ x.th(fact_attribute.to_s.humanize.titleize) # TODO replace with sortable column header
98
+ end
99
+ end
100
+ end
101
+
102
+ row_dims.each do |row_dim| # rows
103
+ row_dim_value = row_dim.send(row_group)
104
+ cell_row_path = row_path + [row_dim_value]
105
+ x.tr do |x|
106
+ x.td do |x| # row dimension label
107
+ x << link_to_if((rstage < row_hierarchy_length - 1), row_dim_value, {
108
+ :cstage => cstage,
109
+ :rstage => rstage + 1,
110
+ "#{row_param_prefix}_#{row_group}" => row_dim_value}.merge(cube_params).merge(html_params))
111
+ end
112
+
113
+ col_dims.each do |col_dim| # aggregated facts
114
+ col_dim_value = col_dim.send(col_group)
115
+ cell_col_path = col_path + [col_dim_value]
116
+ fact_attributes.each_with_index do |fact_attribute, index|
117
+ x.td do |x|
118
+ #puts "Row path (length=#{cell_row_path.length}): #{cell_row_path.join(':')}"
119
+ #puts "Col path (length=#{cell_col_path.length}): #{cell_col_path.join(':')}"
120
+
121
+ block = format[fact_attribute.to_sym]
122
+ value = agg_map.value(cell_row_path.join(':'), cell_col_path.join(':'), index).to_s
123
+ value = block.call(value) if block
124
+
125
+ x << link_to_if((cstage < col_hierarchy_length - 1 and rstage < row_hierarchy_length - 1 and link_cell),
126
+ #agg_map[row_path.join(':')][col_path.join(':')][index].to_s, {
127
+ value, {
128
+ :cstage => cstage + 1,
129
+ :rstage => rstage + 1,
130
+ "#{col_param_prefix}_#{col_group}" => col_dim_value,
131
+ "#{row_param_prefix}_#{row_group}" => row_dim_value}.merge(cube_params).merge(html_params))
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ end
139
+ end
140
+
141
+ def render_chart_report(report, html_options)
142
+ "Not Yet Implemented"
143
+ end
144
+
145
+ protected
146
+ def cube_params
147
+ p = {}
148
+ params.each { |key, value| p[key] = value if key =~ /^r_/ or key =~ /^c_/ }
149
+ p
150
+ end
151
+ def find_column_dimensions(dimension_class, hierarchy, current_stage, param_prefix='c')
152
+ find_dimensions(dimension_class, hierarchy, current_stage, param_prefix)
153
+ end
154
+
155
+ def find_row_dimensions(dimension_class, hierarchy, current_stage, param_prefix='r')
156
+ find_dimensions(dimension_class, hierarchy, current_stage, param_prefix)
157
+ end
158
+
159
+ def find_dimensions(dimension_class, hierarchy, current_stage, param_prefix)
160
+ # Construct the column find options
161
+ find_options = {}
162
+ group_by = []
163
+ conditions_sql = []
164
+ conditions_args = []
165
+ current_stage.to_i.downto(0) do |stage|
166
+ level = dimension_class.hierarchy(hierarchy)[stage]
167
+ group_by << level
168
+ unless stage == current_stage
169
+ conditions_sql << "#{level} = ?"
170
+ conditions_args << params["#{param_prefix}_#{level}"]
171
+ end
172
+ end
173
+ find_options[:conditions] = nil
174
+ if conditions_args.length > 0
175
+ find_options[:conditions] = [conditions_sql.join(" and ")] + conditions_args
176
+ end
177
+ find_options[:group] = find_options[:order] = group_by.join(',')
178
+ dimension_class.find(:all, find_options)
179
+ end
180
+
181
+ public
182
+ # Create a sortable column header.
183
+ #
184
+ # Parameters are
185
+ # column_name: The column display name
186
+ # order_by: The column name from the table used for sorting
187
+ # link_parameters: Parameters passed to the hypertext link
188
+ # attributes: Parameters passed to the <th> element
189
+ #
190
+ # Example:
191
+ # sortable_column_header('SSN','ssn')
192
+ # sortable_column_header('Name','name',{:onclick => 'doSomething();'}, {:title => 'Some Title'})
193
+ def sortable_column_header(column_name, order_by, link_parameters={}, attributes={})
194
+ order = 'desc'
195
+ if params[:order_by].to_s == order_by.to_s
196
+ sorted = 'sorted'
197
+ if params[:order] == 'desc' || params[:order].nil?
198
+ order_css_class = 'order_down'
199
+ order = 'asc'
200
+ elsif params[:order] == 'asc'
201
+ order_css_class = 'order_up'
202
+ order = 'desc'
203
+ end
204
+ else
205
+ sorted = ''
206
+ end
207
+
208
+ attributes.merge!({:class => "sortable #{sorted} #{order_css_class}"})
209
+ x.th(attributes) do |th|
210
+ th << link_to(column_name, params.merge({:order_by => order_by, :order => order}), link_parameters)
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,50 @@
1
+ namespace :warehouse do
2
+ desc "Build a 'standard' date dimension"
3
+ task :build_date_dimension => :environment do
4
+ abcs = ActiveRecord::Base.configurations
5
+ ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
6
+
7
+ start_date = (ENV['START_DATE'] ? Time.parse(ENV['START_DATE']) : Time.now.years_ago(5))
8
+ end_date = (ENV['END_DATE'] ? Time.parse(ENV['END_DATE']) : Time.now )
9
+
10
+ ddb = ActiveWarehouse::Builder::DateDimensionBuilder.new(start_date, end_date)
11
+ ddb.build.each do |record|
12
+ dd = DateDimension.new
13
+ record.each do |key,value|
14
+ dd.send("#{key}=".to_sym, value) if dd.respond_to?(key)
15
+ end
16
+ dd.save!
17
+ end
18
+ end
19
+
20
+ desc "Rebuild the warehouse" # TODO: consider moving this logic somewhere into a class and calling it from here
21
+ task :rebuild => :environment do
22
+ puts "Rebuilding data warehouse"
23
+ # Discover and require all cube models
24
+ # TODO: do some more research on the potential problems with this
25
+ models_dir = File.join(File.dirname(__FILE__), '../../../../app/models')
26
+ Dir.glob(File.join(models_dir, "**", "*.rb")).each do |f|
27
+ if f =~ /_cube\.rb/
28
+ require f
29
+ end
30
+ end
31
+
32
+ t = Benchmark.realtime do
33
+ ActiveWarehouse::Cube.subclasses.each do |subclass|
34
+ puts "Rebuilding #{subclass}"
35
+ tc = Benchmark.realtime do
36
+ subclass.populate(:force => true)
37
+ end
38
+ puts "Rebuilt #{subclass} in #{tc}s"
39
+ end
40
+ end
41
+ puts "Data warehouse rebuilt in #{t}s"
42
+ end
43
+
44
+ desc "Migrate ActiveWarehouse"
45
+ task :migrate => :environment do
46
+ puts "Migrating ActiveWarehouse"
47
+ migration_directory = File.join(File.dirname(__FILE__), '../db/migrations')
48
+ ActiveWarehouse::Migrator.migrate(migration_directory, ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
49
+ end
50
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: activewarehouse
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.0
7
+ date: 2006-12-19 00:00:00 -05:00
8
+ summary: Build data warehouses with Rails.
9
+ require_paths:
10
+ - lib
11
+ email: anthonyeden@gmail.com
12
+ homepage: http://activewarehouse.rubyforge.org
13
+ rubyforge_project: activewarehouse
14
+ description: ActiveWarehouse extends Rails to provide functionality specific for building data warehouses.
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: false
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Anthony Eden
31
+ files:
32
+ - install.rb
33
+ - README
34
+ - TODO
35
+ - Rakefile
36
+ - db/migrations
37
+ - db/migrations/001_create_table_reports.rb
38
+ - doc/agg_queries.txt
39
+ - doc/agg_queries_results.txt
40
+ - doc/queries.txt
41
+ - generators/cube
42
+ - generators/dimension
43
+ - generators/fact
44
+ - generators/cube/cube_generator.rb
45
+ - generators/cube/templates
46
+ - generators/cube/USAGE
47
+ - generators/cube/templates/model.rb
48
+ - generators/cube/templates/unit_test.rb
49
+ - generators/dimension/dimension_generator.rb
50
+ - generators/dimension/templates
51
+ - generators/dimension/USAGE
52
+ - generators/dimension/templates/fixture.yml
53
+ - generators/dimension/templates/migration.rb
54
+ - generators/dimension/templates/model.rb
55
+ - generators/dimension/templates/unit_test.rb
56
+ - generators/fact/fact_generator.rb
57
+ - generators/fact/templates
58
+ - generators/fact/USAGE
59
+ - generators/fact/templates/fixture.yml
60
+ - generators/fact/templates/migration.rb
61
+ - generators/fact/templates/model.rb
62
+ - generators/fact/templates/unit_test.rb
63
+ - lib/active_warehouse
64
+ - lib/active_warehouse.rb
65
+ - lib/active_warehouse/builder
66
+ - lib/active_warehouse/builder.rb
67
+ - lib/active_warehouse/core_ext
68
+ - lib/active_warehouse/core_ext.rb
69
+ - lib/active_warehouse/migrations.rb
70
+ - lib/active_warehouse/model
71
+ - lib/active_warehouse/model.rb
72
+ - lib/active_warehouse/version.rb
73
+ - lib/active_warehouse/view
74
+ - lib/active_warehouse/view.rb
75
+ - lib/active_warehouse/builder/date_dimension_builder.rb
76
+ - lib/active_warehouse/builder/random_data_builder.rb
77
+ - lib/active_warehouse/core_ext/time
78
+ - lib/active_warehouse/core_ext/time.rb
79
+ - lib/active_warehouse/core_ext/time/calculations.rb
80
+ - lib/active_warehouse/model/aggregate.rb
81
+ - lib/active_warehouse/model/cube.rb
82
+ - lib/active_warehouse/model/dimension
83
+ - lib/active_warehouse/model/dimension.rb
84
+ - lib/active_warehouse/model/fact.rb
85
+ - lib/active_warehouse/model/report
86
+ - lib/active_warehouse/model/report.rb
87
+ - lib/active_warehouse/model/dimension/bridge.rb
88
+ - lib/active_warehouse/model/dimension/dimension.rb
89
+ - lib/active_warehouse/model/dimension/hierarchical_dimension.rb
90
+ - lib/active_warehouse/model/report/abstract_report.rb
91
+ - lib/active_warehouse/model/report/chart_report.rb
92
+ - lib/active_warehouse/model/report/table_report.rb
93
+ - lib/active_warehouse/view/report_helper.rb
94
+ - tasks/active_warehouse_tasks.rake
95
+ test_files: []
96
+
97
+ rdoc_options:
98
+ - --exclude
99
+ - .
100
+ extra_rdoc_files: []
101
+
102
+ executables: []
103
+
104
+ extensions: []
105
+
106
+ requirements: []
107
+
108
+ dependencies:
109
+ - !ruby/object:Gem::Dependency
110
+ name: rake
111
+ version_requirement:
112
+ version_requirements: !ruby/object:Gem::Version::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 0.7.1
117
+ version:
118
+ - !ruby/object:Gem::Dependency
119
+ name: activesupport
120
+ version_requirement:
121
+ version_requirements: !ruby/object:Gem::Version::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: 1.3.1.5618
126
+ version:
127
+ - !ruby/object:Gem::Dependency
128
+ name: activerecord
129
+ version_requirement:
130
+ version_requirements: !ruby/object:Gem::Version::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: 1.14.4.5618
135
+ version:
136
+ - !ruby/object:Gem::Dependency
137
+ name: actionpack
138
+ version_requirement:
139
+ version_requirements: !ruby/object:Gem::Version::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: 1.12.5.5618
144
+ version: