activewarehouse 0.1.0

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.
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: