clark_kent 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +34 -0
  5. data/app/assets/javascripts/clark_kent/application.js +13 -0
  6. data/app/assets/javascripts/clark_kent/reports.js +10 -0
  7. data/app/assets/stylesheets/_reports.scss +67 -0
  8. data/app/assets/stylesheets/_reports_print.scss +44 -0
  9. data/app/assets/stylesheets/clark_kent/application.css +15 -0
  10. data/app/assets/stylesheets/clark_kent/reports.css +4 -0
  11. data/app/assets/stylesheets/scaffold.css +56 -0
  12. data/app/controllers/clark_kent/application_controller.rb +2 -0
  13. data/app/controllers/clark_kent/report_columns_controller.rb +47 -0
  14. data/app/controllers/clark_kent/report_emails_controller.rb +47 -0
  15. data/app/controllers/clark_kent/report_filters_controller.rb +51 -0
  16. data/app/controllers/clark_kent/reports_controller.rb +79 -0
  17. data/app/controllers/clark_kent/user_report_emails_controller.rb +53 -0
  18. data/app/helpers/clark_kent/application_helper.rb +70 -0
  19. data/app/mailers/clark_kent/report_mailer.rb +15 -0
  20. data/app/models/clark_kent/report.rb +272 -0
  21. data/app/models/clark_kent/report_column.rb +24 -0
  22. data/app/models/clark_kent/report_date_filter.rb +81 -0
  23. data/app/models/clark_kent/report_email.rb +105 -0
  24. data/app/models/clark_kent/report_filter.rb +26 -0
  25. data/app/models/clark_kent/report_filter_option.rb +18 -0
  26. data/app/models/clark_kent/report_number_filter.rb +20 -0
  27. data/app/models/clark_kent/report_object_filter.rb +27 -0
  28. data/app/models/clark_kent/report_result.rb +55 -0
  29. data/app/models/clark_kent/report_string_filter.rb +5 -0
  30. data/app/models/clark_kent/reportable.rb +148 -0
  31. data/app/models/clark_kent/sharing_scope.rb +34 -0
  32. data/app/models/clark_kent/sharing_scope_kind.rb +73 -0
  33. data/app/models/clark_kent/user_report_email.rb +19 -0
  34. data/app/validators/clark_kent/user_email_validator.rb +7 -0
  35. data/app/views/clark_kent/report_columns/_form.html.erb +50 -0
  36. data/app/views/clark_kent/report_columns/_index.html.erb +16 -0
  37. data/app/views/clark_kent/report_columns/_show.html.erb +20 -0
  38. data/app/views/clark_kent/report_columns/_show_wrapper.html.erb +3 -0
  39. data/app/views/clark_kent/report_emails/_edit.html.erb +12 -0
  40. data/app/views/clark_kent/report_emails/_form.html.erb +34 -0
  41. data/app/views/clark_kent/report_emails/_index.html.erb +16 -0
  42. data/app/views/clark_kent/report_emails/_show.html.erb +17 -0
  43. data/app/views/clark_kent/report_emails/_show_wrapper.html.erb +3 -0
  44. data/app/views/clark_kent/report_filters/_date_filter_edit.html.erb +9 -0
  45. data/app/views/clark_kent/report_filters/_date_filter_show.html.erb +1 -0
  46. data/app/views/clark_kent/report_filters/_form.html.erb +58 -0
  47. data/app/views/clark_kent/report_filters/_index.html.erb +16 -0
  48. data/app/views/clark_kent/report_filters/_number_filter_edit.html.erb +2 -0
  49. data/app/views/clark_kent/report_filters/_number_filter_show.html.erb +1 -0
  50. data/app/views/clark_kent/report_filters/_object_filter_edit.html.erb +1 -0
  51. data/app/views/clark_kent/report_filters/_object_filter_show.html.erb +1 -0
  52. data/app/views/clark_kent/report_filters/_show.html.erb +14 -0
  53. data/app/views/clark_kent/report_filters/_show_wrapper.html.erb +3 -0
  54. data/app/views/clark_kent/report_filters/_string_filter_edit.html.erb +0 -0
  55. data/app/views/clark_kent/report_filters/_string_filter_show.html.erb +1 -0
  56. data/app/views/clark_kent/report_mailer/report_run.html.erb +2 -0
  57. data/app/views/clark_kent/reports/_date_filter.html.erb +23 -0
  58. data/app/views/clark_kent/reports/_download_link.html.erb +14 -0
  59. data/app/views/clark_kent/reports/_edit.html.erb +47 -0
  60. data/app/views/clark_kent/reports/_form.html.erb +33 -0
  61. data/app/views/clark_kent/reports/_number_filter.html.erb +21 -0
  62. data/app/views/clark_kent/reports/_object_filter.html.erb +15 -0
  63. data/app/views/clark_kent/reports/_print_report.html.erb +30 -0
  64. data/app/views/clark_kent/reports/_show.html.erb +21 -0
  65. data/app/views/clark_kent/reports/_string_filter.html.erb +9 -0
  66. data/app/views/clark_kent/reports/edit.html.erb +21 -0
  67. data/app/views/clark_kent/reports/index.html.erb +22 -0
  68. data/app/views/clark_kent/reports/new.html.erb +9 -0
  69. data/app/views/clark_kent/reports/show.html.erb +43 -0
  70. data/app/views/clark_kent/user_report_emails/_form.html.erb +40 -0
  71. data/app/views/clark_kent/user_report_emails/_index.html.erb +16 -0
  72. data/app/views/clark_kent/user_report_emails/_show.html.erb +11 -0
  73. data/app/views/clark_kent/user_report_emails/_show_wrapper.html.erb +3 -0
  74. data/config/routes.rb +13 -0
  75. data/db/migrate/20150304233739_create_clark_kent_reports.rb +12 -0
  76. data/lib/clark_kent/engine.rb +5 -0
  77. data/lib/clark_kent/version.rb +3 -0
  78. data/lib/clark_kent.rb +20 -0
  79. data/lib/tasks/clark_kent_tasks.rake +4 -0
  80. data/test/clark_kent_test.rb +7 -0
  81. data/test/controllers/clark_kent/reports_controller_test.rb +51 -0
  82. data/test/dummy/README.rdoc +28 -0
  83. data/test/dummy/Rakefile +6 -0
  84. data/test/dummy/app/assets/javascripts/application.js +13 -0
  85. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  86. data/test/dummy/app/controllers/application_controller.rb +5 -0
  87. data/test/dummy/app/helpers/application_helper.rb +2 -0
  88. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  89. data/test/dummy/bin/bundle +3 -0
  90. data/test/dummy/bin/rails +4 -0
  91. data/test/dummy/bin/rake +4 -0
  92. data/test/dummy/config/application.rb +23 -0
  93. data/test/dummy/config/boot.rb +5 -0
  94. data/test/dummy/config/database.yml +25 -0
  95. data/test/dummy/config/environment.rb +5 -0
  96. data/test/dummy/config/environments/development.rb +37 -0
  97. data/test/dummy/config/environments/production.rb +78 -0
  98. data/test/dummy/config/environments/test.rb +39 -0
  99. data/test/dummy/config/initializers/assets.rb +8 -0
  100. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  101. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  102. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  103. data/test/dummy/config/initializers/inflections.rb +16 -0
  104. data/test/dummy/config/initializers/mime_types.rb +4 -0
  105. data/test/dummy/config/initializers/session_store.rb +3 -0
  106. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  107. data/test/dummy/config/locales/en.yml +23 -0
  108. data/test/dummy/config/routes.rb +4 -0
  109. data/test/dummy/config/secrets.yml +22 -0
  110. data/test/dummy/config.ru +4 -0
  111. data/test/dummy/db/development.sqlite3 +0 -0
  112. data/test/dummy/db/schema.rb +25 -0
  113. data/test/dummy/log/development.log +35 -0
  114. data/test/dummy/public/404.html +67 -0
  115. data/test/dummy/public/422.html +67 -0
  116. data/test/dummy/public/500.html +66 -0
  117. data/test/dummy/public/favicon.ico +0 -0
  118. data/test/dummy/tmp/cache/assets/development/sprockets/0a5b3da98f8307d16bc302a1f7206591 +0 -0
  119. data/test/dummy/tmp/cache/assets/development/sprockets/0a9995208f1340e4b34008cbd5b73c64 +0 -0
  120. data/test/dummy/tmp/cache/assets/development/sprockets/0ec37c0a58c1be93659732a3efc73581 +0 -0
  121. data/test/dummy/tmp/cache/assets/development/sprockets/0fd54fd98cd2fa0085b77e6743046927 +0 -0
  122. data/test/dummy/tmp/cache/assets/development/sprockets/5d3db72d44bc30497bd84a40d2002e12 +0 -0
  123. data/test/dummy/tmp/cache/assets/development/sprockets/60fb63be4cad769d9adc90c4c5501c67 +0 -0
  124. data/test/dummy/tmp/cache/assets/development/sprockets/8aa37926d964a9eb59cf9b940e4fe2f4 +0 -0
  125. data/test/dummy/tmp/cache/assets/development/sprockets/8dd3bd27ebbaecaf6c7ee8ed81be5bde +0 -0
  126. data/test/dummy/tmp/cache/assets/development/sprockets/92058832b745b88c29a75bf2aad7245d +0 -0
  127. data/test/dummy/tmp/cache/assets/development/sprockets/ba6c7581456ee0f828ace58e4856a9f4 +0 -0
  128. data/test/dummy/tmp/cache/assets/development/sprockets/ebe8eac74b8e6016fd44b19e6e708e61 +0 -0
  129. data/test/dummy/tmp/cache/assets/development/sprockets/f23dd414c3bac1b6833d2aa9e62fbedd +0 -0
  130. data/test/fixtures/clark_kent/reports.yml +13 -0
  131. data/test/helpers/clark_kent/reports_helper_test.rb +6 -0
  132. data/test/integration/navigation_test.rb +10 -0
  133. data/test/models/clark_kent/report_test.rb +9 -0
  134. data/test/test_helper.rb +17 -0
  135. metadata +260 -0
