reporter 0.0.1

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.
@@ -0,0 +1,62 @@
1
+ module Reporter::Support::TimeRange
2
+
3
+ def time_range?
4
+ valid_types = [Date, DateTime, Time, ActiveSupport::TimeWithZone]
5
+ return false unless valid_types.include?(self.begin.class)
6
+ return false unless valid_types.include?(self.end.class)
7
+ true
8
+ end
9
+
10
+ def human_name
11
+ return inspect unless time_range?
12
+ b = test_begin_ends(self.begin)
13
+ e = test_begin_ends(self.end)
14
+
15
+ if self.begin.year == self.end.year
16
+ if b[:by] and e[:ey]
17
+ return self.begin.strftime("%Y")
18
+ elsif b[:bq] and e[:eq]
19
+ bq = get_quarter(self.begin)
20
+ eq = get_quarter(self.end)
21
+ if bq == eq
22
+ return I18n.t("time_range.quarter", :year => self.begin.year, :quarter => bq, :default => "q%{quarter} %{year}")
23
+ else
24
+ return I18n.t("time_range.multi_quarter", :year => self.begin.year,
25
+ :begin_quarter => bq, :end_quarter => eq, :default => "q%{begin_quarter} .. q%{end_quarter} %{year}")
26
+ end
27
+ elsif b[:bm] and e[:em]
28
+ if self.begin.month == self.end.month
29
+ return self.begin.strftime("%b '%y")
30
+ else
31
+ return "#{self.begin.strftime("%b")} .. #{self.end.strftime("%b '%y")}"
32
+ end
33
+ end
34
+ else # multi year
35
+ if b[:by] and e[:ey]
36
+ return "#{self.begin.strftime("'%y")} .. #{self.end.strftime("'%y")}"
37
+ else
38
+ return inspect
39
+ end
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def get_quarter(date)
46
+ qs = [3, 6, 9, 12]
47
+ q = qs.detect { |q| date.month <= q }
48
+ (qs.index q) + 1
49
+ end
50
+
51
+ def test_begin_ends(date)
52
+ r = {}
53
+ ["year", "quarter", "month", "week"].each do |element|
54
+ r["b#{element.first}".to_sym] = (date.send("at_beginning_of_#{element}".to_sym) == date)
55
+ r["e#{element.first}".to_sym] = (date.send("at_end_of_#{element}".to_sym) == date)
56
+ end
57
+ r
58
+ end
59
+
60
+ end
61
+
62
+ Range.send(:include, Reporter::Support::TimeRange)
@@ -0,0 +1,85 @@
1
+ module Reporter::TimeIterator
2
+
3
+ public
4
+
5
+ VALID_TIME_STEP_SIZES = :total, :year, :quarter, :month, :week, :day
6
+
7
+ def iterate_time axis, *steps, &block
8
+ options = steps.extract_options!
9
+ scope = data_source.scopes.get axis
10
+ raise "Scope is not of Date type" unless scope.is_a? Reporter::Scope::DateScope
11
+ steps.each do |step|
12
+ raise "invalid stepsize: #{step}. must be one of #{VALID_TIME_STEP_SIZES.inspect}" unless VALID_TIME_STEP_SIZES.include? step
13
+ end
14
+ date_tree = build_date_tree steps
15
+ iterate_date_tree scope, date_tree, scope.limit, &block
16
+ scope.change nil
17
+ end
18
+
19
+ private
20
+
21
+ DATE_PART_VALUES = {
22
+ :total => 6,
23
+ :year => 5,
24
+ :quarter => 4,
25
+ :month => 3,
26
+ :week => 2,
27
+ :day => 1
28
+ }
29
+
30
+ def build_date_tree date_parts
31
+ date_parts = date_parts.dup
32
+ return nil if date_parts.empty?
33
+ # [6, 4, 3, 5]
34
+ # 2010 - 2011, q1, 1,2,3, q2, 4,5,6, q3, 7,8,9, q4, 10,11,12, 2010, q1, 1,2,3, q2, 4,5,6, q3, 7,8,9, q4, 10,11,12, 2011
35
+ date_coded = date_parts.collect { |part| DATE_PART_VALUES[part] }
36
+ if date_coded.first == date_coded.max
37
+ parent = date_parts.shift
38
+ {:children_first => false, :name => parent, :children => build_date_tree(date_parts)}
39
+ elsif date_coded.last == date_coded.max
40
+ parent = date_parts.pop
41
+ {:children_first => true, :name => parent, :children => build_date_tree(date_parts)}
42
+ else
43
+ raise "invalid sequence: #{date_parts.inspect}"
44
+ end
45
+ end
46
+
47
+ def iterate_date_tree scope, tree, time_frame, &block
48
+ iterate_time_periods time_frame, tree[:name] do |new_time_frame, optimization|
49
+ iterate_date_tree scope, tree[:children], time_frame, &block if tree[:children_first] and tree[:children]
50
+ scope.change new_time_frame
51
+ @time_iteration_row ||= Reporter::TimeOptimizedResultRow.new(self, nil, scope, time_frame)
52
+ @time_iteration_row.current_iteration = optimization
53
+ @time_iteration_row.scope = data_source.scopes.current_scope
54
+ yield @time_iteration_row
55
+ iterate_date_tree scope, tree[:children], time_frame, &block if !tree[:children_first] and tree[:children]
56
+ end
57
+ end
58
+
59
+ def iterate_time_periods period, block_type, &block
60
+ if block_type == :total
61
+ yield period
62
+ return
63
+ end
64
+ advancement, filter = case block_type
65
+ when :year : [{ :years => 1 }, [:year]]
66
+ when :quarter : [{ :months => 3 }, [:quarter, :year]]
67
+ when :month : [{ :months => 1 }, [:month, :year]]
68
+ when :week : [{ :weeks => 1 }, [:week, :year]]
69
+ when :day : [{ :days => 1 }, [:day, :month, :year]]
70
+ else raise "Unsupported type: #{block_type}"
71
+ end
72
+ iterate_period = period.begin.send("beginning_of_#{block_type}".to_sym) .. period.begin.send("end_of_#{block_type}".to_sym)
73
+ optimization = { :type => block_type, :filter => filter, :period => {} }
74
+
75
+ while iterate_period.begin < period.end
76
+ optimization[:period] = {}
77
+ optimization[:filter].each { |field| optimization[:period][field] = iterate_period.begin.send(field) }
78
+
79
+ yield iterate_period, optimization
80
+ iterate_period = iterate_period.begin.advance(advancement).send("beginning_of_#{block_type}".to_sym) ..
81
+ iterate_period.end.advance(advancement).send("end_of_#{block_type}".to_sym)
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,39 @@
1
+ class Reporter::TimeOptimizedResultRow < Reporter::ResultRow
2
+
3
+ def initialize(data_set, scope_serialization, scope, period)
4
+ super data_set, scope_serialization
5
+ @scope = scope
6
+ @period = period
7
+ @active_scope = nil
8
+ end
9
+
10
+ attr_accessor :current_iteration
11
+
12
+ def scope= scope
13
+ @scope_serialization = scope
14
+ end
15
+
16
+ def [] field
17
+ field_cache[field] ||= {}
18
+ preload_time_period_values_for field unless field_cache[field].has_key? current_iteration[:type]
19
+ field_cache[field][current_iteration[:type]] ||= {}
20
+ field_cache[field][current_iteration[:type]][current_iteration[:period]] ||= load_field_values(field)[field]
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :data_set, :scope_serialization, :period
26
+
27
+ def preload_time_period_values_for(field_name)
28
+ #Rails.logger.info "Trying to pre-load data for #{field_name} for the period of #{period} in chunks of #{current_iteration[:filter].to_sentence}"
29
+
30
+ field = data_set.data_structure.fields[field_name]
31
+ if field.respond_to? :preload_for_period
32
+ #Rails.logger.info "Preloading possible for #{field_name}!"
33
+ field_cache[field_name] ||= {}
34
+ field_cache[field_name][current_iteration[:type]] = \
35
+ field.preload_for_period data_set.data_source, {}, period, current_iteration[:filter], @scope
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,36 @@
1
+ class Reporter::Value
2
+
3
+ def initialize(field_alias, field_human_name, value, human_value, description, source_link)
4
+ @field_alias = field_alias
5
+ @field_human_name = field_human_name || field_alias
6
+ @value = value
7
+ @human_value = human_value
8
+ @description = description
9
+ @source_link = source_link
10
+ end
11
+
12
+ attr_reader :field_alias, :field_human_name
13
+ attr_accessor :value, :description, :source_link
14
+ attr_writer :human_value
15
+
16
+ def human_value
17
+ @human_value || value
18
+ end
19
+
20
+ def to_s
21
+ human_value
22
+ end
23
+
24
+ def as_percentage
25
+ if @value.is_a? Numeric
26
+ "%.2f %%" % (@value * 100.0)
27
+ end
28
+ end
29
+
30
+ def round(precision = 2)
31
+ if @value.is_a? Numeric
32
+ "%.#{precision}f" % @value
33
+ end
34
+ end
35
+
36
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reporter
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Matthijs Groen
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-09-16 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: activerecord
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 7
30
+ segments:
31
+ - 3
32
+ - 0
33
+ - 0
34
+ version: 3.0.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: activesupport
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 7
46
+ segments:
47
+ - 3
48
+ - 0
49
+ - 0
50
+ version: 3.0.0
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: arel
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ hash: 21
62
+ segments:
63
+ - 1
64
+ - 0
65
+ - 1
66
+ version: 1.0.1
67
+ type: :runtime
68
+ version_requirements: *id003
69
+ description: "\n Reporter adds a consistent way to build reports.\n "
70
+ email: matthijs.groen@gmail.com
71
+ executables: []
72
+
73
+ extensions: []
74
+
75
+ extra_rdoc_files:
76
+ - README.markdown
77
+ files:
78
+ - Gemfile
79
+ - MIT-LICENSE
80
+ - README.markdown
81
+ - Rakefile
82
+ - VERSION
83
+ - lib/reporter/data_set.rb
84
+ - lib/reporter/data_source.rb
85
+ - lib/reporter/data_source/active_record_source.rb
86
+ - lib/reporter/data_source/scoping.rb
87
+ - lib/reporter/data_structure.rb
88
+ - lib/reporter/field/average_field.rb
89
+ - lib/reporter/field/base.rb
90
+ - lib/reporter/field/calculation_field.rb
91
+ - lib/reporter/field/count_field.rb
92
+ - lib/reporter/field/field.rb
93
+ - lib/reporter/field/formula_field.rb
94
+ - lib/reporter/field/sum_field.rb
95
+ - lib/reporter/formula.rb
96
+ - lib/reporter/result_row.rb
97
+ - lib/reporter/scope/base.rb
98
+ - lib/reporter/scope/date_scope.rb
99
+ - lib/reporter/scope/reference_scope.rb
100
+ - lib/reporter/support/time_range.rb
101
+ - lib/reporter/time_iterator.rb
102
+ - lib/reporter/time_optimized_result_row.rb
103
+ - lib/reporter/value.rb
104
+ has_rdoc: true
105
+ homepage: http://github.com/matthijsgroen/reporter
106
+ licenses: []
107
+
108
+ post_install_message:
109
+ rdoc_options:
110
+ - --charset=UTF-8
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ hash: 3
119
+ segments:
120
+ - 0
121
+ version: "0"
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ hash: 3
128
+ segments:
129
+ - 0
130
+ version: "0"
131
+ requirements: []
132
+
133
+ rubyforge_project:
134
+ rubygems_version: 1.3.7
135
+ signing_key:
136
+ specification_version: 3
137
+ summary: Report builder.
138
+ test_files: []
139
+