@@ -0,0 +1,272 @@
1
+ module ClarkKent
2
+ # load the builders
3
+ Dir.glob(Rails.root.join('app/models/reporting/*.rb')) { |file| load file }
4
+ class Report < ActiveRecord::Base
5
+
6
+ include Cloneable
7
+
8
+ SortDirections = {'A->Z' => 'asc', 'Z->A' => 'desc'}
9
+
10
+ attr_accessible :resource_type, :name, :sharing_scope_id, :sharing_scope_type
11
+ attr_accessor :summary_row_storage
12
+
13
+ belongs_to :sharing_scope, polymorphic: true
14
+ has_many :report_filters, as: :filterable, dependent: :destroy
15
+ has_many :report_columns, -> {order("clark_kent_report_columns.column_order").references(:report_columns)}, dependent: :destroy
16
+ has_many :report_emails, dependent: :destroy
17
+ has_many :report_email_filters, through: :report_emails, source: :report_filters
18
+
19
+ scope :for, ->(resource_type) { where(resource_type: resource_type) }
20
+ scope :shared, -> { where(sharing_scope_id: nil) }
21
+
22
+ validates :sharing_scope_id, presence: true, if: ->(r) { r.sharing_scope_type.present? }
23
+
24
+ def self.send_report_to_s3(report_id, params)
25
+ params = params
26
+ report_class = params['report_class'].constantize if params['report_class']
27
+ report_class ||= ::ClarkKent::Report
28
+ reportable = report_class.find(report_id)
29
+ report = ('ClarkKent::ReportEmail' == report_class.name) ? reportable.report : reportable
30
+ query = reportable.get_query(params)
31
+ row_count = reportable.get_query(params, true)
32
+ bucket = AWS::S3::Bucket.new(ClarkKent::ReportUploaderBucketName)
33
+ report_destination = bucket.objects[params['report_result_name']]
34
+ byte_count = 0
35
+ temp_buffer = report.headers.to_csv
36
+ offset = 0
37
+ summary_rows = []
38
+ some_left = true
39
+ report_destination.write(estimated_content_length: row_count * 1000) do |buffer, bytes|
40
+ while temp_buffer.length < bytes and some_left
41
+ this_batch = query.offset(offset).limit(100)
42
+ some_left = this_batch.each do |row|
43
+ temp_buffer << report.report_columns.map{ |column| row[column.column_name] }.to_csv
44
+ end.any?
45
+ summary_rows.push report.summary_row(this_batch) if report.summary_row? and some_left
46
+ offset += 100
47
+ end
48
+ if report.summary_row_storage.blank? and (!some_left)
49
+ if report.summary_row? and summary_rows.any?
50
+ summary_row_map = report.summary_row(summary_rows)
51
+ report.summary_row_storage = report.report_columns.map do |col|
52
+ summary_row_map.send col.column_name
53
+ end
54
+ temp_buffer << report.summary_row_storage.to_csv
55
+ end
56
+ end
57
+ if temp_buffer.present?
58
+ chunk = temp_buffer.slice!(0...bytes)
59
+ buffer << chunk
60
+ end
61
+ end
62
+ report_result_url = report_destination.url_for(
63
+ :read,
64
+ secure: true,
65
+ response_content_type: 'text/csv',
66
+ response_content_encoding: 'binary',
67
+ expires: 60*60*24*30 #good for 30 days
68
+ )
69
+ if 'ClarkKent::Report' == report_class.name
70
+ ForeignOffice.publish(channel: params['report_result_name'], object: {report_result_url: report_result_url.to_s} )
71
+ end
72
+ report_result_url.to_s
73
+ end
74
+
75
+ def get_query(params, count = false)
76
+ self.resource_class.report(params,self, count)
77
+ end
78
+
79
+ def resource_class
80
+ @resource_class ||= self.resource_type.constantize
81
+ end
82
+
83
+ def sort_column
84
+ @sort_column ||= self.report_columns.where("clark_kent_report_columns.report_sort is not NULL and clark_kent_report_columns.report_sort != ''").first
85
+ end
86
+
87
+ def sorter
88
+ if self.sort_column
89
+ sort_column_name = self.sort_column.column_name
90
+ sort_direction = Report::SortDirections[sort_column.report_sort]
91
+ Map.new(order_column: sort_column_name, order_direction: sort_direction)
92
+ end
93
+ end
94
+
95
+ def arel_includes
96
+ self.report_columns.map{|column|
97
+ column_info = self.column_options_for(column.column_name)
98
+ column_info.includes if column_info.respond_to? :includes
99
+ }.compact
100
+ end
101
+
102
+ def arel_joins
103
+ self.report_columns.map{|column|
104
+ column_info = self.column_options_for(column.column_name)
105
+ column_info.joins if column_info.respond_to? :joins
106
+ }.compact
107
+ end
108
+
109
+ def extra_scopes
110
+ self.report_columns.map{|column|
111
+ column_info = self.column_options_for(column.column_name)
112
+ column_info.extra_scopes if column_info.respond_to? :extra_scopes
113
+ }.flatten.compact
114
+ end
115
+
116
+ def extra_filters
117
+ self.report_columns.map{|column|
118
+ column_info = self.column_options_for(column.column_name)
119
+ column_info.where if column_info.respond_to? :where
120
+ }.flatten.compact
121
+ end
122
+
123
+ def groups
124
+ self.report_columns.map{|column|
125
+ column_info = self.column_options_for(column.column_name)
126
+ column_info.group if column_info.respond_to? :group
127
+ }.flatten.compact
128
+ end
129
+
130
+ def report_filter_params
131
+ Hash[*self.report_filters.map{|filter| filter.filter_match_params}.flatten].
132
+ merge(order: self.sorter)
133
+ end
134
+
135
+ def select_clauses
136
+ @selects = []
137
+ self.report_columns.each do |report_column|
138
+ column_option = self.column_options_for(report_column.column_name)
139
+ @selects.push column_option.custom_select if column_option.respond_to? :custom_select
140
+ end
141
+ self.report_filters.each do |report_filter|
142
+ column_option = self.column_options_for(report_filter.filter_name)
143
+ @selects.push column_option.custom_select if column_option.respond_to? :custom_select
144
+ end
145
+ @selects
146
+ end
147
+
148
+
149
+ def filter_options_for(filter_name)
150
+ self.resource_class::REPORT_FILTER_OPTIONS.detect{|filter| filter.param == filter_name}
151
+ end
152
+
153
+ def column_options_for(column_name)
154
+ if self.resource_class::REPORT_COLUMN_OPTIONS.has_key? column_name.to_sym
155
+ self.resource_class::REPORT_COLUMN_OPTIONS[column_name.to_sym]
156
+ else
157
+ column_name = column_name.to_s.split('_')[0..-2].join('_')
158
+ if self.resource_class::REPORT_COLUMN_OPTIONS.has_key? column_name.to_sym
159
+ self.resource_class::REPORT_COLUMN_OPTIONS[column_name.to_sym]
160
+ end
161
+ end
162
+ end
163
+
164
+ def filter_kind(filter_name)
165
+ self.filter_options_for(filter_name).kind
166
+ end
167
+
168
+ def date_filter_names
169
+ self.resource_class::REPORT_FILTER_OPTIONS.select{|filter| 'date_filter' == filter.kind}.map{|filter| filter.param}
170
+ end
171
+
172
+ def available_filters
173
+ self.available_email_filters.reject{|name| self.date_filter_names.include? name}
174
+ end
175
+
176
+ def available_filter_options
177
+ self.available_filters.map{|id| [self.filter_options_for(id).label,id]}
178
+ end
179
+
180
+ def available_email_filters
181
+ self.resource_class::REPORT_DEFINITION_OPTIONS.reject{|name| (self.report_filters.pluck(:filter_name)).include? name}
182
+ end
183
+
184
+ def collection_for(filter_name)
185
+ self.resource_class::REPORT_FILTER_OPTIONS.detect{|filter| filter.param == filter_name}.collection
186
+ end
187
+
188
+ def custom_filters
189
+ self.resource_class::REPORT_FILTER_OPTIONS.select{|filter| self.report_filters.pluck(:filter_name).exclude? filter.param}
190
+ end
191
+
192
+ def available_columns
193
+ self.resource_class::REPORT_COLUMN_OPTIONS.keys.reject{|column| self.report_columns.pluck(:column_name).include? column.to_s}
194
+ end
195
+
196
+ def sortable?(column)
197
+ !!(self.column_options_for(column.column_name).respond_to? :order_sql)
198
+ end
199
+
200
+ def sharing_scope_pretty
201
+ (self.sharing_scope.try :name ) || 'Everyone'
202
+ end
203
+
204
+ def resource_type_pretty
205
+ self.resource_class.prettify_name.pluralize
206
+ end
207
+
208
+ def get_filter_class(params)
209
+ filter_option = self.resource_class::REPORT_FILTER_OPTIONS.detect{|filter| filter.param == params[:filter_name]}
210
+ "ClarkKent::Report#{filter_option.kind.camelcase}".constantize
211
+ end
212
+
213
+ def summary_row_values(rows)
214
+ self.report_columns.each_with_index.map do |report_column,index|
215
+ report_column.calculate_summary(rows.map{|row| row[index]})
216
+ end
217
+ end
218
+
219
+ def summary_row(rows)
220
+ row_array = self.report_columns.map do |report_column|
221
+ [report_column.column_name,report_column.calculate_summary(rows.map{|row| row.send(report_column.column_name)})]
222
+ end
223
+ Map.new(row_array.to_h)
224
+ end
225
+
226
+ def summary_row?
227
+ @summary_row_presence ||= self.report_columns.to_a.any?{|c| c.summary_method.present? }
228
+ end
229
+
230
+ def headers
231
+ the_headers = self.report_columns.sorted.pluck(:column_name)
232
+
233
+ unless name =~ /net\s?promoter/i
234
+ the_headers.map(&:humanize)
235
+ else
236
+ the_headers
237
+ end
238
+ end
239
+
240
+ def deep_clone
241
+ Report.transaction do
242
+ new_report = dup.reset_timestamps
243
+ new_report.name << " CLONED: #{Date.today.to_s(:db)}"
244
+ new_report.save!
245
+
246
+ report_filters.each do |report_filter|
247
+ new_report.report_filters << report_filter.dup.reset_timestamps
248
+ end
249
+
250
+ report_columns.each do |report_column|
251
+ new_report.report_columns << report_column.dup.reset_timestamps
252
+ end
253
+
254
+ report_emails.each do |report_email|
255
+ new_report_email = report_email.dup.reset_timestamps
256
+ new_report.report_emails << new_report_email
257
+
258
+ report_email.report_filters.each do |report_filter|
259
+ new_report_email.report_filters << report_filter.dup.reset_timestamps
260
+ end
261
+
262
+ report_email.user_report_emails.each do |user_report_email|
263
+ new_report_email.user_report_emails << user_report_email.dup.reset_timestamps
264
+ end
265
+ end
266
+
267
+ new_report
268
+ end
269
+ end
270
+
271
+ end
272
+ end
@@ -0,0 +1,24 @@
1
+ module ClarkKent
2
+ class ReportColumn < ActiveRecord::Base
3
+ include Cloneable
4
+
5
+ SummaryMethods = ['total','average']
6
+ attr_accessible :report_id, :column_name, :column_order, :report_sort, :summary_method
7
+ belongs_to :report
8
+
9
+ scope :sorted, -> { order("clark_kent_report_columns.column_order") }
10
+
11
+ def report_sort_pretty
12
+ {'ascending' => 'A->Z','descending' => 'Z->A'}[self.report_sort]
13
+ end
14
+
15
+ def calculate_summary(values)
16
+ return nil unless self.summary_method.present?
17
+ values.send self.summary_method
18
+ end
19
+
20
+ def summarizable?
21
+ report.column_options_for(self.column_name).respond_to? :summarizable
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,81 @@
1
+ module ClarkKent
2
+ class ReportDateFilter < ReportFilter
3
+ WeekDayOptions = ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']
4
+ MonthDayOptions = ['beginning of month']
5
+ DayOptions = WeekDayOptions + MonthDayOptions
6
+ WeekPeriodOptions = {
7
+ 'previous week' => 'last_week',
8
+ 'same week' => 'this_week',
9
+ 'following week' => 'next_week'
10
+ }
11
+ MonthPeriodOptions = {
12
+ 'previous month' => 'last_month',
13
+ 'same month' => 'this_month',
14
+ 'following month' => 'next_month'
15
+ }
16
+
17
+ PeriodOptions = WeekPeriodOptions.merge MonthPeriodOptions
18
+
19
+ Durations = ['day','week','two weeks','month']
20
+
21
+ before_save :handle_filter_value
22
+
23
+ attr_accessible :kind_of_day, :offset, :duration
24
+
25
+ def filter_match_params
26
+ [[self.begin_param_name,self.begin_date],[self.end_param_name,self.end_date]]
27
+ end
28
+
29
+ def begin_param_name
30
+ "#{self.filter_name}_from"
31
+ end
32
+
33
+ def end_param_name
34
+ "#{self.filter_name}_until"
35
+ end
36
+
37
+ def begin_date
38
+ @begin_date = Date.ih_today
39
+ direction, period = self.period_offset
40
+ @begin_date = @begin_date.send(direction, 1.send(period)) if direction
41
+ @begin_date = @begin_date.find_day(self.day_offset) if self.day_offset
42
+ @begin_date
43
+ end
44
+
45
+ def day_offset
46
+ return false if 'today' == self.kind_of_day
47
+ self.kind_of_day.gsub(/ /,'_')
48
+ end
49
+
50
+ def period_offset
51
+ direction, period = self.offset.split('_')
52
+ direction = {'last' => '-', 'next' => '+', 'this' => false, '' => false}[direction.to_s]
53
+ return [direction, period]
54
+ end
55
+
56
+ def end_date
57
+ return self.begin_date if 'day' == self.duration
58
+ return self.begin_date + 6.days if 'week' == self.duration
59
+ return self.begin_date + 13.days if 'two weeks' == self.duration
60
+ if Date::DAYNAMES.include? self.kind_of_day.capitalize
61
+ return self.begin_date + 1.month
62
+ else
63
+ return self.begin_date.end_of_month
64
+ end
65
+ end
66
+
67
+ def handle_filter_value
68
+ if self.filter_value_1.present? and self.filter_value_2.present?
69
+ self.filter_value = [filter_value_1, self.filter_value_2].join(' ')
70
+ end
71
+ end
72
+
73
+ def offset_pretty
74
+ self.class::PeriodOptions.rassoc(self.offset).first
75
+ end
76
+
77
+ def date_display
78
+ "starts on #{self.kind_of_day} #{self.offset_pretty} for 1 #{self.duration}"
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,105 @@
1
+ module ClarkKent
2
+ class ReportEmail < ActiveRecord::Base
3
+ include Cloneable
4
+
5
+ SEND_TIMES = {
6
+ 'Mondays' => 'Monday',
7
+ 'Tuesdays' => 'Tuesday',
8
+ 'Wednesdays' => 'Wednesday',
9
+ 'Thursdays' => 'Thursday',
10
+ 'Fridays' => 'Friday',
11
+ 'Saturdays' => 'Saturday',
12
+ 'Sundays' => 'Sunday',
13
+ '1st of the month' => 'beginning_of_month',
14
+ 'End of the month' => 'end_of_month'
15
+ }
16
+ belongs_to :report
17
+ has_many :report_filters, as: :filterable, dependent: :destroy
18
+ has_many :report_date_filters, as: :filterable, dependent: :destroy
19
+ has_many :user_report_emails, dependent: :destroy
20
+ has_many :users, through: :user_report_emails
21
+
22
+ attr_accessible :report_id, :when_to_send, :name
23
+
24
+ def self.send_emails_for_today
25
+ today = Date.ih_today
26
+ todays_filters = []
27
+ ['beginning_of_month','end_of_month'].each do |month_bookend|
28
+ todays_filters.push month_bookend if today.send(month_bookend) == today
29
+ end
30
+ todays_filters.push Date::DAYNAMES[today.wday]
31
+ self.where(when_to_send: todays_filters).each do |report_email|
32
+ report_email.send_emails
33
+ end
34
+ end
35
+
36
+ def send_emails
37
+ self.user_report_emails.each do |user_report_email|
38
+ ConeyIsland.submit(ClarkKent::ReportEmail, :send_email, instance_id: self.id, args: [user_report_email.user_id], timeout: 300, work_queue: 'boardwalk')
39
+ end
40
+ end
41
+
42
+ def send_email(user_id)
43
+ user = ClarkKent.user_class.find(user_id)
44
+ params = {'report_result_name' => "report-#{self.id}-user-#{user_id}-#{Time.now.to_formatted_s(:number)}", 'report_class' => 'ClarkKent::ReportEmail'}
45
+ SharingScopeKind.custom.each do |sharing_scope_kind|
46
+ unless report.report_filters.map(&:filter_name).include? sharing_scope_kind.basic_association_id_collection_name.to_s
47
+ associations = sharing_scope_kind.associated_containers_for(user)
48
+ params[sharing_scope_kind.basic_association_id_collection_name] = associations.map(&:id)
49
+ end
50
+ end
51
+ report_download_url = ClarkKent::Report.send_report_to_s3(self.id, params)
52
+ ClarkKent::ReportMailer.report_run(self.report_id, user_id, report_download_url).deliver
53
+ end
54
+
55
+ def report_filter_params
56
+ Hash[*self.report_filters.map{|filter| filter.filter_match_params}.flatten].
57
+ merge(order: self.report.sorter).merge(self.report.report_filter_params)
58
+ end
59
+
60
+ def filter_kind(filter_name)
61
+ self.report.filter_kind(filter_name)
62
+ end
63
+
64
+ def resource_class
65
+ self.report.resource_class
66
+ end
67
+
68
+ def filter_options_for(filter_name)
69
+ self.report.filter_options_for(filter_name)
70
+ end
71
+
72
+ def collection_for(filter_name)
73
+ self.report.collection_for(filter_name)
74
+ end
75
+
76
+ def get_filter_class(params)
77
+ self.report.get_filter_class(params)
78
+ end
79
+
80
+ def available_email_filters
81
+ self.resource_class::REPORT_DEFINITION_OPTIONS.reject{|name, label| (self.report_filters.pluck(:filter_name) + self.report.report_filters.pluck(:filter_name)).include? name}
82
+ end
83
+
84
+ def available_filters
85
+ self.available_email_filters
86
+ end
87
+
88
+ def available_filter_options
89
+ self.available_filters.map{|id| [self.filter_options_for(id).label,id]}
90
+ end
91
+
92
+ def get_query(params, count = false)
93
+ self.report.resource_class.report(params,self, count)
94
+ end
95
+
96
+ def period_pretty
97
+ self.report_date_filters.map{|filter| [filter.filter_name,filter.date_display].join(' ')}.join('<br>').html_safe
98
+ end
99
+
100
+ def emails
101
+ self.users.map(&:email)
102
+ end
103
+
104
+ end
105
+ end
@@ -0,0 +1,26 @@
1
+ module ClarkKent
2
+ class ReportFilter < ActiveRecord::Base
3
+ include Cloneable
4
+
5
+ attr_accessor :filter_value_1, :filter_value_2
6
+ attr_accessible :filter_value_1, :filter_value_2, :report_id, :filter_name, :filter_value, :filterable_id, :filterable_type, :type
7
+ belongs_to :filterable, polymorphic: true
8
+
9
+ def filter_match_params
10
+ [self.filter_match_param,self.filter_match_value]
11
+ end
12
+
13
+ def filter_match_param
14
+ self.filter_name
15
+ end
16
+
17
+ def filter_match_value
18
+ self.filter_value
19
+ end
20
+
21
+ def display_name
22
+ self.filter_name
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,18 @@
1
+ module ClarkKent
2
+ class ReportFilterOption
3
+ include Cloneable
4
+
5
+ attr_accessor :param, :label, :collection, :kind, :select
6
+ def initialize(params)
7
+ self.param = params[:param] if params[:param].present?
8
+ self.label = params[:label] if params[:label].present?
9
+ self.collection = params[:collection] if params[:collection].present?
10
+ self.kind = params[:kind] if params[:kind].present?
11
+ self.select = params[:select] if params[:select].present?
12
+ end
13
+
14
+ def label
15
+ @label || @param
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ module ClarkKent
2
+ class ReportNumberFilter < ReportFilter
3
+ include Cloneable
4
+
5
+ attr_accessible :max_value, :min_value
6
+
7
+ def filter_match_params
8
+ [[self.min_param_name,self.min_value],[self.max_param_name,self.max_value]]
9
+ end
10
+
11
+ def min_param_name
12
+ "#{self.filter_name}_min"
13
+ end
14
+
15
+ def max_param_name
16
+ "#{self.filter_name}_max"
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ module ClarkKent
2
+ class ReportObjectFilter < ReportFilter
3
+ include Cloneable
4
+
5
+ def get_display_value
6
+ if self.filter_value.to_i > 0
7
+ self.filter_class.find(self.filter_value).name
8
+ else
9
+ self.filter_value
10
+ end
11
+ end
12
+
13
+ def filter_class
14
+ if self.filter_name =~ /_id/
15
+ self.filter_name.split('_')[0..-2].join('_').camelcase.constantize
16
+ end
17
+ end
18
+
19
+ def display_name
20
+ if self.filter_class.present?
21
+ self.filter_class.name.underscore.humanize
22
+ else
23
+ self.filter_name
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,55 @@
1
+ module ClarkKent
2
+ class ReportResult
3
+ include Enumerable
4
+
5
+ def initialize(arel_query, params)
6
+ @arel_query = arel_query
7
+ @params = params
8
+ end
9
+
10
+ def paginated_query
11
+ if @params.has_key? :page and @params.has_key? :per
12
+ page = @params[:page] || 1
13
+ @arel_query.offset((page.to_i - 1) * @params[:per]).limit(@params[:per])
14
+ else
15
+ @arel_query
16
+ end
17
+ end
18
+
19
+ def current_page
20
+ @params[:page]
21
+ end
22
+
23
+ def per_page
24
+ @params[:per]
25
+ end
26
+
27
+ def query
28
+ @arel_query
29
+ end
30
+
31
+ def total_count
32
+ unless defined?(@total_count)
33
+ results = results_for(@arel_query.to_sql)
34
+ @total_count = results.num_tuples
35
+ end
36
+
37
+ @total_count
38
+ end
39
+
40
+ def rows
41
+ results_for(paginated_query.to_sql)
42
+ end
43
+
44
+ def each(&block)
45
+ rows.each(&block)
46
+ end
47
+
48
+ private
49
+
50
+ def results_for(sql)
51
+ Report.connection.raw_connection.exec(sql)
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,5 @@
1
+ module ClarkKent
2
+ class ReportStringFilter < ReportFilter
3
+ include Cloneable
4
+ end
5
+ end